diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json index efcde345630494fa3472842133e233bcec0d1d2b..4666b5004436ff2f27fbf7b9f288e357e3ce1602 100644 --- a/packages/create-llama/package.json +++ b/packages/create-llama/package.json @@ -48,6 +48,7 @@ "picocolors": "1.0.0", "prompts": "2.1.0", "rimraf": "^5.0.5", + "smol-toml": "^1.1.3", "tar": "6.1.15", "terminal-link": "^3.0.0", "update-check": "1.5.4", diff --git a/packages/create-llama/questions.ts b/packages/create-llama/questions.ts index d37096011ff94eb97615f4e5d761ce537c836aac..72c1e07583ec61ead0a4aefdea385858a4d0dea8 100644 --- a/packages/create-llama/questions.ts +++ b/packages/create-llama/questions.ts @@ -239,28 +239,24 @@ export const askQuestions = async ( if (ciInfo.isCI) { program.vectorDb = getPrefOrDefault("vectorDb"); } else { - if (program.framework === "fastapi") { - program.vectorDb = "none"; - } else { - const { vectorDb } = await prompts( - { - type: "select", - name: "vectorDb", - message: "Would you like to use a vector database?", - choices: [ - { - title: "No, just store the data in the file system", - value: "none", - }, - { title: "MongoDB", value: "mongo" }, - ], - initial: 0, - }, - handlers, - ); - program.vectorDb = vectorDb; - preferences.vectorDb = vectorDb; - } + const { vectorDb } = await prompts( + { + type: "select", + name: "vectorDb", + message: "Would you like to use a vector database?", + choices: [ + { + title: "No, just store the data in the file system", + value: "none", + }, + { title: "MongoDB", value: "mongo" }, + ], + initial: 0, + }, + handlers, + ); + program.vectorDb = vectorDb; + preferences.vectorDb = vectorDb; } } diff --git a/packages/create-llama/templates/components/vectordbs/python/mongo/__init__.py b/packages/create-llama/templates/components/vectordbs/python/mongo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/create-llama/templates/components/vectordbs/python/mongo/constants.py b/packages/create-llama/templates/components/vectordbs/python/mongo/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..547ff9a8b9a7df6a752e07084f22d9bd771ce20c --- /dev/null +++ b/packages/create-llama/templates/components/vectordbs/python/mongo/constants.py @@ -0,0 +1,3 @@ +DATA_DIR = "data" # directory containing the documents to index +CHUNK_SIZE = 1024 +CHUNK_OVERLAP = 20 diff --git a/packages/create-llama/templates/components/vectordbs/python/mongo/context.py b/packages/create-llama/templates/components/vectordbs/python/mongo/context.py new file mode 100644 index 0000000000000000000000000000000000000000..ceb8a50ae0cd02425aeb5fa4436df374590a4fad --- /dev/null +++ b/packages/create-llama/templates/components/vectordbs/python/mongo/context.py @@ -0,0 +1,14 @@ +from llama_index import ServiceContext + +from app.context import create_base_context +from app.engine.constants import CHUNK_SIZE, CHUNK_OVERLAP + + +def create_service_context(): + base = create_base_context() + return ServiceContext.from_defaults( + llm=base.llm, + embed_model=base.embed_model, + chunk_size=CHUNK_SIZE, + chunk_overlap=CHUNK_OVERLAP, + ) diff --git a/packages/create-llama/templates/components/vectordbs/python/mongo/generate.py b/packages/create-llama/templates/components/vectordbs/python/mongo/generate.py new file mode 100644 index 0000000000000000000000000000000000000000..fe0ee9aa016d5adab9d95be06f2e0708f8aa903d --- /dev/null +++ b/packages/create-llama/templates/components/vectordbs/python/mongo/generate.py @@ -0,0 +1,48 @@ +from dotenv import load_dotenv + +load_dotenv() +import os +import logging +from llama_index.vector_stores import MongoDBAtlasVectorSearch + +from app.engine.constants import DATA_DIR +from app.engine.context import create_service_context + + +from llama_index import ( + SimpleDirectoryReader, + VectorStoreIndex, + StorageContext, +) + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger() + + +def generate_datasource(service_context): + logger.info("Creating new index") + # load the documents and create the index + documents = SimpleDirectoryReader(DATA_DIR).load_data() + store = MongoDBAtlasVectorSearch( + db_name=os.environ["MONGODB_DATABASE"], + collection_name=os.environ["MONGODB_VECTORS"], + index_name=os.environ["MONGODB_VECTOR_INDEX"], + ) + storage_context = StorageContext.from_defaults(vector_store=store) + VectorStoreIndex.from_documents( + documents, + service_context=service_context, + storage_context=storage_context, + show_progress=True, # this will show you a progress bar as the embeddings are created + ) + logger.info( + f"Successfully created embeddings in the MongoDB collection {os.environ['MONGODB_VECTORS']}" + ) + logger.info( + """IMPORTANT: You can't query your index yet because you need to create a vector search index in MongoDB's UI now. +See https://github.com/run-llama/mongodb-demo/tree/main?tab=readme-ov-file#create-a-vector-search-index""" + ) + + +if __name__ == "__main__": + generate_datasource(create_service_context()) diff --git a/packages/create-llama/templates/components/vectordbs/python/mongo/index.py b/packages/create-llama/templates/components/vectordbs/python/mongo/index.py new file mode 100644 index 0000000000000000000000000000000000000000..d9b14dd22e4e0f1273612a8d986150e865a2bf5c --- /dev/null +++ b/packages/create-llama/templates/components/vectordbs/python/mongo/index.py @@ -0,0 +1,23 @@ +import logging +import os + +from llama_index import ( + VectorStoreIndex, +) +from llama_index.vector_stores import MongoDBAtlasVectorSearch + +from app.engine.context import create_service_context + + +def get_chat_engine(): + service_context = create_service_context() + logger = logging.getLogger("uvicorn") + logger.info("Connecting to index from MongoDB...") + store = MongoDBAtlasVectorSearch( + db_name=os.environ["MONGODB_DATABASE"], + collection_name=os.environ["MONGODB_VECTORS"], + index_name=os.environ["MONGODB_VECTOR_INDEX"], + ) + index = VectorStoreIndex.from_vector_store(store, service_context) + logger.info("Finished connecting to index from MongoDB.") + return index.as_chat_engine(similarity_top_k=5) diff --git a/packages/create-llama/templates/components/vectordbs/typescript/mongo/generate.mjs b/packages/create-llama/templates/components/vectordbs/typescript/mongo/generate.mjs index e7751e2eda68450f196b4843de8975ca32cfe671..7337d1222b997a044734396109d1da4ad3019699 100644 --- a/packages/create-llama/templates/components/vectordbs/typescript/mongo/generate.mjs +++ b/packages/create-llama/templates/components/vectordbs/typescript/mongo/generate.mjs @@ -11,7 +11,7 @@ import { STORAGE_DIR, checkRequiredEnvVars } from "./shared.mjs"; dotenv.config(); -const mongoUri = process.env.MONGODB_URI; +const mongoUri = process.env.MONGO_URI; const databaseName = process.env.MONGODB_DATABASE; const vectorCollectionName = process.env.MONGODB_VECTORS; const indexName = process.env.MONGODB_VECTOR_INDEX; diff --git a/packages/create-llama/templates/components/vectordbs/typescript/mongo/index.ts b/packages/create-llama/templates/components/vectordbs/typescript/mongo/index.ts index 68482f87db280defc46b506a576e5337ba19c2f7..7aceaff06f8b1df4bf67d46ad589218e4e8c6fea 100644 --- a/packages/create-llama/templates/components/vectordbs/typescript/mongo/index.ts +++ b/packages/create-llama/templates/components/vectordbs/typescript/mongo/index.ts @@ -11,7 +11,7 @@ import { checkRequiredEnvVars, CHUNK_OVERLAP, CHUNK_SIZE } from "./shared.mjs"; async function getDataSource(llm: LLM) { checkRequiredEnvVars(); - const client = new MongoClient(process.env.MONGODB_URI!); + const client = new MongoClient(process.env.MONGO_URI!); const serviceContext = serviceContextFromDefaults({ llm, chunkSize: CHUNK_SIZE, diff --git a/packages/create-llama/templates/components/vectordbs/typescript/mongo/shared.mjs b/packages/create-llama/templates/components/vectordbs/typescript/mongo/shared.mjs index 5d45eba6288cc5ab6cf85b05749d9ee75dd87655..264a82f0626cbb2c603afa3a054375c092557e7c 100644 --- a/packages/create-llama/templates/components/vectordbs/typescript/mongo/shared.mjs +++ b/packages/create-llama/templates/components/vectordbs/typescript/mongo/shared.mjs @@ -3,7 +3,7 @@ export const CHUNK_SIZE = 512; export const CHUNK_OVERLAP = 20; const REQUIRED_ENV_VARS = [ - "MONGODB_URI", + "MONGO_URI", "MONGODB_DATABASE", "MONGODB_VECTORS", "MONGODB_VECTOR_INDEX", diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts index 4924e1ccc20e6e08cb290f843985c48192305c70..973a65b85961988b082fa8dcaa5c789967f9b28f 100644 --- a/packages/create-llama/templates/index.ts +++ b/packages/create-llama/templates/index.ts @@ -2,20 +2,20 @@ import { copy } from "../helpers/copy"; import { callPackageManager } from "../helpers/install"; import fs from "fs/promises"; -import os from "os"; import path from "path"; -import { bold, cyan } from "picocolors"; -import { version } from "../../core/package.json"; +import { cyan } from "picocolors"; import { COMMUNITY_OWNER, COMMUNITY_REPO } from "../helpers/constant"; import { PackageManager } from "../helpers/get-pkg-manager"; import { downloadAndExtractRepo } from "../helpers/repo"; +import { installPythonTemplate } from "./python"; import { InstallTemplateArgs, TemplateEngine, TemplateFramework, TemplateVectorDB, } from "./types"; +import { installTSTemplate } from "./typescript"; const createEnvLocalFile = async ( root: string, @@ -42,7 +42,8 @@ const createEnvLocalFile = async ( switch (opts?.vectorDb) { case "mongo": { - content += `MONGODB_URI=\n`; + content += `# For generating a connection URI, see https://www.mongodb.com/docs/guides/atlas/connection-string\n`; + content += `MONGO_URI=\n`; content += `MONGODB_DATABASE=\n`; content += `MONGODB_VECTORS=\n`; content += `MONGODB_VECTOR_INDEX=\n`; @@ -101,259 +102,6 @@ const copyTestData = async ( } }; -const rename = (name: string) => { - switch (name) { - case "gitignore": - case "eslintrc.json": { - return `.${name}`; - } - // README.md is ignored by webpack-asset-relocator-loader used by ncc: - // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 - case "README-template.md": { - return "README.md"; - } - default: { - return name; - } - } -}; - -/** - * Install a LlamaIndex internal template to a given `root` directory. - */ -const installTSTemplate = async ({ - appName, - root, - packageManager, - isOnline, - template, - framework, - engine, - ui, - eslint, - customApiPath, - forBackend, - model, - vectorDb, -}: InstallTemplateArgs) => { - console.log(bold(`Using ${packageManager}.`)); - - /** - * Copy the template files to the target directory. - */ - console.log("\nInitializing project with template:", template, "\n"); - const templatePath = path.join(__dirname, "types", template, framework); - const copySource = ["**"]; - if (!eslint) copySource.push("!eslintrc.json"); - - await copy(copySource, root, { - parents: true, - cwd: templatePath, - rename, - }); - - /** - * If the backend is next.js, rename next.config.app.js to next.config.js - * If not, rename next.config.static.js to next.config.js - */ - if (framework == "nextjs" && forBackend === "nextjs") { - const nextConfigAppPath = path.join(root, "next.config.app.js"); - const nextConfigPath = path.join(root, "next.config.js"); - await fs.rename(nextConfigAppPath, nextConfigPath); - // delete next.config.static.js - const nextConfigStaticPath = path.join(root, "next.config.static.js"); - await fs.rm(nextConfigStaticPath); - } else if (framework == "nextjs" && typeof forBackend === "undefined") { - const nextConfigStaticPath = path.join(root, "next.config.static.js"); - const nextConfigPath = path.join(root, "next.config.js"); - await fs.rename(nextConfigStaticPath, nextConfigPath); - // delete next.config.app.js - const nextConfigAppPath = path.join(root, "next.config.app.js"); - await fs.rm(nextConfigAppPath); - } - - /** - * Copy the selected chat engine files to the target directory and reference it. - */ - let relativeEngineDestPath; - const compPath = path.join(__dirname, "components"); - if (engine && (framework === "express" || framework === "nextjs")) { - console.log("\nUsing chat engine:", engine, "\n"); - - let vectorDBFolder: string = engine; - - if (engine !== "simple" && vectorDb) { - console.log("\nUsing vector DB:", vectorDb, "\n"); - vectorDBFolder = vectorDb; - } - - const VectorDBPath = path.join( - compPath, - "vectordbs", - "typescript", - vectorDBFolder, - ); - relativeEngineDestPath = - framework === "nextjs" - ? path.join("app", "api", "chat") - : path.join("src", "controllers"); - await copy("**", path.join(root, relativeEngineDestPath, "engine"), { - parents: true, - cwd: VectorDBPath, - }); - } - - /** - * Copy the selected UI files to the target directory and reference it. - */ - if (framework === "nextjs" && ui !== "shadcn") { - console.log("\nUsing UI:", ui, "\n"); - const uiPath = path.join(compPath, "ui", ui); - const destUiPath = path.join(root, "app", "components", "ui"); - // remove the default ui folder - await fs.rm(destUiPath, { recursive: true }); - // copy the selected ui folder - await copy("**", destUiPath, { - parents: true, - cwd: uiPath, - rename, - }); - } - - /** - * Update the package.json scripts. - */ - const packageJsonFile = path.join(root, "package.json"); - const packageJson: any = JSON.parse( - await fs.readFile(packageJsonFile, "utf8"), - ); - packageJson.name = appName; - packageJson.version = "0.1.0"; - - packageJson.dependencies = { - ...packageJson.dependencies, - llamaindex: version, - }; - - if (framework === "nextjs" && customApiPath) { - console.log( - "\nUsing external API with custom API path:", - customApiPath, - "\n", - ); - // remove the default api folder - const apiPath = path.join(root, "app", "api"); - await fs.rm(apiPath, { recursive: true }); - // modify the dev script to use the custom api path - packageJson.scripts = { - ...packageJson.scripts, - dev: `cross-env NEXT_PUBLIC_CHAT_API=${customApiPath} next dev`, - }; - } - - if (engine === "context" && relativeEngineDestPath) { - // add generate script if using context engine - packageJson.scripts = { - ...packageJson.scripts, - generate: `node ${path.join( - relativeEngineDestPath, - "engine", - "generate.mjs", - )}`, - }; - } - - if (framework === "nextjs" && ui === "html") { - // remove shadcn dependencies if html ui is selected - packageJson.dependencies = { - ...packageJson.dependencies, - "tailwind-merge": undefined, - "@radix-ui/react-slot": undefined, - "class-variance-authority": undefined, - clsx: undefined, - "lucide-react": undefined, - remark: undefined, - "remark-code-import": undefined, - "remark-gfm": undefined, - "remark-math": undefined, - "react-markdown": undefined, - "react-syntax-highlighter": undefined, - }; - - packageJson.devDependencies = { - ...packageJson.devDependencies, - "@types/react-syntax-highlighter": undefined, - }; - } - - if (!eslint) { - // Remove packages starting with "eslint" from devDependencies - packageJson.devDependencies = Object.fromEntries( - Object.entries(packageJson.devDependencies).filter( - ([key]) => !key.startsWith("eslint"), - ), - ); - } - await fs.writeFile( - packageJsonFile, - JSON.stringify(packageJson, null, 2) + os.EOL, - ); - - console.log("\nInstalling dependencies:"); - for (const dependency in packageJson.dependencies) - console.log(`- ${cyan(dependency)}`); - - console.log("\nInstalling devDependencies:"); - for (const dependency in packageJson.devDependencies) - console.log(`- ${cyan(dependency)}`); - - console.log(); - - await callPackageManager(packageManager, isOnline); -}; - -const installPythonTemplate = async ({ - root, - template, - framework, - engine, -}: Pick<InstallTemplateArgs, "root" | "framework" | "template" | "engine">) => { - console.log("\nInitializing Python project with template:", template, "\n"); - const templatePath = path.join(__dirname, "types", template, framework); - await copy("**", root, { - parents: true, - cwd: templatePath, - rename(name) { - switch (name) { - case "gitignore": { - return `.${name}`; - } - // README.md is ignored by webpack-asset-relocator-loader used by ncc: - // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 - case "README-template.md": { - return "README.md"; - } - default: { - return name; - } - } - }, - }); - - if (engine === "context") { - const compPath = path.join(__dirname, "components"); - const VectorDBPath = path.join(compPath, "vectordbs", "python", "none"); - await copy("**", path.join(root, "app", "engine"), { - parents: true, - cwd: VectorDBPath, - }); - } - - console.log( - "\nPython project, dependencies won't be installed automatically.\n", - ); -}; - const installCommunityProject = async ({ root, communityProjectPath, diff --git a/packages/create-llama/templates/python.ts b/packages/create-llama/templates/python.ts new file mode 100644 index 0000000000000000000000000000000000000000..1848782bb1bc128d207df1e875d9a5e06529a209 --- /dev/null +++ b/packages/create-llama/templates/python.ts @@ -0,0 +1,118 @@ +import fs from "fs/promises"; +import path from "path"; +import { cyan } from "picocolors"; +import { parse, stringify } from "smol-toml"; +import { copy } from "../helpers/copy"; +import { InstallTemplateArgs, TemplateVectorDB } from "./types"; + +interface Dependency { + name: string; + version: string; +} + +const getAdditionalDependencies = (vectorDb?: TemplateVectorDB) => { + const dependencies: Dependency[] = []; + + switch (vectorDb) { + case "mongo": { + dependencies.push({ + name: "pymongo", + version: "^4.6.1", + }); + break; + } + } + + return dependencies; +}; + +const addDependencies = async ( + projectDir: string, + dependencies: Dependency[], +) => { + if (dependencies.length === 0) return; + + const FILENAME = "pyproject.toml"; + try { + // Parse toml file + const file = path.join(projectDir, FILENAME); + const fileContent = await fs.readFile(file, "utf8"); + const fileParsed = parse(fileContent); + + // Modify toml dependencies + const tool = fileParsed.tool as any; + const dependencies = tool.poetry.dependencies as any; + for (const dependency of dependencies) { + dependencies[dependency.name] = dependency.version; + } + + // Write toml file + const newFileContent = stringify(fileParsed); + await fs.writeFile(file, newFileContent); + + const dependenciesString = dependencies + .map((d: Dependency) => d.name) + .join(", "); + console.log(`\nAdded ${dependenciesString} to ${cyan(FILENAME)}\n`); + } catch (error) { + console.log( + `Error while updating dependencies for Poetry project file ${FILENAME}\n`, + error, + ); + console.log(error); + } +}; + +export const installPythonTemplate = async ({ + root, + template, + framework, + engine, + vectorDb, +}: Pick< + InstallTemplateArgs, + "root" | "framework" | "template" | "engine" | "vectorDb" +>) => { + console.log("\nInitializing Python project with template:", template, "\n"); + const templatePath = path.join(__dirname, "types", template, framework); + await copy("**", root, { + parents: true, + cwd: templatePath, + rename(name) { + switch (name) { + case "gitignore": { + return `.${name}`; + } + // README.md is ignored by webpack-asset-relocator-loader used by ncc: + // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 + case "README-template.md": { + return "README.md"; + } + default: { + return name; + } + } + }, + }); + + if (engine === "context") { + const compPath = path.join(__dirname, "components"); + const VectorDBPath = path.join( + compPath, + "vectordbs", + "python", + vectorDb || "none", + ); + await copy("**", path.join(root, "app", "engine"), { + parents: true, + cwd: VectorDBPath, + }); + } + + const addOnDependencies = getAdditionalDependencies(vectorDb); + await addDependencies(root, addOnDependencies); + + console.log( + "\nPython project, dependencies won't be installed automatically.\n", + ); +}; diff --git a/packages/create-llama/templates/typescript.ts b/packages/create-llama/templates/typescript.ts new file mode 100644 index 0000000000000000000000000000000000000000..df3c0b15b679405ebbb054303c5ab3732834e60b --- /dev/null +++ b/packages/create-llama/templates/typescript.ts @@ -0,0 +1,217 @@ +import fs from "fs/promises"; +import os from "os"; +import path from "path"; +import { bold, cyan } from "picocolors"; +import { version } from "../../core/package.json"; +import { copy } from "../helpers/copy"; +import { callPackageManager } from "../helpers/install"; +import { InstallTemplateArgs } from "./types"; + +const rename = (name: string) => { + switch (name) { + case "gitignore": + case "eslintrc.json": { + return `.${name}`; + } + // README.md is ignored by webpack-asset-relocator-loader used by ncc: + // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227 + case "README-template.md": { + return "README.md"; + } + default: { + return name; + } + } +}; +/** + * Install a LlamaIndex internal template to a given `root` directory. + */ +export const installTSTemplate = async ({ + appName, + root, + packageManager, + isOnline, + template, + framework, + engine, + ui, + eslint, + customApiPath, + forBackend, + vectorDb, +}: InstallTemplateArgs) => { + console.log(bold(`Using ${packageManager}.`)); + + /** + * Copy the template files to the target directory. + */ + console.log("\nInitializing project with template:", template, "\n"); + const templatePath = path.join(__dirname, "types", template, framework); + const copySource = ["**"]; + if (!eslint) copySource.push("!eslintrc.json"); + + await copy(copySource, root, { + parents: true, + cwd: templatePath, + rename, + }); + + /** + * If the backend is next.js, rename next.config.app.js to next.config.js + * If not, rename next.config.static.js to next.config.js + */ + if (framework == "nextjs" && forBackend === "nextjs") { + const nextConfigAppPath = path.join(root, "next.config.app.js"); + const nextConfigPath = path.join(root, "next.config.js"); + await fs.rename(nextConfigAppPath, nextConfigPath); + // delete next.config.static.js + const nextConfigStaticPath = path.join(root, "next.config.static.js"); + await fs.rm(nextConfigStaticPath); + } else if (framework == "nextjs" && typeof forBackend === "undefined") { + const nextConfigStaticPath = path.join(root, "next.config.static.js"); + const nextConfigPath = path.join(root, "next.config.js"); + await fs.rename(nextConfigStaticPath, nextConfigPath); + // delete next.config.app.js + const nextConfigAppPath = path.join(root, "next.config.app.js"); + await fs.rm(nextConfigAppPath); + } + + /** + * Copy the selected chat engine files to the target directory and reference it. + */ + let relativeEngineDestPath; + const compPath = path.join(__dirname, "components"); + if (engine && (framework === "express" || framework === "nextjs")) { + console.log("\nUsing chat engine:", engine, "\n"); + + let vectorDBFolder: string = engine; + + if (engine !== "simple" && vectorDb) { + console.log("\nUsing vector DB:", vectorDb, "\n"); + vectorDBFolder = vectorDb; + } + + const VectorDBPath = path.join( + compPath, + "vectordbs", + "typescript", + vectorDBFolder, + ); + relativeEngineDestPath = + framework === "nextjs" + ? path.join("app", "api", "chat") + : path.join("src", "controllers"); + await copy("**", path.join(root, relativeEngineDestPath, "engine"), { + parents: true, + cwd: VectorDBPath, + }); + } + + /** + * Copy the selected UI files to the target directory and reference it. + */ + if (framework === "nextjs" && ui !== "shadcn") { + console.log("\nUsing UI:", ui, "\n"); + const uiPath = path.join(compPath, "ui", ui); + const destUiPath = path.join(root, "app", "components", "ui"); + // remove the default ui folder + await fs.rm(destUiPath, { recursive: true }); + // copy the selected ui folder + await copy("**", destUiPath, { + parents: true, + cwd: uiPath, + rename, + }); + } + + /** + * Update the package.json scripts. + */ + const packageJsonFile = path.join(root, "package.json"); + const packageJson: any = JSON.parse( + await fs.readFile(packageJsonFile, "utf8"), + ); + packageJson.name = appName; + packageJson.version = "0.1.0"; + + packageJson.dependencies = { + ...packageJson.dependencies, + llamaindex: version, + }; + + if (framework === "nextjs" && customApiPath) { + console.log( + "\nUsing external API with custom API path:", + customApiPath, + "\n", + ); + // remove the default api folder + const apiPath = path.join(root, "app", "api"); + await fs.rm(apiPath, { recursive: true }); + // modify the dev script to use the custom api path + packageJson.scripts = { + ...packageJson.scripts, + dev: `cross-env NEXT_PUBLIC_CHAT_API=${customApiPath} next dev`, + }; + } + + if (engine === "context" && relativeEngineDestPath) { + // add generate script if using context engine + packageJson.scripts = { + ...packageJson.scripts, + generate: `node ${path.join( + relativeEngineDestPath, + "engine", + "generate.mjs", + )}`, + }; + } + + if (framework === "nextjs" && ui === "html") { + // remove shadcn dependencies if html ui is selected + packageJson.dependencies = { + ...packageJson.dependencies, + "tailwind-merge": undefined, + "@radix-ui/react-slot": undefined, + "class-variance-authority": undefined, + clsx: undefined, + "lucide-react": undefined, + remark: undefined, + "remark-code-import": undefined, + "remark-gfm": undefined, + "remark-math": undefined, + "react-markdown": undefined, + "react-syntax-highlighter": undefined, + }; + + packageJson.devDependencies = { + ...packageJson.devDependencies, + "@types/react-syntax-highlighter": undefined, + }; + } + + if (!eslint) { + // Remove packages starting with "eslint" from devDependencies + packageJson.devDependencies = Object.fromEntries( + Object.entries(packageJson.devDependencies).filter( + ([key]) => !key.startsWith("eslint"), + ), + ); + } + await fs.writeFile( + packageJsonFile, + JSON.stringify(packageJson, null, 2) + os.EOL, + ); + + console.log("\nInstalling dependencies:"); + for (const dependency in packageJson.dependencies) + console.log(`- ${cyan(dependency)}`); + + console.log("\nInstalling devDependencies:"); + for (const dependency in packageJson.devDependencies) + console.log(`- ${cyan(dependency)}`); + + console.log(); + + await callPackageManager(packageManager, isOnline); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5320ad0363bb9b9b70c07a3255980a5112e2d410..85008c0b1e05288fa2a717dd54a29d9b1d5b1e25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,6 +312,9 @@ importers: rimraf: specifier: ^5.0.5 version: 5.0.5 + smol-toml: + specifier: ^1.1.3 + version: 1.1.3 tar: specifier: 6.1.15 version: 6.1.15 @@ -14478,6 +14481,11 @@ packages: yargs: 15.4.1 dev: true + /smol-toml@1.1.3: + resolution: {integrity: sha512-qTyy6Owjho1ISBmxj4HdrFWB2kMQ5RczU6J04OqslSfdSH656OIHuomHS4ZDvhwm37nig/uXyiTMJxlC9zIVfw==} + engines: {node: '>= 18', pnpm: '>= 8'} + dev: true + /snake-case@2.1.0: resolution: {integrity: sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==} dependencies: