Skip to content
Snippets Groups Projects
Commit 07fd79ec authored by Marcus Schiesser's avatar Marcus Schiesser
Browse files

feat: generate fullstack app with fastapi or express

parent 42ed0045
No related branches found
No related tags found
No related merge requests found
......@@ -7,8 +7,10 @@ import { getOnline } from "./helpers/is-online";
import { isWriteable } from "./helpers/is-writeable";
import { makeDir } from "./helpers/make-dir";
import fs from "fs";
import terminalLink from "terminal-link";
import type { InstallTemplateArgs } from "./templates";
import { installPythonTemplate, installTemplate } from "./templates";
import { installTemplate } from "./templates";
export async function createApp({
template,
......@@ -18,9 +20,13 @@ export async function createApp({
appPath,
packageManager,
eslint,
customApiPath,
}: Omit<InstallTemplateArgs, "appName" | "root" | "isOnline"> & {
frontend,
}: Omit<
InstallTemplateArgs,
"appName" | "root" | "isOnline" | "customApiPath"
> & {
appPath: string;
frontend: boolean;
}): Promise<void> {
const root = path.resolve(appPath);
......@@ -47,30 +53,54 @@ export async function createApp({
console.log(`Creating a new LlamaIndex app in ${green(root)}.`);
console.log();
process.chdir(root);
const args = {
appName,
root,
template,
framework,
engine,
ui,
packageManager,
isOnline,
eslint,
};
if (framework === "fastapi") {
await installPythonTemplate({ appName, root, template, framework });
} else {
if (frontend) {
// install backend
const backendRoot = path.join(root, "backend");
await makeDir(backendRoot);
await installTemplate({ ...args, root: backendRoot });
// install frontend
const frontendRoot = path.join(root, "frontend");
await makeDir(frontendRoot);
await installTemplate({
appName,
root,
template,
framework,
engine,
ui,
packageManager,
isOnline,
eslint,
customApiPath,
...args,
root: frontendRoot,
framework: "nextjs",
customApiPath: "http://localhost:8000/api/chat",
});
// copy readme for fullstack
await fs.promises.copyFile(
path.join(__dirname, "templates", "README-fullstack.md"),
path.join(root, "README.md"),
);
} else {
await installTemplate(args);
}
process.chdir(root);
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}
console.log(`${green("Success!")} Created ${appName} at ${appPath}`);
console.log(
`Now have a look at the ${terminalLink(
"README.md",
"file://${appName}/README.md",
)} and learn how to get started.`,
);
console.log();
}
......@@ -11,7 +11,6 @@ import checkForUpdate from "update-check";
import { createApp } from "./create-app";
import { getPkgManager } from "./helpers/get-pkg-manager";
import { isFolderEmpty } from "./helpers/is-folder-empty";
import { isUrl } from "./helpers/is-url";
import { validateNpmName } from "./helpers/validate-pkg";
import packageJson from "./package.json";
......@@ -167,7 +166,7 @@ async function run(): Promise<void> {
engine: "simple",
ui: "html",
eslint: true,
customApiPath: "http://localhost:8000/api/chat",
frontend: false,
};
const getPrefOrDefault = (field: string) =>
preferences[field] ?? defaults[field];
......@@ -224,7 +223,36 @@ async function run(): Promise<void> {
}
}
if (program.framework === "nextjs") {
if (program.framework === "express" || program.framework === "fastapi") {
// if a backend-only framework is selected, ask whether we should create a frontend
if (!program.frontend) {
if (ciInfo.isCI) {
program.frontend = getPrefOrDefault("frontend");
} else {
const styledNextJS = blue("NextJS");
const styledBackend = green(
program.framework === "express"
? "Express "
: program.framework === "fastapi"
? "FastAPI (Python) "
: "",
);
const { frontend } = await prompts({
onState: onPromptState,
type: "toggle",
name: "frontend",
message: `Would you like to generate a ${styledNextJS} frontend for your ${styledBackend}backend?`,
initial: getPrefOrDefault("frontend"),
active: "Yes",
inactive: "No",
});
program.frontend = Boolean(frontend);
preferences.frontend = Boolean(frontend);
}
}
}
if (program.framework === "nextjs" || program.frontend) {
if (!program.ui) {
if (ciInfo.isCI) {
program.ui = getPrefOrDefault("ui");
......@@ -253,15 +281,6 @@ async function run(): Promise<void> {
if (ciInfo.isCI) {
program.engine = getPrefOrDefault("engine");
} else {
const external =
program.framework === "nextjs"
? [
{
title: "External chat engine (e.g. FastAPI)",
value: "external",
},
]
: [];
const { engine } = await prompts(
{
type: "select",
......@@ -270,7 +289,6 @@ async function run(): Promise<void> {
choices: [
{ title: "SimpleChatEngine", value: "simple" },
{ title: "ContextChatEngine", value: "context" },
...external,
],
initial: 0,
},
......@@ -280,29 +298,6 @@ async function run(): Promise<void> {
preferences.engine = engine;
}
}
if (
program.framework === "nextjs" &&
program.engine === "external" &&
!program.customApiPath
) {
if (ciInfo.isCI) {
program.customApiPath = getPrefOrDefault("customApiPath");
} else {
const { customApiPath } = await prompts(
{
type: "text",
name: "customApiPath",
message:
"URL path of your external chat engine (used for development)?",
validate: (url) => (isUrl(url) ? true : "Please enter a valid URL"),
initial: getPrefOrDefault("customApiPath"),
},
handlers,
);
program.customApiPath = customApiPath;
preferences.customApiPath = customApiPath;
}
}
}
if (
......@@ -336,7 +331,7 @@ async function run(): Promise<void> {
appPath: resolvedProjectPath,
packageManager,
eslint: program.eslint,
customApiPath: program.customApiPath,
frontend: program.frontend,
});
conf.set("preferences", preferences);
}
......
......@@ -38,6 +38,7 @@
"@types/validate-npm-package-name": "3.0.0",
"@vercel/ncc": "0.34.0",
"async-retry": "1.3.1",
"async-sema": "3.0.1",
"ci-info": "watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540",
"commander": "2.20.0",
"conf": "10.2.0",
......@@ -50,7 +51,7 @@
"tar": "6.1.15",
"update-check": "1.5.4",
"validate-npm-package-name": "3.0.0",
"async-sema": "3.0.1"
"terminal-link": "^3.0.0"
},
"engines": {
"node": ">=16.14.0"
......
This is a [LlamaIndex](https://www.llamaindex.ai/) project bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
## Getting Started
First, startup the backend as described in the [backend README](./backend/README.md).
Second, run the development server of the frontend as described in the [frontend README](./frontend/README.md).
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Learn More
To learn more about LlamaIndex, take a look at the following resources:
- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
......@@ -7,12 +7,12 @@ import path from "path";
import { bold, cyan } from "picocolors";
import { version } from "../package.json";
import { InstallPythonTemplateArgs, InstallTemplateArgs } from "./types";
import { InstallTemplateArgs } from "./types";
/**
* Install a LlamaIndex internal template to a given `root` directory.
*/
export const installTemplate = async ({
const installTSTemplate = async ({
appName,
root,
packageManager,
......@@ -60,7 +60,7 @@ export const installTemplate = async ({
*/
let relativeEngineDestPath;
const compPath = path.join(__dirname, "components");
if (framework === "express" || framework === "nextjs") {
if (engine && (framework === "express" || framework === "nextjs")) {
console.log("\nUsing chat engine:", engine, "\n");
const enginePath = path.join(compPath, "engines", engine);
relativeEngineDestPath =
......@@ -101,7 +101,7 @@ export const installTemplate = async ({
llamaindex: version,
};
if (engine === "external" && customApiPath) {
if (framework === "nextjs" && customApiPath) {
console.log(
"\nUsing external API with custom API path:",
customApiPath,
......@@ -166,12 +166,11 @@ export const installTemplate = async ({
await install(packageManager, isOnline);
};
export const installPythonTemplate = async ({
appName,
const installPythonTemplate = async ({
root,
template,
framework,
}: InstallPythonTemplateArgs) => {
}: Pick<InstallTemplateArgs, "root" | "framework" | "template">) => {
console.log("\nInitializing Python project with template:", template, "\n");
const templatePath = path.join(__dirname, "types", template, framework);
await copy("**", root, {
......@@ -198,4 +197,13 @@ export const installPythonTemplate = async ({
);
};
export const installTemplate = async (props: InstallTemplateArgs) => {
process.chdir(props.root);
if (props.framework === "fastapi") {
await installPythonTemplate(props);
} else {
await installTSTemplate(props);
}
};
export * from "./types";
......@@ -2,16 +2,9 @@ import { PackageManager } from "../helpers/get-pkg-manager";
export type TemplateType = "simple" | "streaming";
export type TemplateFramework = "nextjs" | "express" | "fastapi";
export type TemplateEngine = "simple" | "context" | "external";
export type TemplateEngine = "simple" | "context";
export type TemplateUI = "html" | "shadcn";
export interface InstallPythonTemplateArgs {
appName: string;
root: string;
template: TemplateType;
framework: TemplateFramework;
}
export interface InstallTemplateArgs {
appName: string;
root: string;
......@@ -19,8 +12,8 @@ export interface InstallTemplateArgs {
isOnline: boolean;
template: TemplateType;
framework: TemplateFramework;
engine: TemplateEngine;
engine?: TemplateEngine;
ui: TemplateUI;
eslint: boolean;
customApiPath: string;
customApiPath?: string;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment