From 9f8677035c1d6d11f0cfe049059788b1232859c0 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 11:30:40 +0700 Subject: [PATCH 01/63] add script to copy server folder --- .../llamaindexserver/nextjs/package.json | 3 +- packages/server/bin/eject.cjs | 48 +++++++++++++++++++ packages/server/package.json | 6 ++- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 packages/server/bin/eject.cjs diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json index f68276d1c..c191b22b2 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json @@ -6,7 +6,8 @@ "generate:datasource": "tsx src/generate.ts datasource", "generate:ui": "tsx src/generate.ts ui", "dev": "nodemon", - "start": "tsx src/index.ts" + "start": "tsx src/index.ts", + "eject": "@llamaindex/server eject" }, "dependencies": { "@llamaindex/openai": "~0.4.0", diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs new file mode 100644 index 000000000..1a54c7f17 --- /dev/null +++ b/packages/server/bin/eject.cjs @@ -0,0 +1,48 @@ +#!/usr/bin/env node + +/* eslint-disable @typescript-eslint/no-require-imports */ + +const fs = require("fs").promises; +const path = require("path"); + +// Resolve the source directory (@llamaindex/server/server) +const sourceDir = path.resolve(__dirname, "../server"); + +// Resolve the destination directory (consumer's project root/next) +const destDir = path.join(process.cwd(), "next"); + +async function eject() { + try { + // Check if source directory exists + const sourceExists = await fs + .access(sourceDir) + .then(() => true) + .catch(() => false); + if (!sourceExists) { + console.error("Error: Source directory does not exist at", sourceDir); + process.exit(1); + } + + // Create next directory if it doesn't exist + await fs.mkdir(destDir, { recursive: true }); + + // Copy the server directory to the next folder + await fs.cp(sourceDir, destDir, { + recursive: true, + force: false, + errorOnExist: true, + }); + console.log("Successfully ejected @llamaindex/server/server to", destDir); + } catch (error) { + if (error.code === "EEXIST") { + console.error( + "Error: One or more files already exist in the destination directory. Please ensure the destination is clear or manually merge the files.", + ); + } else { + console.error("Error during eject:", error.message); + } + process.exit(1); + } +} + +eject(); diff --git a/packages/server/package.json b/packages/server/package.json index 88b9ea2e9..815dced30 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -19,8 +19,12 @@ }, "files": [ "dist", - "server" + "server", + "bin" ], + "bin": { + "@llamaindex/server": "./bin/eject.js" + }, "repository": { "type": "git", "url": "git+https://github.com/run-llama/LlamaIndexTS.git", From 9a352423b15b3ce7a783b3c5487d430b826acbb1 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 11:54:16 +0700 Subject: [PATCH 02/63] change command --- .../templates/types/llamaindexserver/nextjs/package.json | 2 +- packages/server/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json index c191b22b2..c0864dd54 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json @@ -7,7 +7,7 @@ "generate:ui": "tsx src/generate.ts ui", "dev": "nodemon", "start": "tsx src/index.ts", - "eject": "@llamaindex/server eject" + "eject": "llamaindex-server eject" }, "dependencies": { "@llamaindex/openai": "~0.4.0", diff --git a/packages/server/package.json b/packages/server/package.json index 815dced30..43cf4610a 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -23,7 +23,7 @@ "bin" ], "bin": { - "@llamaindex/server": "./bin/eject.js" + "llamaindex-server": "./bin/eject.cjs" }, "repository": { "type": "git", From 07578f8f24308df4c85137daadca06dbcd220f52 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 11:58:10 +0700 Subject: [PATCH 03/63] clean up before copy --- packages/server/bin/eject.cjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 1a54c7f17..cc59d8d29 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -23,7 +23,14 @@ async function eject() { process.exit(1); } - // Create next directory if it doesn't exist + // Remove next directory if it exists + try { + await fs.rm(destDir, { recursive: true, force: true }); + } catch (error) { + // Ignore error if directory doesn't exist + } + + // Create next directory await fs.mkdir(destDir, { recursive: true }); // Copy the server directory to the next folder From 3e8ec672dbeb2770f7cf3d6f1dbce52b1a243835 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 13:38:51 +0700 Subject: [PATCH 04/63] copy route handler folder --- packages/server/bin/eject.cjs | 16 ++++++++++++++++ packages/server/package.json | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index cc59d8d29..b80bf8d6c 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -8,6 +8,9 @@ const path = require("path"); // Resolve the source directory (@llamaindex/server/server) const sourceDir = path.resolve(__dirname, "../server"); +// Resolve the route-handler directory +const routeHandlerDir = path.resolve(__dirname, "../dist/api"); + // Resolve the destination directory (consumer's project root/next) const destDir = path.join(process.cwd(), "next"); @@ -39,6 +42,19 @@ async function eject() { force: false, errorOnExist: true, }); + + // Copy the route handler directory to replace the api directory + const destApiDir = path.join(destDir, "app", "api"); + try { + await fs.rm(destApiDir, { recursive: true, force: true }); + } catch (error) { + // Ignore error if directory doesn't exist + } + await fs.cp(routeHandlerDir, destApiDir, { + recursive: true, + force: true, + }); + console.log("Successfully ejected @llamaindex/server/server to", destDir); } catch (error) { if (error.code === "EEXIST") { diff --git a/packages/server/package.json b/packages/server/package.json index 43cf4610a..f83bcc195 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -35,12 +35,13 @@ "clean": "rm -rf ./dist ./server next/.next next/out ./temp", "prebuild": "pnpm clean", "build": "bunchee", - "postbuild": "pnpm prepare:ts-server && pnpm prepare:py-static", - "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api", + "postbuild": "pnpm prepare:ts-server", + "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:api", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", "build:css": "postcss server/app/globals.css -o server/app/globals.css && rm -rf ./server/postcss.config.js", "build:api": "rm -rf ./server/app/api && tsc --skipLibCheck --project tsconfig.api.json", + "copy:api": "cp -r ./next/app/api ./dist/api", "prepare:static": "cp -r ./next ./temp && rm -rf ./temp/app/api && mv ./temp/next-build.config.ts ./temp/next.config.ts", "build:static": "cd ./temp && next build", "copy:static": "cp -r ./temp/out ./dist/static && rm -rf ./temp" From 009858ebd12dbba733134940488fc3586a71ef37 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 13:39:35 +0700 Subject: [PATCH 05/63] fix lint --- packages/server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/package.json b/packages/server/package.json index f83bcc195..f437287ad 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -35,7 +35,7 @@ "clean": "rm -rf ./dist ./server next/.next next/out ./temp", "prebuild": "pnpm clean", "build": "bunchee", - "postbuild": "pnpm prepare:ts-server", + "postbuild": "pnpm prepare:ts-server && pnpm prepare:py-static", "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:api", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", From 254ab1cbf29e0ded8af8d80f9d7f69667aa248d3 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:01:02 +0700 Subject: [PATCH 06/63] add eject options --- packages/server/src/server.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 71f1c83da..56b955d2d 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -12,6 +12,7 @@ import type { LlamaIndexServerOptions } from "./types"; const nextDir = path.join(__dirname, "..", "server"); const configFile = path.join(__dirname, "..", "server", "public", "config.js"); const dev = process.env.NODE_ENV !== "production"; +const eject = process.env.EJECT === "true"; export class LlamaIndexServer { port: number; @@ -20,15 +21,19 @@ export class LlamaIndexServer { componentsDir?: string | undefined; layoutDir: string; suggestNextQuestions: boolean; + eject: boolean; constructor(options: LlamaIndexServerOptions) { const { workflow, suggestNextQuestions, ...nextAppOptions } = options; - this.app = next({ dev, dir: nextDir, ...nextAppOptions }); + const dir = eject ? "./next" : nextDir; + + this.app = next({ dev, dir, ...nextAppOptions }); this.port = nextAppOptions.port ?? parseInt(process.env.PORT || "3000", 10); this.workflowFactory = workflow; this.componentsDir = options.uiConfig?.componentsDir; this.layoutDir = options.uiConfig?.layoutDir ?? "layout"; this.suggestNextQuestions = suggestNextQuestions ?? true; + this.eject = eject; if (this.componentsDir) { this.createComponentsDir(this.componentsDir); @@ -78,7 +83,7 @@ export class LlamaIndexServer { const pathname = parsedUrl.pathname; const query = parsedUrl.query; - if (pathname === "/api/chat" && req.method === "POST") { + if (pathname === "/api/chat" && req.method === "POST" && !this.eject) { // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] // make sure to keep its semantic in sync with handleChat From ffe836a07e5e163ff292e329b8bc892808325cca Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:18:14 +0700 Subject: [PATCH 07/63] use EJECT from env --- packages/server/src/server.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 56b955d2d..535f535aa 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -12,7 +12,6 @@ import type { LlamaIndexServerOptions } from "./types"; const nextDir = path.join(__dirname, "..", "server"); const configFile = path.join(__dirname, "..", "server", "public", "config.js"); const dev = process.env.NODE_ENV !== "production"; -const eject = process.env.EJECT === "true"; export class LlamaIndexServer { port: number; @@ -25,15 +24,24 @@ export class LlamaIndexServer { constructor(options: LlamaIndexServerOptions) { const { workflow, suggestNextQuestions, ...nextAppOptions } = options; - const dir = eject ? "./next" : nextDir; - this.app = next({ dev, dir, ...nextAppOptions }); + this.eject = process.env.EJECT === "true"; + if (this.eject) { + console.log( + "Eject mode is enabled in ./next folder. Frontend and route handlers will be hot-reloaded when changes are made.", + ); + } + + this.app = next({ + dev, + dir: this.eject ? "./next" : nextDir, + ...nextAppOptions, + }); this.port = nextAppOptions.port ?? parseInt(process.env.PORT || "3000", 10); this.workflowFactory = workflow; this.componentsDir = options.uiConfig?.componentsDir; this.layoutDir = options.uiConfig?.layoutDir ?? "layout"; this.suggestNextQuestions = suggestNextQuestions ?? true; - this.eject = eject; if (this.componentsDir) { this.createComponentsDir(this.componentsDir); From 65864e59831da0138e06f7598a893f43eb72988b Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:30:31 +0700 Subject: [PATCH 08/63] update import workflow path --- packages/server/next/app/api/chat/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index b11801458..585474347 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -9,7 +9,7 @@ import { sendSuggestedQuestionsEvent } from "./utils/suggestion"; import { runWorkflow } from "./utils/workflow"; // import workflow factory from local file -import { workflowFactory } from "../../../../app/workflow"; +import { workflowFactory } from "../../../../src/app/workflow"; export async function POST(req: NextRequest) { try { From 60afca806b97ee429599ce40c770c3a9bd178ed3 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:46:21 +0700 Subject: [PATCH 09/63] init settings in chat route --- packages/server/next/app/api/chat/route.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index 585474347..ddfcf7569 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -8,9 +8,12 @@ import { toDataStream } from "./utils/stream"; import { sendSuggestedQuestionsEvent } from "./utils/suggestion"; import { runWorkflow } from "./utils/workflow"; -// import workflow factory from local file +// import workflow factory and settings from local file +import { initSettings } from "../../../../src/app/settings"; import { workflowFactory } from "../../../../src/app/workflow"; +initSettings(); + export async function POST(req: NextRequest) { try { const reqBody = await req.json(); From 20c662e65ff81a39b6175df089946f6d96225c2b Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:47:03 +0700 Subject: [PATCH 10/63] refactor: move prompts & events to chat utils folder --- packages/server/src/index.ts | 4 ++-- packages/server/src/{ => utils}/events.ts | 0 packages/server/src/{ => utils}/prompts.ts | 0 packages/server/src/utils/suggestion.ts | 2 +- packages/server/src/utils/workflow.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename packages/server/src/{ => utils}/events.ts (100%) rename packages/server/src/{ => utils}/prompts.ts (100%) diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 643f25846..e08c57892 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,5 +1,5 @@ -export * from "./events"; -export * from "./prompts"; export * from "./server"; export * from "./types"; +export * from "./utils/events"; export { generateEventComponent } from "./utils/gen-ui"; +export * from "./utils/prompts"; diff --git a/packages/server/src/events.ts b/packages/server/src/utils/events.ts similarity index 100% rename from packages/server/src/events.ts rename to packages/server/src/utils/events.ts diff --git a/packages/server/src/prompts.ts b/packages/server/src/utils/prompts.ts similarity index 100% rename from packages/server/src/prompts.ts rename to packages/server/src/utils/prompts.ts diff --git a/packages/server/src/utils/suggestion.ts b/packages/server/src/utils/suggestion.ts index 047544acf..3a5e00270 100644 --- a/packages/server/src/utils/suggestion.ts +++ b/packages/server/src/utils/suggestion.ts @@ -1,7 +1,7 @@ import { getEnv } from "@llamaindex/env"; import type { DataStreamWriter } from "ai"; import { type ChatMessage, Settings } from "llamaindex"; -import { NEXT_QUESTION_PROMPT } from "../prompts"; +import { NEXT_QUESTION_PROMPT } from "./prompts"; export const sendSuggestedQuestionsEvent = async ( streamWriter: DataStreamWriter, diff --git a/packages/server/src/utils/workflow.ts b/packages/server/src/utils/workflow.ts index cd83dea1a..566c8f330 100644 --- a/packages/server/src/utils/workflow.ts +++ b/packages/server/src/utils/workflow.ts @@ -19,7 +19,7 @@ import { toAgentRunEvent, toSourceEvent, type SourceEventNode, -} from "../events"; +} from "./events"; import { downloadFile } from "./file"; export async function runWorkflow( From f32e971d3ace55dc549c463d9097249c16728e5c Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 14:50:15 +0700 Subject: [PATCH 11/63] copy utils for chat route --- packages/server/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index f437287ad..09b08c11e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -36,12 +36,12 @@ "prebuild": "pnpm clean", "build": "bunchee", "postbuild": "pnpm prepare:ts-server && pnpm prepare:py-static", - "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:api", + "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:routes", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", "build:css": "postcss server/app/globals.css -o server/app/globals.css && rm -rf ./server/postcss.config.js", "build:api": "rm -rf ./server/app/api && tsc --skipLibCheck --project tsconfig.api.json", - "copy:api": "cp -r ./next/app/api ./dist/api", + "copy:routes": "cp -r ./next/app/api ./dist/api && cp -r ./src/utils ./dist/api/chat", "prepare:static": "cp -r ./next ./temp && rm -rf ./temp/app/api && mv ./temp/next-build.config.ts ./temp/next.config.ts", "build:static": "cd ./temp && next build", "copy:static": "cp -r ./temp/out ./dist/static && rm -rf ./temp" From b66cebb7e02008fccc5f81ef6e9cd1589def2e19 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 15:05:57 +0700 Subject: [PATCH 12/63] update config.js file in eject mode --- packages/server/src/server.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 535f535aa..b0392ee6b 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -13,6 +13,8 @@ const nextDir = path.join(__dirname, "..", "server"); const configFile = path.join(__dirname, "..", "server", "public", "config.js"); const dev = process.env.NODE_ENV !== "production"; +const EJECT_FOLDER = "next"; + export class LlamaIndexServer { port: number; app: ReturnType; @@ -28,13 +30,13 @@ export class LlamaIndexServer { this.eject = process.env.EJECT === "true"; if (this.eject) { console.log( - "Eject mode is enabled in ./next folder. Frontend and route handlers will be hot-reloaded when changes are made.", + `Eject mode is enabled in ./${EJECT_FOLDER} folder. Frontend and routes will be hot-reloaded when changes are made.`, ); } this.app = next({ dev, - dir: this.eject ? "./next" : nextDir, + dir: this.eject ? EJECT_FOLDER : nextDir, ...nextAppOptions, }); this.port = nextAppOptions.port ?? parseInt(process.env.PORT || "3000", 10); @@ -73,7 +75,10 @@ export class LlamaIndexServer { SUGGEST_NEXT_QUESTIONS: ${JSON.stringify(this.suggestNextQuestions)} } `; - fs.writeFileSync(configFile, content); + const configFilePath = this.eject + ? path.join(EJECT_FOLDER, "public", "config.js") + : configFile; + fs.writeFileSync(configFilePath, content); } private async createComponentsDir(componentsDir: string) { From 7c338b3917d1df1987ea55fb1b75ce0a80357b74 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 15:11:19 +0700 Subject: [PATCH 13/63] pass suggestNextQuestions as query params to chat route handler --- packages/server/src/server.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index b0392ee6b..bd30eea1f 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -96,16 +96,22 @@ export class LlamaIndexServer { const pathname = parsedUrl.pathname; const query = parsedUrl.query; - if (pathname === "/api/chat" && req.method === "POST" && !this.eject) { - // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route - // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] - // make sure to keep its semantic in sync with handleChat - return handleChat( - req, - res, - this.workflowFactory, - this.suggestNextQuestions, - ); + if (pathname === "/api/chat" && req.method === "POST") { + query.suggestNextQuestions = this.suggestNextQuestions + ? "true" + : "false"; + + if (!this.eject) { + // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route + // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] + // make sure to keep its semantic in sync with handleChat + return handleChat( + req, + res, + this.workflowFactory, + this.suggestNextQuestions, + ); + } } if ( From 5be6a60e98404f010cc90657c5a8cdbda46fbed5 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 15:12:39 +0700 Subject: [PATCH 14/63] enable/disable suggestNextQuestions in route handler --- packages/server/next/app/api/chat/route.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index ddfcf7569..6d0f96164 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -17,6 +17,9 @@ initSettings(); export async function POST(req: NextRequest) { try { const reqBody = await req.json(); + const params = req.nextUrl.searchParams; + const suggestNextQuestions = params.get("suggestNextQuestions") === "true"; + const { messages } = reqBody as { messages: Message[] }; const chatHistory = messages.map((message) => ({ role: message.role as MessageType, @@ -57,7 +60,9 @@ export async function POST(req: NextRequest) { role: "assistant" as MessageType, content: completion, }); - await sendSuggestedQuestionsEvent(dataStreamWriter, chatHistory); + if (suggestNextQuestions) { + await sendSuggestedQuestionsEvent(dataStreamWriter, chatHistory); + } }, }, }); From 9e021b0beb313716a04eb69f4aebab549db6b692 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 15:23:21 +0700 Subject: [PATCH 15/63] remove todo --- packages/server/next/app/api/chat/route.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index 6d0f96164..8cc1e5b0b 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -53,7 +53,6 @@ export async function POST(req: NextRequest) { ); const dataStream = toDataStream(workflowEventStream, { - // TODO: Support enable/disable suggestion callbacks: { onFinal: async (completion, dataStreamWriter) => { chatHistory.push({ From d8b63902890ebcb6f5171111545df7665f41ceca Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 15:49:22 +0700 Subject: [PATCH 16/63] add document about eject mode --- .../typescript/agentic_rag/README-template.md | 9 ++++++++ .../code_generator/README-template.md | 9 ++++++++ .../deep_research/README-template.md | 9 ++++++++ .../document_generator/README-template.md | 9 ++++++++ .../financial_report/README-template.md | 9 ++++++++ .../llamaindexserver/nextjs/package.json | 6 +++-- packages/server/README.md | 22 +++++++++++++++++++ 7 files changed, 71 insertions(+), 2 deletions(-) diff --git a/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md index 73182ab9f..f0f9389c0 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md @@ -41,6 +41,15 @@ curl --location 'localhost:3000/api/chat' \ --data '{ "messages": [{ "role": "user", "content": "What standards for a letter exist?" }] }' ``` +## Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +npm run eject +npm run dev:eject +``` + ## Learn More To learn more about LlamaIndex, take a look at the following resources: diff --git a/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md index f79151961..a76756e3e 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md @@ -42,6 +42,15 @@ curl --location 'localhost:3000/api/chat' \ --data '{ "messages": [{ "role": "user", "content": "Compare the financial performance of Apple and Tesla" }] }' ``` +## Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +npm run eject +npm run dev:eject +``` + ## Learn More To learn more about LlamaIndex, take a look at the following resources: diff --git a/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md index 5789bd486..f11ae2292 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md @@ -53,6 +53,15 @@ curl --location 'localhost:3000/api/chat' \ --data '{ "messages": [{ "role": "user", "content": "Compare the financial performance of Apple and Tesla" }] }' ``` +## Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +npm run eject +npm run dev:eject +``` + ## Learn More To learn more about LlamaIndex, take a look at the following resources: diff --git a/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md index 528ec9ff6..d921737c5 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md @@ -42,6 +42,15 @@ curl --location 'localhost:3000/api/chat' \ --data '{ "messages": [{ "role": "user", "content": "Compare the financial performance of Apple and Tesla" }] }' ``` +## Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +npm run eject +npm run dev:eject +``` + ## Learn More To learn more about LlamaIndex, take a look at the following resources: diff --git a/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md index 3c31a8283..83f5a4b89 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md @@ -41,6 +41,15 @@ curl --location 'localhost:3000/api/chat' \ --data '{ "messages": [{ "role": "user", "content": "Generate a financial report that compares the financial performance of Apple and Tesla" }] }' ``` +## Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +npm run eject +npm run dev:eject +``` + ## Learn More To learn more about LlamaIndex, take a look at the following resources: diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json index c0864dd54..8ccb68923 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json @@ -7,7 +7,8 @@ "generate:ui": "tsx src/generate.ts ui", "dev": "nodemon", "start": "tsx src/index.ts", - "eject": "llamaindex-server eject" + "eject": "llamaindex-server eject", + "dev:eject": "cross-env EJECT=true pnpm dev" }, "dependencies": { "@llamaindex/openai": "~0.4.0", @@ -22,6 +23,7 @@ "@types/node": "^20.10.3", "tsx": "^4.7.2", "typescript": "^5.3.2", - "nodemon": "^3.1.10" + "nodemon": "^3.1.10", + "cross-env": "^7.0.3" } } diff --git a/packages/server/README.md b/packages/server/README.md index 51bedca90..fb293236f 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -300,6 +300,28 @@ The server always provides a chat interface at the root path (`/`) with: - The server automatically mounts the `data` and `output` folders at `{server_url}{api_prefix}/files/data` (default: `/api/files/data`) and `{server_url}{api_prefix}/files/output` (default: `/api/files/output`) respectively. - Your workflows can use both folders to store and access files. By convention, the `data` folder is used for documents that are ingested, and the `output` folder is used for documents generated by the workflow. +### Eject Mode + +If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. + +```bash +pnpm eject +``` + +``` +next/ -- this folder is generated by pnpm eject +src/ + index.ts + ... +``` + +To start the server in eject mode, set the `EJECT` environment variable to `true`, then run `pnpm run dev`. + +```bash +export EJECT=true pnpm dev +pnpm run dev +``` + ## API Reference - [LlamaIndexServer](https://ts.llamaindex.ai/docs/api/classes/LlamaIndexServer) From d12b7a4b2ee5c1089924c79058a0cd84d05c39a8 Mon Sep 17 00:00:00 2001 From: Thuc Pham <51660321+thucpn@users.noreply.github.com> Date: Mon, 26 May 2025 15:50:55 +0700 Subject: [PATCH 17/63] Create many-knives-warn.md --- .changeset/many-knives-warn.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/many-knives-warn.md diff --git a/.changeset/many-knives-warn.md b/.changeset/many-knives-warn.md new file mode 100644 index 000000000..417206598 --- /dev/null +++ b/.changeset/many-knives-warn.md @@ -0,0 +1,6 @@ +--- +"create-llama": patch +"@llamaindex/server": patch +--- + +support eject to fully customize next folder From 5841c44905e2e76e994be0e8d6faf07edf931fb9 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 16:11:28 +0700 Subject: [PATCH 18/63] ignore ejected next/ folder --- .../templates/types/llamaindexserver/nextjs/gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore b/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore index c6b35cfa8..45634b0a8 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore @@ -37,4 +37,6 @@ next-env.d.ts output/ storage/ -.env \ No newline at end of file +.env + +next/ \ No newline at end of file From 13103525c081fd93a59f5b1e09685c0e4d03ae4c Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 16:59:03 +0700 Subject: [PATCH 19/63] wrong doc --- .../use-cases/typescript/agentic_rag/README-template.md | 3 +-- .../use-cases/typescript/code_generator/README-template.md | 3 +-- .../use-cases/typescript/deep_research/README-template.md | 3 +-- .../use-cases/typescript/document_generator/README-template.md | 3 +-- .../use-cases/typescript/financial_report/README-template.md | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md index f0f9389c0..995eba057 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/agentic_rag/README-template.md @@ -43,11 +43,10 @@ curl --location 'localhost:3000/api/chat' \ ## Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash npm run eject -npm run dev:eject ``` ## Learn More diff --git a/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md index a76756e3e..463237c71 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/code_generator/README-template.md @@ -44,11 +44,10 @@ curl --location 'localhost:3000/api/chat' \ ## Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash npm run eject -npm run dev:eject ``` ## Learn More diff --git a/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md index f11ae2292..45ce35d79 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/deep_research/README-template.md @@ -55,11 +55,10 @@ curl --location 'localhost:3000/api/chat' \ ## Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash npm run eject -npm run dev:eject ``` ## Learn More diff --git a/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md index d921737c5..c54958681 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/document_generator/README-template.md @@ -44,11 +44,10 @@ curl --location 'localhost:3000/api/chat' \ ## Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash npm run eject -npm run dev:eject ``` ## Learn More diff --git a/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md b/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md index 83f5a4b89..be912db97 100644 --- a/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md +++ b/packages/create-llama/templates/components/use-cases/typescript/financial_report/README-template.md @@ -43,11 +43,10 @@ curl --location 'localhost:3000/api/chat' \ ## Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash npm run eject -npm run dev:eject ``` ## Learn More From e35f9bd135700356b13841c8c40b66502e4aa562 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 17:01:42 +0700 Subject: [PATCH 20/63] fix: doc --- .../types/llamaindexserver/nextjs/gitignore | 4 +--- .../types/llamaindexserver/nextjs/package.json | 6 ++---- packages/server/README.md | 16 +--------------- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore b/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore index 45634b0a8..c6b35cfa8 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/gitignore @@ -37,6 +37,4 @@ next-env.d.ts output/ storage/ -.env - -next/ \ No newline at end of file +.env \ No newline at end of file diff --git a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json index 8ccb68923..c0864dd54 100644 --- a/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json +++ b/packages/create-llama/templates/types/llamaindexserver/nextjs/package.json @@ -7,8 +7,7 @@ "generate:ui": "tsx src/generate.ts ui", "dev": "nodemon", "start": "tsx src/index.ts", - "eject": "llamaindex-server eject", - "dev:eject": "cross-env EJECT=true pnpm dev" + "eject": "llamaindex-server eject" }, "dependencies": { "@llamaindex/openai": "~0.4.0", @@ -23,7 +22,6 @@ "@types/node": "^20.10.3", "tsx": "^4.7.2", "typescript": "^5.3.2", - "nodemon": "^3.1.10", - "cross-env": "^7.0.3" + "nodemon": "^3.1.10" } } diff --git a/packages/server/README.md b/packages/server/README.md index fb293236f..62f2c604f 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -302,26 +302,12 @@ The server always provides a chat interface at the root path (`/`) with: ### Eject Mode -If you want to fully customize the server UI and routes, you can use `pnpm eject`. When using `pnpm eject`, the server will be rendered in the `next` folder. Frontend and routes will be hot-reloaded when changes are made. +If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. ```bash pnpm eject ``` -``` -next/ -- this folder is generated by pnpm eject -src/ - index.ts - ... -``` - -To start the server in eject mode, set the `EJECT` environment variable to `true`, then run `pnpm run dev`. - -```bash -export EJECT=true pnpm dev -pnpm run dev -``` - ## API Reference - [LlamaIndexServer](https://ts.llamaindex.ai/docs/api/classes/LlamaIndexServer) From 172bacf44200945f8d912c2a25ace6c6fcf17df4 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 17:04:22 +0700 Subject: [PATCH 21/63] revert server --- packages/server/src/server.ts | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index bd30eea1f..b0392ee6b 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -96,22 +96,16 @@ export class LlamaIndexServer { const pathname = parsedUrl.pathname; const query = parsedUrl.query; - if (pathname === "/api/chat" && req.method === "POST") { - query.suggestNextQuestions = this.suggestNextQuestions - ? "true" - : "false"; - - if (!this.eject) { - // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route - // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] - // make sure to keep its semantic in sync with handleChat - return handleChat( - req, - res, - this.workflowFactory, - this.suggestNextQuestions, - ); - } + if (pathname === "/api/chat" && req.method === "POST" && !this.eject) { + // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route + // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] + // make sure to keep its semantic in sync with handleChat + return handleChat( + req, + res, + this.workflowFactory, + this.suggestNextQuestions, + ); } if ( From 9eb0ea647ec157bebba7982fded1da8673c07e83 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 17:06:32 +0700 Subject: [PATCH 22/63] keep server options --- packages/server/src/server.ts | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index b0392ee6b..71f1c83da 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -13,8 +13,6 @@ const nextDir = path.join(__dirname, "..", "server"); const configFile = path.join(__dirname, "..", "server", "public", "config.js"); const dev = process.env.NODE_ENV !== "production"; -const EJECT_FOLDER = "next"; - export class LlamaIndexServer { port: number; app: ReturnType; @@ -22,23 +20,10 @@ export class LlamaIndexServer { componentsDir?: string | undefined; layoutDir: string; suggestNextQuestions: boolean; - eject: boolean; constructor(options: LlamaIndexServerOptions) { const { workflow, suggestNextQuestions, ...nextAppOptions } = options; - - this.eject = process.env.EJECT === "true"; - if (this.eject) { - console.log( - `Eject mode is enabled in ./${EJECT_FOLDER} folder. Frontend and routes will be hot-reloaded when changes are made.`, - ); - } - - this.app = next({ - dev, - dir: this.eject ? EJECT_FOLDER : nextDir, - ...nextAppOptions, - }); + this.app = next({ dev, dir: nextDir, ...nextAppOptions }); this.port = nextAppOptions.port ?? parseInt(process.env.PORT || "3000", 10); this.workflowFactory = workflow; this.componentsDir = options.uiConfig?.componentsDir; @@ -75,10 +60,7 @@ export class LlamaIndexServer { SUGGEST_NEXT_QUESTIONS: ${JSON.stringify(this.suggestNextQuestions)} } `; - const configFilePath = this.eject - ? path.join(EJECT_FOLDER, "public", "config.js") - : configFile; - fs.writeFileSync(configFilePath, content); + fs.writeFileSync(configFile, content); } private async createComponentsDir(componentsDir: string) { @@ -96,7 +78,7 @@ export class LlamaIndexServer { const pathname = parsedUrl.pathname; const query = parsedUrl.query; - if (pathname === "/api/chat" && req.method === "POST" && !this.eject) { + if (pathname === "/api/chat" && req.method === "POST") { // because of https://github.com/vercel/next.js/discussions/79402 we can't use route.ts here, so we need to call this custom route // when calling `pnpm eject`, the user will get an equivalent route at [path to chat route.ts] // make sure to keep its semantic in sync with handleChat From 80e7267a79944c9a23033f64b472d05c5195b841 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 17:10:48 +0700 Subject: [PATCH 23/63] use config from .env for route handler --- packages/server/next/app/api/chat/route.ts | 3 +-- packages/server/next/app/api/components/route.ts | 3 ++- packages/server/next/app/api/layout/route.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index 8cc1e5b0b..1eec38150 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -17,8 +17,7 @@ initSettings(); export async function POST(req: NextRequest) { try { const reqBody = await req.json(); - const params = req.nextUrl.searchParams; - const suggestNextQuestions = params.get("suggestNextQuestions") === "true"; + const suggestNextQuestions = process.env.SUGGEST_NEXT_QUESTIONS === "true"; const { messages } = reqBody as { messages: Message[] }; const chatHistory = messages.map((message) => ({ diff --git a/packages/server/next/app/api/components/route.ts b/packages/server/next/app/api/components/route.ts index 168e8c21a..ce72faf86 100644 --- a/packages/server/next/app/api/components/route.ts +++ b/packages/server/next/app/api/components/route.ts @@ -3,6 +3,7 @@ import { handleComponentRoute } from "../shared/component-handler"; export async function GET(request: NextRequest) { const params = request.nextUrl.searchParams; - const directory = params.get("componentsDir") || "components"; + const directory = + params.get("componentsDir") || process.env.COMPONENTS_DIR || "components"; return handleComponentRoute(directory); } diff --git a/packages/server/next/app/api/layout/route.ts b/packages/server/next/app/api/layout/route.ts index af63cdc7a..58e04e2a2 100644 --- a/packages/server/next/app/api/layout/route.ts +++ b/packages/server/next/app/api/layout/route.ts @@ -5,6 +5,7 @@ const LAYOUT_TYPES = ["header", "footer"] as const; export async function GET(request: NextRequest) { const params = request.nextUrl.searchParams; - const directory = params.get("layoutDir") || "layout"; + const directory = + params.get("layoutDir") || process.env.LAYOUT_DIR || "layout"; return handleComponentRoute(directory, LAYOUT_TYPES); } From 18b072277311e6680591f18365d06757d4e815e2 Mon Sep 17 00:00:00 2001 From: thucpn Date: Mon, 26 May 2025 18:36:42 +0700 Subject: [PATCH 24/63] prepare nextjs project --- eslint.config.mjs | 1 + packages/server/.gitignore | 3 + packages/server/bin/eject.cjs | 22 ++----- packages/server/next/README.md | 32 +++++++++++ packages/server/next/next-package.json | 80 ++++++++++++++++++++++++++ packages/server/package.json | 7 ++- 6 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 packages/server/next/README.md create mode 100644 packages/server/next/next-package.json diff --git a/eslint.config.mjs b/eslint.config.mjs index a5c2c559d..a5845d8c9 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -58,6 +58,7 @@ export default tseslint.config( "**/node_modules/**", "**/build/**", "packages/server/server/**", + "packages/server/project/**", ], }, ); diff --git a/packages/server/.gitignore b/packages/server/.gitignore index 589466f3e..b5936fb95 100644 --- a/packages/server/.gitignore +++ b/packages/server/.gitignore @@ -1,5 +1,8 @@ # server contains Nextjs frontend code (not compiled) server/ +# the ejected nextjs project +project/ + # temp is the copy of next folder but without API folder, used to build frontend static files temp/ diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index b80bf8d6c..8652f5b47 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -5,14 +5,11 @@ const fs = require("fs").promises; const path = require("path"); -// Resolve the source directory (@llamaindex/server/server) -const sourceDir = path.resolve(__dirname, "../server"); - -// Resolve the route-handler directory -const routeHandlerDir = path.resolve(__dirname, "../dist/api"); +// Resolve the source directory (@llamaindex/server/project) +const sourceDir = path.resolve(__dirname, "../project"); // Resolve the destination directory (consumer's project root/next) -const destDir = path.join(process.cwd(), "next"); +const destDir = path.join(process.cwd(), "next"); // TODO: add parameter to specify the destination directory async function eject() { try { @@ -43,17 +40,8 @@ async function eject() { errorOnExist: true, }); - // Copy the route handler directory to replace the api directory - const destApiDir = path.join(destDir, "app", "api"); - try { - await fs.rm(destApiDir, { recursive: true, force: true }); - } catch (error) { - // Ignore error if directory doesn't exist - } - await fs.cp(routeHandlerDir, destApiDir, { - recursive: true, - force: true, - }); + // TODO: get current package.json and merge it with next-package.json (we need generate scripts) + // TODO: copy src/app/workflow.ts & src/app/settings.ts to next/app/api/chat console.log("Successfully ejected @llamaindex/server/server to", destDir); } catch (error) { diff --git a/packages/server/next/README.md b/packages/server/next/README.md new file mode 100644 index 000000000..ed15f8555 --- /dev/null +++ b/packages/server/next/README.md @@ -0,0 +1,32 @@ +This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`llamaindex-server`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/server). + +## Getting Started + +First, install the dependencies: + +``` +npm install +``` + +Second, generate the embeddings of the documents in the `./data` directory: + +``` +npm run generate +``` + +Third, run the development server: + +``` +npm run dev +``` + +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! diff --git a/packages/server/next/next-package.json b/packages/server/next/next-package.json new file mode 100644 index 000000000..5bc1d115a --- /dev/null +++ b/packages/server/next/next-package.json @@ -0,0 +1,80 @@ +{ + "name": "nextjs-project", + "description": "Next.js project with full feature set of @llamaindex/server", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/babel__standalone": "^7.1.9", + "@types/babel__traverse": "^7.20.7", + "postcss": "^8.5.3", + "postcss-cli": "^11.0.1", + "tailwindcss": "^4", + "tsx": "^4.19.3", + "tw-animate-css": "1.2.5" + }, + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/standalone": "^7.27.0", + "@babel/traverse": "^7.27.0", + "@babel/types": "^7.27.0", + "@hookform/resolvers": "^5.0.1", + "@llamaindex/chat-ui": "0.4.5", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-alert-dialog": "^1.1.7", + "@radix-ui/react-aspect-ratio": "^1.1.3", + "@radix-ui/react-avatar": "^1.1.4", + "@radix-ui/react-checkbox": "^1.1.5", + "@radix-ui/react-collapsible": "^1.1.3", + "@radix-ui/react-context-menu": "^2.2.7", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.7", + "@radix-ui/react-hover-card": "^1.1.7", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-menubar": "^1.1.7", + "@radix-ui/react-navigation-menu": "^1.2.6", + "@radix-ui/react-popover": "^1.1.7", + "@radix-ui/react-progress": "^1.1.3", + "@radix-ui/react-radio-group": "^1.2.4", + "@radix-ui/react-scroll-area": "^1.2.4", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.3", + "@radix-ui/react-slider": "^1.2.1", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-switch": "^1.1.4", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-toggle": "^1.1.3", + "@radix-ui/react-toggle-group": "^1.1.3", + "@radix-ui/react-tooltip": "^1.1.4", + "ai": "^4.2.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", + "input-otp": "^1.4.2", + "lucide-react": "^0.460.0", + "next": "^15.3.0", + "next-themes": "^0.4.3", + "react": "^19.1.0", + "react-day-picker": "8.10.1", + "react-dom": "^19.1.0", + "react-hook-form": "^7.55.0", + "react-resizable-panels": "^2.1.7", + "recharts": "^2.15.2", + "sonner": "^2.0.3", + "tailwind-merge": "^2.6.0", + "vaul": "^1.1.2", + "@llamaindex/env": "~0.1.30", + "@llamaindex/workflow": "~1.1.3", + "llamaindex": "~0.11.0", + "zod": "^3.24.2", + "zod-to-json-schema": "^3.23.3" + } +} diff --git a/packages/server/package.json b/packages/server/package.json index 09b08c11e..8fac99907 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -20,6 +20,7 @@ "files": [ "dist", "server", + "project", "bin" ], "bin": { @@ -32,16 +33,16 @@ }, "scripts": { "dev": "bunchee --watch", - "clean": "rm -rf ./dist ./server next/.next next/out ./temp", + "clean": "rm -rf ./dist ./server ./project next/.next next/out ./temp", "prebuild": "pnpm clean", "build": "bunchee", - "postbuild": "pnpm prepare:ts-server && pnpm prepare:py-static", + "postbuild": "pnpm prepare:project && pnpm prepare:ts-server && pnpm prepare:py-static", "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:routes", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", "build:css": "postcss server/app/globals.css -o server/app/globals.css && rm -rf ./server/postcss.config.js", "build:api": "rm -rf ./server/app/api && tsc --skipLibCheck --project tsconfig.api.json", - "copy:routes": "cp -r ./next/app/api ./dist/api && cp -r ./src/utils ./dist/api/chat", + "prepare:project": "cp -r ./next ./project && cp -r ./src/utils ./project/app/api/chat", "prepare:static": "cp -r ./next ./temp && rm -rf ./temp/app/api && mv ./temp/next-build.config.ts ./temp/next.config.ts", "build:static": "cd ./temp && next build", "copy:static": "cp -r ./temp/out ./dist/static && rm -rf ./temp" From 1135931ff3a87190a839f4f6df9d66fe41a03f76 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 08:49:58 +0700 Subject: [PATCH 25/63] fix: scripts --- packages/server/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/package.json b/packages/server/package.json index 8fac99907..92282a666 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -36,13 +36,13 @@ "clean": "rm -rf ./dist ./server ./project next/.next next/out ./temp", "prebuild": "pnpm clean", "build": "bunchee", - "postbuild": "pnpm prepare:project && pnpm prepare:ts-server && pnpm prepare:py-static", - "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api && pnpm copy:routes", + "postbuild": "pnpm prepare:nextjs && pnpm prepare:ts-server && pnpm prepare:py-static", + "prepare:nextjs": "cp -r ./next ./project && cp -r ./src/utils ./project/app/api/chat", + "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", "build:css": "postcss server/app/globals.css -o server/app/globals.css && rm -rf ./server/postcss.config.js", "build:api": "rm -rf ./server/app/api && tsc --skipLibCheck --project tsconfig.api.json", - "prepare:project": "cp -r ./next ./project && cp -r ./src/utils ./project/app/api/chat", "prepare:static": "cp -r ./next ./temp && rm -rf ./temp/app/api && mv ./temp/next-build.config.ts ./temp/next.config.ts", "build:static": "cd ./temp && next build", "copy:static": "cp -r ./temp/out ./dist/static && rm -rf ./temp" From 77146a188ad0d6e0dc2d90b9a05f49ebf592aea2 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 09:53:31 +0700 Subject: [PATCH 26/63] update script --- eslint.config.mjs | 1 + packages/server/bin/eject.cjs | 75 +++++++++++++++++------------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index a5845d8c9..e5e489fc5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -59,6 +59,7 @@ export default tseslint.config( "**/build/**", "packages/server/server/**", "packages/server/project/**", + "packages/server/bin/**", ], }, ); diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 8652f5b47..f55ca73ec 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -1,57 +1,56 @@ #!/usr/bin/env node -/* eslint-disable @typescript-eslint/no-require-imports */ - const fs = require("fs").promises; const path = require("path"); -// Resolve the source directory (@llamaindex/server/project) -const sourceDir = path.resolve(__dirname, "../project"); +// Resolve the project directory in node_modules/@llamaindex/server/project +// This is the template that used to construct the nextjs project +const projectDir = path.resolve(__dirname, "../project"); -// Resolve the destination directory (consumer's project root/next) -const destDir = path.join(process.cwd(), "next"); // TODO: add parameter to specify the destination directory +// Resolve the src directory that contains workflow & setting files +const srcDir = path.join(process.cwd(), "src"); +const srcAppDir = path.join(srcDir, "app"); +const generateFile = path.join(srcDir, "generate.ts"); // optional, used to generate embeddings for index async function eject() { try { - // Check if source directory exists - const sourceExists = await fs - .access(sourceDir) - .then(() => true) - .catch(() => false); - if (!sourceExists) { - console.error("Error: Source directory does not exist at", sourceDir); - process.exit(1); - } - - // Remove next directory if it exists - try { - await fs.rm(destDir, { recursive: true, force: true }); - } catch (error) { - // Ignore error if directory doesn't exist - } - - // Create next directory + // validate required directories (nextjs project template, src directory, src/app directory) + const requiredDirs = [projectDir, srcDir, srcAppDir]; + requiredDirs.forEach(async (dir) => { + const exists = await fs + .access(dir) + .then(() => true) + .catch(() => false); + if (!exists) { + console.error("Error: directory does not exist at", dir); + process.exit(1); + } + }); + + // Get destination directory from command line arguments + const destDir = process.argv[2] + ? path.resolve(process.argv[2]) // Use provided path + : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory + + // remove destination directory if it exists + await fs.rm(destDir, { recursive: true, force: true }); + + // create destination directory await fs.mkdir(destDir, { recursive: true }); - // Copy the server directory to the next folder - await fs.cp(sourceDir, destDir, { - recursive: true, - force: false, - errorOnExist: true, - }); + // Copy the nextjs project template to the destination directory + await fs.cp(projectDir, destDir, { recursive: true }); + + // copy src/app/* & src/generate.ts to destDir/app/api/chat + const chatRouteDir = path.join(destDir, "app", "api", "chat"); + await fs.cp(srcAppDir, chatRouteDir, { recursive: true }); + await fs.cp(generateFile, chatRouteDir); // TODO: get current package.json and merge it with next-package.json (we need generate scripts) - // TODO: copy src/app/workflow.ts & src/app/settings.ts to next/app/api/chat console.log("Successfully ejected @llamaindex/server/server to", destDir); } catch (error) { - if (error.code === "EEXIST") { - console.error( - "Error: One or more files already exist in the destination directory. Please ensure the destination is clear or manually merge the files.", - ); - } else { - console.error("Error during eject:", error.message); - } + console.error("Error during eject:", error.message); process.exit(1); } } From f7a0871ddc5c14b32faeb392ab8bd177ebdfd320 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 10:10:35 +0700 Subject: [PATCH 27/63] fix eject output argument --- packages/server/bin/eject.cjs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index f55ca73ec..c43d7a333 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -16,7 +16,7 @@ async function eject() { try { // validate required directories (nextjs project template, src directory, src/app directory) const requiredDirs = [projectDir, srcDir, srcAppDir]; - requiredDirs.forEach(async (dir) => { + for (const dir of requiredDirs) { const exists = await fs .access(dir) .then(() => true) @@ -25,12 +25,15 @@ async function eject() { console.error("Error: directory does not exist at", dir); process.exit(1); } - }); + } - // Get destination directory from command line arguments - const destDir = process.argv[2] - ? path.resolve(process.argv[2]) // Use provided path - : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory + // Get destination directory from command line arguments (pnpm eject --output ) + const args = process.argv; + const outputIndex = args.indexOf("--output"); + const destDir = + outputIndex !== -1 && args[outputIndex + 1] + ? path.resolve(args[outputIndex + 1]) // Use provided path after --output + : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory // remove destination directory if it exists await fs.rm(destDir, { recursive: true, force: true }); @@ -41,10 +44,18 @@ async function eject() { // Copy the nextjs project template to the destination directory await fs.cp(projectDir, destDir, { recursive: true }); - // copy src/app/* & src/generate.ts to destDir/app/api/chat + // copy src/app/* to destDir/app/api/chat const chatRouteDir = path.join(destDir, "app", "api", "chat"); - await fs.cp(srcAppDir, chatRouteDir, { recursive: true }); - await fs.cp(generateFile, chatRouteDir); + await fs.cp(srcAppDir, path.join(chatRouteDir, "app"), { recursive: true }); + + // copy generate.ts if it exists + const generateFileExists = await fs + .access(generateFile) + .then(() => true) + .catch(() => false); + if (generateFileExists) { + await fs.cp(generateFile, path.join(chatRouteDir, "generate.ts")); + } // TODO: get current package.json and merge it with next-package.json (we need generate scripts) From a5ecc74071314a0a1a9554203ee5a890437e0fad Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 10:13:28 +0700 Subject: [PATCH 28/63] update doc --- packages/server/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/README.md b/packages/server/README.md index 62f2c604f..3b471690f 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -303,9 +303,10 @@ The server always provides a chat interface at the root path (`/`) with: ### Eject Mode If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. +By default, the ejected project will be in the `next` directory in the current working directory. You can change the output directory by providing the `--output` flag: ```bash -pnpm eject +npm eject --output ``` ## API Reference From a846adc22730bcfda399f0f3f47b6d159b8d6063 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 10:18:17 +0700 Subject: [PATCH 29/63] update route imports --- packages/server/next/app/api/chat/route.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index 1eec38150..18f13c8f6 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -9,8 +9,8 @@ import { sendSuggestedQuestionsEvent } from "./utils/suggestion"; import { runWorkflow } from "./utils/workflow"; // import workflow factory and settings from local file -import { initSettings } from "../../../../src/app/settings"; -import { workflowFactory } from "../../../../src/app/workflow"; +import { initSettings } from "./app/settings"; +import { workflowFactory } from "./app/workflow"; initSettings(); From 7efd2277b2a0cb2fd80a17c2777e9217771c8317 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 10:34:11 +0700 Subject: [PATCH 30/63] update package.json --- packages/server/bin/eject.cjs | 6 ++++++ .../server/next/{.gitignore => gitignore} | 0 .../next/{next-package.json => package.json} | 19 +++++++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) rename packages/server/next/{.gitignore => gitignore} (100%) rename packages/server/next/{next-package.json => package.json} (80%) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index c43d7a333..6e2c211bf 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -57,6 +57,12 @@ async function eject() { await fs.cp(generateFile, path.join(chatRouteDir, "generate.ts")); } + // rename gitignore -> .gitignore + await fs.rename( + path.join(destDir, "gitignore"), + path.join(destDir, ".gitignore"), + ); + // TODO: get current package.json and merge it with next-package.json (we need generate scripts) console.log("Successfully ejected @llamaindex/server/server to", destDir); diff --git a/packages/server/next/.gitignore b/packages/server/next/gitignore similarity index 100% rename from packages/server/next/.gitignore rename to packages/server/next/gitignore diff --git a/packages/server/next/next-package.json b/packages/server/next/package.json similarity index 80% rename from packages/server/next/next-package.json rename to packages/server/next/package.json index 5bc1d115a..bbb493d50 100644 --- a/packages/server/next/next-package.json +++ b/packages/server/next/package.json @@ -5,19 +5,34 @@ "version": "0.0.1", "type": "module", "scripts": { + "generate": "tsx app\\api\\chat\\generate.ts", "dev": "next dev", "build": "next build", - "start": "next start" + "start": "next start", + "lint": "next lint", + "format": "prettier --ignore-unknown --cache --check .", + "format:write": "prettier --ignore-unknown --write .", + "typecheck": "tsc --noEmit" }, "devDependencies": { "@tailwindcss/postcss": "^4", "@types/babel__standalone": "^7.1.9", "@types/babel__traverse": "^7.20.7", + "@types/node": "^20.10.3", + "@types/react": "^19.0.2", + "@types/react-dom": "^19.0.2", + "@types/uuid": "^9.0.8", "postcss": "^8.5.3", "postcss-cli": "^11.0.1", "tailwindcss": "^4", "tsx": "^4.19.3", - "tw-animate-css": "1.2.5" + "tw-animate-css": "1.2.5", + "eslint": "^9.14.0", + "eslint-config-next": "^15.1.3", + "eslint-config-prettier": "^9.1.0", + "prettier": "^3.2.5", + "prettier-plugin-organize-imports": "^3.2.4", + "typescript": "^5.3.2" }, "dependencies": { "@babel/parser": "^7.27.0", From 72795458186af3f549a4b2719228ff02a56dd064 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 10:37:01 +0700 Subject: [PATCH 31/63] update gitignore --- packages/server/next/gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/next/gitignore b/packages/server/next/gitignore index 9b2d3e929..66b4c2948 100644 --- a/packages/server/next/gitignore +++ b/packages/server/next/gitignore @@ -26,6 +26,7 @@ yarn-error.log* # local env files .env*.local +.env # vercel .vercel From e8f899b020ee4ac6b22d61a0af83d8341b0259fb Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 11:14:22 +0700 Subject: [PATCH 32/63] copy other files --- packages/server/bin/eject.cjs | 15 +++++++++------ packages/server/next/package.json | 22 ++++++++++------------ packages/server/next/prettierrc | 3 +++ 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 packages/server/next/prettierrc diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 6e2c211bf..40ec12f23 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -57,13 +57,16 @@ async function eject() { await fs.cp(generateFile, path.join(chatRouteDir, "generate.ts")); } - // rename gitignore -> .gitignore - await fs.rename( - path.join(destDir, "gitignore"), - path.join(destDir, ".gitignore"), - ); + // rename files: .gitignore, .prettierrc + const filesToRename = ["gitignore", "prettierrc"]; + for (const file of filesToRename) { + await fs.rename(path.join(destDir, file), path.join(destDir, `.${file}`)); + } + + // remove next-build.config.ts + await fs.unlink(path.join(destDir, "next-build.config.ts")); - // TODO: get current package.json and merge it with next-package.json (we need generate scripts) + // TODO: copy llamaindex package versions console.log("Successfully ejected @llamaindex/server/server to", destDir); } catch (error) { diff --git a/packages/server/next/package.json b/packages/server/next/package.json index bbb493d50..97bc05ec9 100644 --- a/packages/server/next/package.json +++ b/packages/server/next/package.json @@ -15,24 +15,22 @@ "typecheck": "tsc --noEmit" }, "devDependencies": { - "@tailwindcss/postcss": "^4", "@types/babel__standalone": "^7.1.9", "@types/babel__traverse": "^7.20.7", - "@types/node": "^20.10.3", - "@types/react": "^19.0.2", - "@types/react-dom": "^19.0.2", - "@types/uuid": "^9.0.8", - "postcss": "^8.5.3", - "postcss-cli": "^11.0.1", - "tailwindcss": "^4", + "typescript": "^5", "tsx": "^4.19.3", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "@tailwindcss/postcss": "^4", + "tailwindcss": "^4", "tw-animate-css": "1.2.5", - "eslint": "^9.14.0", - "eslint-config-next": "^15.1.3", + "eslint": "^9", + "eslint-config-next": "15.3.2", + "@eslint/eslintrc": "^3", "eslint-config-prettier": "^9.1.0", "prettier": "^3.2.5", - "prettier-plugin-organize-imports": "^3.2.4", - "typescript": "^5.3.2" + "prettier-plugin-organize-imports": "^3.2.4" }, "dependencies": { "@babel/parser": "^7.27.0", diff --git a/packages/server/next/prettierrc b/packages/server/next/prettierrc new file mode 100644 index 000000000..55c1943ae --- /dev/null +++ b/packages/server/next/prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-organize-imports"] +} From 2fed965c173f4986b0053bc5931e0126ba7e9373 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 11:36:40 +0700 Subject: [PATCH 33/63] nextjs project config files --- packages/server/next/prettierrc | 3 -- packages/server/package.json | 2 +- .../server/project-config/eslint.config.mjs | 34 +++++++++++++++++++ .../server/{next => project-config}/gitignore | 0 .../{next => project-config}/package.json | 9 ++--- .../server/project-config/prettier.config.mjs | 3 ++ 6 files changed, 43 insertions(+), 8 deletions(-) delete mode 100644 packages/server/next/prettierrc create mode 100644 packages/server/project-config/eslint.config.mjs rename packages/server/{next => project-config}/gitignore (100%) rename packages/server/{next => project-config}/package.json (93%) create mode 100644 packages/server/project-config/prettier.config.mjs diff --git a/packages/server/next/prettierrc b/packages/server/next/prettierrc deleted file mode 100644 index 55c1943ae..000000000 --- a/packages/server/next/prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "plugins": ["prettier-plugin-organize-imports"] -} diff --git a/packages/server/package.json b/packages/server/package.json index 92282a666..2075dae8b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -37,7 +37,7 @@ "prebuild": "pnpm clean", "build": "bunchee", "postbuild": "pnpm prepare:nextjs && pnpm prepare:ts-server && pnpm prepare:py-static", - "prepare:nextjs": "cp -r ./next ./project && cp -r ./src/utils ./project/app/api/chat", + "prepare:nextjs": "cp -r ./next ./project && cp -r ./src/utils ./project/app/api/chat && cp -r ./project-config/* ./project/", "prepare:ts-server": "pnpm copy:next-src && pnpm build:css && pnpm build:api", "prepare:py-static": "pnpm prepare:static && pnpm build:static && pnpm copy:static", "copy:next-src": "cp -r ./next ./server", diff --git a/packages/server/project-config/eslint.config.mjs b/packages/server/project-config/eslint.config.mjs new file mode 100644 index 000000000..4da0f732b --- /dev/null +++ b/packages/server/project-config/eslint.config.mjs @@ -0,0 +1,34 @@ +import { FlatCompat } from "@eslint/eslintrc"; +import { dirname } from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript", "prettier"), + { + rules: { + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": "off", + "react-hooks/exhaustive-deps": "off", + "@next/next/no-img-element": "off", + "@next/next/no-assign-module-variable": "off", + }, + }, + { + ignores: [ + "**/.next/**", + "**/node_modules/**", + "prettier.config.mjs", + "eslint.config.mjs", + "postcss.config.js", + ], + }, +]; + +export default eslintConfig; diff --git a/packages/server/next/gitignore b/packages/server/project-config/gitignore similarity index 100% rename from packages/server/next/gitignore rename to packages/server/project-config/gitignore diff --git a/packages/server/next/package.json b/packages/server/project-config/package.json similarity index 93% rename from packages/server/next/package.json rename to packages/server/project-config/package.json index 97bc05ec9..241f841ec 100644 --- a/packages/server/next/package.json +++ b/packages/server/project-config/package.json @@ -5,14 +5,14 @@ "version": "0.0.1", "type": "module", "scripts": { - "generate": "tsx app\\api\\chat\\generate.ts", "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "format": "prettier --ignore-unknown --cache --check .", "format:write": "prettier --ignore-unknown --write .", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "generate": "tsx app\\api\\chat\\generate.ts" }, "devDependencies": { "@types/babel__standalone": "^7.1.9", @@ -30,7 +30,8 @@ "@eslint/eslintrc": "^3", "eslint-config-prettier": "^9.1.0", "prettier": "^3.2.5", - "prettier-plugin-organize-imports": "^3.2.4" + "prettier-plugin-organize-imports": "^4.1.0", + "prettier-plugin-tailwindcss": "^0.6.11" }, "dependencies": { "@babel/parser": "^7.27.0", @@ -76,7 +77,7 @@ "next": "^15.3.0", "next-themes": "^0.4.3", "react": "^19.1.0", - "react-day-picker": "8.10.1", + "react-day-picker": "^9.7.0", "react-dom": "^19.1.0", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", diff --git a/packages/server/project-config/prettier.config.mjs b/packages/server/project-config/prettier.config.mjs new file mode 100644 index 000000000..e2c5f1198 --- /dev/null +++ b/packages/server/project-config/prettier.config.mjs @@ -0,0 +1,3 @@ +export default { + plugins: ["prettier-plugin-organize-imports", "prettier-plugin-tailwindcss"], +}; From d4fc3151cac93cd2c98791e173e286423e5b2e03 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 11:39:02 +0700 Subject: [PATCH 34/63] update eject script --- packages/server/bin/eject.cjs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 40ec12f23..141574eba 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -57,18 +57,18 @@ async function eject() { await fs.cp(generateFile, path.join(chatRouteDir, "generate.ts")); } - // rename files: .gitignore, .prettierrc - const filesToRename = ["gitignore", "prettierrc"]; - for (const file of filesToRename) { - await fs.rename(path.join(destDir, file), path.join(destDir, `.${file}`)); - } + // rename gitignore -> .gitignore + await fs.rename( + path.join(destDir, "gitignore"), + path.join(destDir, ".gitignore"), + ); // remove next-build.config.ts await fs.unlink(path.join(destDir, "next-build.config.ts")); // TODO: copy llamaindex package versions - console.log("Successfully ejected @llamaindex/server/server to", destDir); + console.log("Successfully ejected @llamaindex/server to", destDir); } catch (error) { console.error("Error during eject:", error.message); process.exit(1); From 8f01f37ea5925217aa8f2d3a7d5448508b04f6fb Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 12:23:13 +0700 Subject: [PATCH 35/63] update package.json --- packages/server/project-config/package.json | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/server/project-config/package.json b/packages/server/project-config/package.json index 241f841ec..60c16e9c8 100644 --- a/packages/server/project-config/package.json +++ b/packages/server/project-config/package.json @@ -15,23 +15,23 @@ "generate": "tsx app\\api\\chat\\generate.ts" }, "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", "@types/babel__standalone": "^7.1.9", "@types/babel__traverse": "^7.20.7", - "typescript": "^5", - "tsx": "^4.19.3", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", - "tw-animate-css": "1.2.5", "eslint": "^9", - "eslint-config-next": "15.3.2", - "@eslint/eslintrc": "^3", + "eslint-config-next": "^15.1.3", "eslint-config-prettier": "^9.1.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-tailwindcss": "^0.6.11" + "prettier-plugin-tailwindcss": "^0.6.11", + "tailwindcss": "^4", + "tsx": "^4.19.3", + "tw-animate-css": "1.2.5", + "typescript": "^5" }, "dependencies": { "@babel/parser": "^7.27.0", @@ -40,6 +40,11 @@ "@babel/types": "^7.27.0", "@hookform/resolvers": "^5.0.1", "@llamaindex/chat-ui": "0.4.5", + "@llamaindex/env": "~0.1.30", + "@llamaindex/openai": "~0.4.0", + "@llamaindex/readers": "~3.1.4", + "@llamaindex/tools": "~0.0.11", + "@llamaindex/workflow": "~1.1.3", "@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-alert-dialog": "^1.1.7", "@radix-ui/react-aspect-ratio": "^1.1.3", @@ -73,11 +78,12 @@ "date-fns": "^4.1.0", "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", + "llamaindex": "~0.11.0", "lucide-react": "^0.460.0", "next": "^15.3.0", "next-themes": "^0.4.3", "react": "^19.1.0", - "react-day-picker": "^9.7.0", + "react-day-picker": "8.10.1", "react-dom": "^19.1.0", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", @@ -85,10 +91,7 @@ "sonner": "^2.0.3", "tailwind-merge": "^2.6.0", "vaul": "^1.1.2", - "@llamaindex/env": "~0.1.30", - "@llamaindex/workflow": "~1.1.3", - "llamaindex": "~0.11.0", - "zod": "^3.24.2", + "zod": "^3.23.8", "zod-to-json-schema": "^3.23.3" } } From 83c82034855db77338c4e66aaadc5b2466e4fa33 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 12:28:58 +0700 Subject: [PATCH 36/63] missing eslint packages --- packages/server/project-config/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/project-config/package.json b/packages/server/project-config/package.json index 60c16e9c8..3ef246f13 100644 --- a/packages/server/project-config/package.json +++ b/packages/server/project-config/package.json @@ -16,6 +16,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@next/eslint-plugin-next": "^15.3.2", "@tailwindcss/postcss": "^4", "@types/babel__standalone": "^7.1.9", "@types/babel__traverse": "^7.20.7", @@ -25,6 +26,7 @@ "eslint": "^9", "eslint-config-next": "^15.1.3", "eslint-config-prettier": "^9.1.0", + "eslint-plugin-react-hooks": "^5.2.0", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-tailwindcss": "^0.6.11", From fdcd7769e70fba69f227b345d14636386265103f Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 12:45:01 +0700 Subject: [PATCH 37/63] bump react-day-picker to fix peer deps issue with date-fns --- .../next/app/components/ui/calendar.tsx | 12 ++++----- packages/server/package.json | 2 +- packages/server/project-config/package.json | 2 +- pnpm-lock.yaml | 26 ++++++++++++++----- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/server/next/app/components/ui/calendar.tsx b/packages/server/next/app/components/ui/calendar.tsx index 41c0c0875..f977c0c19 100644 --- a/packages/server/next/app/components/ui/calendar.tsx +++ b/packages/server/next/app/components/ui/calendar.tsx @@ -60,12 +60,12 @@ function Calendar({ ...classNames, }} components={{ - IconLeft: ({ className, ...props }) => ( - - ), - IconRight: ({ className, ...props }) => ( - - ), + Chevron: ({ ...props }) => + props.orientation === "left" ? ( + + ) : ( + + ), }} {...props} /> diff --git a/packages/server/package.json b/packages/server/package.json index 2075dae8b..d84459aa2 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -103,7 +103,7 @@ "next": "^15.3.0", "next-themes": "^0.4.3", "react": "^19.1.0", - "react-day-picker": "8.10.1", + "react-day-picker": "9.7.0", "react-dom": "^19.1.0", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", diff --git a/packages/server/project-config/package.json b/packages/server/project-config/package.json index 3ef246f13..dd95f45a7 100644 --- a/packages/server/project-config/package.json +++ b/packages/server/project-config/package.json @@ -85,7 +85,7 @@ "next": "^15.3.0", "next-themes": "^0.4.3", "react": "^19.1.0", - "react-day-picker": "8.10.1", + "react-day-picker": "9.7.0", "react-dom": "^19.1.0", "react-hook-form": "^7.55.0", "react-resizable-panels": "^2.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fac443fe8..fc7146117 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -301,8 +301,8 @@ importers: specifier: ^19.1.0 version: 19.1.0 react-day-picker: - specifier: 8.10.1 - version: 8.10.1(date-fns@4.1.0)(react@19.1.0) + specifier: 9.7.0 + version: 9.7.0(react@19.1.0) react-dom: specifier: ^19.1.0 version: 19.1.0(react@19.1.0) @@ -613,6 +613,9 @@ packages: peerDependencies: '@bufbuild/protobuf': ^2.2.0 + '@date-fns/tz@1.2.0': + resolution: {integrity: sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==} + '@discoveryjs/json-ext@0.6.3': resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} engines: {node: '>=14.17.0'} @@ -3256,6 +3259,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + date-fns-jalali@4.1.0-0: + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -5358,11 +5364,11 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-day-picker@8.10.1: - resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} + react-day-picker@9.7.0: + resolution: {integrity: sha512-urlK4C9XJZVpQ81tmVgd2O7lZ0VQldZeHzNejbwLWZSkzHH498KnArT0EHNfKBOWwKc935iMLGZdxXPRISzUxQ==} + engines: {node: '>=18'} peerDependencies: - date-fns: ^2.28.0 || ^3.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: '>=16.8.0' react-dom@19.1.0: resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} @@ -6812,6 +6818,8 @@ snapshots: dependencies: '@bufbuild/protobuf': 2.3.0 + '@date-fns/tz@1.2.0': {} + '@discoveryjs/json-ext@0.6.3': {} '@e2b/code-interpreter@1.5.0': @@ -9443,6 +9451,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + date-fns-jalali@4.1.0-0: {} + date-fns@4.1.0: {} debug@3.2.7: @@ -11895,9 +11905,11 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-day-picker@8.10.1(date-fns@4.1.0)(react@19.1.0): + react-day-picker@9.7.0(react@19.1.0): dependencies: + '@date-fns/tz': 1.2.0 date-fns: 4.1.0 + date-fns-jalali: 4.1.0-0 react: 19.1.0 react-dom@19.1.0(react@19.1.0): From cf8678c5d8e28d32a94e10a52163eb5dd06c743e Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 13:19:54 +0700 Subject: [PATCH 38/63] missing dotenv --- packages/server/project-config/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/project-config/package.json b/packages/server/project-config/package.json index dd95f45a7..b43d62d02 100644 --- a/packages/server/project-config/package.json +++ b/packages/server/project-config/package.json @@ -78,6 +78,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "date-fns": "^4.1.0", + "dotenv": "^16.5.0", "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", "llamaindex": "~0.11.0", From 13ee73affa5a859a317dc6673877c1ecbfa769e7 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 14:40:50 +0700 Subject: [PATCH 39/63] update ignore --- packages/server/project-config/gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/project-config/gitignore b/packages/server/project-config/gitignore index 66b4c2948..571c33d7e 100644 --- a/packages/server/project-config/gitignore +++ b/packages/server/project-config/gitignore @@ -36,5 +36,6 @@ yarn-error.log* next-env.d.ts output/ +storage/ !lib/ \ No newline at end of file From cdfbf9533c6c79d631911f624097bd3ae48f5723 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 15:00:28 +0700 Subject: [PATCH 40/63] copy env --- packages/server/bin/eject.cjs | 66 +++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 141574eba..548756a8f 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -10,7 +10,8 @@ const projectDir = path.resolve(__dirname, "../project"); // Resolve the src directory that contains workflow & setting files const srcDir = path.join(process.cwd(), "src"); const srcAppDir = path.join(srcDir, "app"); -const generateFile = path.join(srcDir, "generate.ts"); // optional, used to generate embeddings for index +const generateFile = path.join(srcDir, "generate.ts"); +const envFile = path.join(process.cwd(), ".env"); async function eject() { try { @@ -49,14 +50,53 @@ async function eject() { await fs.cp(srcAppDir, path.join(chatRouteDir, "app"), { recursive: true }); // copy generate.ts if it exists - const generateFileExists = await fs - .access(generateFile) - .then(() => true) - .catch(() => false); - if (generateFileExists) { - await fs.cp(generateFile, path.join(chatRouteDir, "generate.ts")); + await copy(generateFile, path.join(chatRouteDir, "generate.ts")); + + // copy folders in root directory if exists + const rootFolders = ["components", "layout", "data", "output", "storage"]; + for (const folder of rootFolders) { + await copy(path.join(process.cwd(), folder), path.join(destDir, folder)); + } + + // copy .env if it exists or create a new one + const envFileExists = await copy(envFile, path.join(destDir, ".env")); + if (!envFileExists) { + await fs.writeFile(path.join(destDir, ".env"), ""); } + // update .env file with more server configs + const envVars = [ + { + key: "OPENAI_API_KEY", + defaultValue: "", + description: "OpenAI API key", + }, + { + key: "SUGGEST_NEXT_QUESTIONS", + defaultValue: "true", + description: "Whether to suggest next questions", + }, + { + key: "COMPONENTS_DIR", + defaultValue: "components", + description: "Directory for custom components", + }, + { + key: "LAYOUT_DIR", + defaultValue: "layout", + description: "Directory for custom layout", + }, + ]; + let envFileContent = await fs.readFile(path.join(destDir, ".env"), "utf-8"); + for (const envVar of envVars) { + const { key, defaultValue, description } = envVar; + if (!envFileContent.includes(key)) { + // if the key is not exists in the env file, add it + envFileContent += `\n# ${description}\n${key}=${defaultValue}\n`; + } + } + await fs.writeFile(path.join(destDir, ".env"), envFileContent); + // rename gitignore -> .gitignore await fs.rename( path.join(destDir, "gitignore"), @@ -75,4 +115,16 @@ async function eject() { } } +// copy src to dest if src exists, return true if src exists +async function copy(src, dest) { + const srcExists = await fs + .access(src) + .then(() => true) + .catch(() => false); + if (srcExists) { + await fs.cp(src, dest, { recursive: true }); + } + return srcExists; +} + eject(); From 2c0fbdc3a356d340f57108be413d6a2d753a3736 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 15:17:27 +0700 Subject: [PATCH 41/63] update frontend config.js file --- packages/server/bin/eject.cjs | 66 ++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 548756a8f..040bd2e13 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -13,6 +13,42 @@ const srcAppDir = path.join(srcDir, "app"); const generateFile = path.join(srcDir, "generate.ts"); const envFile = path.join(process.cwd(), ".env"); +// The environment variables that are used as LlamaIndexServer configs +const SERVER_CONFIG_VARS = [ + { + key: "OPENAI_API_KEY", + defaultValue: "", + description: "OpenAI API key", + }, + { + key: "SUGGEST_NEXT_QUESTIONS", + defaultValue: "true", + description: "Whether to suggest next questions", + }, + { + key: "COMPONENTS_DIR", + defaultValue: "components", + description: "Directory for custom components", + }, + { + key: "LAYOUT_DIR", + defaultValue: "layout", + description: "Directory for custom layout", + }, +]; + +// The default frontend config.js file content, endpoints are fixed +const DEFAULT_FRONTEND_CONFIG = ` +window.LLAMAINDEX = { + CHAT_API: '/api/chat', + LLAMA_CLOUD_API: '/api/chat/config/llamacloud', + COMPONENTS_API: '/api/components', + LAYOUT_API: '/api/layout', + DEV_MODE: true, + STARTER_QUESTIONS: [] +} +`.trim(); + async function eject() { try { // validate required directories (nextjs project template, src directory, src/app directory) @@ -65,30 +101,8 @@ async function eject() { } // update .env file with more server configs - const envVars = [ - { - key: "OPENAI_API_KEY", - defaultValue: "", - description: "OpenAI API key", - }, - { - key: "SUGGEST_NEXT_QUESTIONS", - defaultValue: "true", - description: "Whether to suggest next questions", - }, - { - key: "COMPONENTS_DIR", - defaultValue: "components", - description: "Directory for custom components", - }, - { - key: "LAYOUT_DIR", - defaultValue: "layout", - description: "Directory for custom layout", - }, - ]; let envFileContent = await fs.readFile(path.join(destDir, ".env"), "utf-8"); - for (const envVar of envVars) { + for (const envVar of SERVER_CONFIG_VARS) { const { key, defaultValue, description } = envVar; if (!envFileContent.includes(key)) { // if the key is not exists in the env file, add it @@ -97,6 +111,10 @@ async function eject() { } await fs.writeFile(path.join(destDir, ".env"), envFileContent); + // update frontend config.js file + const frontendConfigFile = path.join(destDir, "frontend", "config.js"); + await fs.writeFile(frontendConfigFile, DEFAULT_FRONTEND_CONFIG); + // rename gitignore -> .gitignore await fs.rename( path.join(destDir, "gitignore"), @@ -106,8 +124,6 @@ async function eject() { // remove next-build.config.ts await fs.unlink(path.join(destDir, "next-build.config.ts")); - // TODO: copy llamaindex package versions - console.log("Successfully ejected @llamaindex/server to", destDir); } catch (error) { console.error("Error during eject:", error.message); From 4c9233577c8125316cc31f300dac9b1cae462464 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 15:28:20 +0700 Subject: [PATCH 42/63] fix: config path --- packages/server/bin/eject.cjs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 040bd2e13..d141462db 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -40,12 +40,15 @@ const SERVER_CONFIG_VARS = [ // The default frontend config.js file content, endpoints are fixed const DEFAULT_FRONTEND_CONFIG = ` window.LLAMAINDEX = { + // these endpoints are fixed in the ejected nextjs project, don't change them CHAT_API: '/api/chat', LLAMA_CLOUD_API: '/api/chat/config/llamacloud', COMPONENTS_API: '/api/components', LAYOUT_API: '/api/layout', - DEV_MODE: true, - STARTER_QUESTIONS: [] + + // update these values to customize frontend + DEV_MODE: true, // whether to enable dev mode + STARTER_QUESTIONS: [] // initial questions to display in the chat } `.trim(); @@ -112,7 +115,7 @@ async function eject() { await fs.writeFile(path.join(destDir, ".env"), envFileContent); // update frontend config.js file - const frontendConfigFile = path.join(destDir, "frontend", "config.js"); + const frontendConfigFile = path.join(destDir, "public", "config.js"); await fs.writeFile(frontendConfigFile, DEFAULT_FRONTEND_CONFIG); // rename gitignore -> .gitignore From d7082b7686581473769b0aface22e7a5c8de8e08 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 16:08:29 +0700 Subject: [PATCH 43/63] enable llamacloud --- packages/server/bin/eject.cjs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index d141462db..44b3f7af9 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -37,18 +37,20 @@ const SERVER_CONFIG_VARS = [ }, ]; -// The default frontend config.js file content, endpoints are fixed +// The default frontend config.js file content const DEFAULT_FRONTEND_CONFIG = ` window.LLAMAINDEX = { // these endpoints are fixed in the ejected nextjs project, don't change them CHAT_API: '/api/chat', - LLAMA_CLOUD_API: '/api/chat/config/llamacloud', COMPONENTS_API: '/api/components', LAYOUT_API: '/api/layout', // update these values to customize frontend DEV_MODE: true, // whether to enable dev mode - STARTER_QUESTIONS: [] // initial questions to display in the chat + STARTER_QUESTIONS: [], // initial questions to display in the chat + + // uncomment this to enable LlamaCloud (need to set LLAMA_CLOUD_API_KEY in .env) + // LLAMA_CLOUD_API: '/api/chat/config/llamacloud', } `.trim(); @@ -116,7 +118,15 @@ async function eject() { // update frontend config.js file const frontendConfigFile = path.join(destDir, "public", "config.js"); - await fs.writeFile(frontendConfigFile, DEFAULT_FRONTEND_CONFIG); + let frontendConfigContent = DEFAULT_FRONTEND_CONFIG; + if (envFileContent.includes("LLAMA_CLOUD_API_KEY")) { + // if user has LLAMA_CLOUD_API_KEY in .env, auto enable LlamaCloud for frontend + frontendConfigContent = frontendConfigContent.replace( + "// LLAMA_CLOUD_API", + "LLAMA_CLOUD_API", + ); + } + await fs.writeFile(frontendConfigFile, frontendConfigContent); // rename gitignore -> .gitignore await fs.rename( From f8a4275143c9f363ed83573908743f092cd85f7a Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 16:13:19 +0700 Subject: [PATCH 44/63] no need layout for normal nextjs --- packages/server/bin/eject.cjs | 11 ++++++++--- .../next/app/components/ui/chat/layout/index.tsx | 4 +++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 44b3f7af9..535ab8d61 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -43,7 +43,6 @@ window.LLAMAINDEX = { // these endpoints are fixed in the ejected nextjs project, don't change them CHAT_API: '/api/chat', COMPONENTS_API: '/api/components', - LAYOUT_API: '/api/layout', // update these values to customize frontend DEV_MODE: true, // whether to enable dev mode @@ -94,7 +93,7 @@ async function eject() { await copy(generateFile, path.join(chatRouteDir, "generate.ts")); // copy folders in root directory if exists - const rootFolders = ["components", "layout", "data", "output", "storage"]; + const rootFolders = ["components", "data", "output", "storage"]; for (const folder of rootFolders) { await copy(path.join(process.cwd(), folder), path.join(destDir, folder)); } @@ -134,7 +133,13 @@ async function eject() { path.join(destDir, ".gitignore"), ); - // remove next-build.config.ts + // user can customize layout directory in nextjs project, remove layout api + await fs.rm(path.join(destDir, "app", "api", "layout"), { + recursive: true, + force: true, + }); + + // clean up, remove no-needed files await fs.unlink(path.join(destDir, "next-build.config.ts")); console.log("Successfully ejected @llamaindex/server to", destDir); diff --git a/packages/server/next/app/components/ui/chat/layout/index.tsx b/packages/server/next/app/components/ui/chat/layout/index.tsx index 440a36d2c..66100481f 100644 --- a/packages/server/next/app/components/ui/chat/layout/index.tsx +++ b/packages/server/next/app/components/ui/chat/layout/index.tsx @@ -121,7 +121,9 @@ async function parseLayoutComponents(layoutFiles: LayoutFile[]) { async function fetchLayoutFiles(): Promise { try { - const response = await fetch(getConfig("LAYOUT_API")); + const layoutApi = getConfig("LAYOUT_API"); + if (!layoutApi) return []; + const response = await fetch(layoutApi); const layoutFiles: LayoutFile[] = await response.json(); return layoutFiles; } catch (error) { From 27994c9f7e32d84393f77dccfd2c80fed6ea5eab Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 16:33:38 +0700 Subject: [PATCH 45/63] remove LAYOUT_DIR from env --- packages/server/bin/eject.cjs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 535ab8d61..848b7e6d5 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -30,11 +30,6 @@ const SERVER_CONFIG_VARS = [ defaultValue: "components", description: "Directory for custom components", }, - { - key: "LAYOUT_DIR", - defaultValue: "layout", - description: "Directory for custom layout", - }, ]; // The default frontend config.js file content From 81b0fc96f19d38c45c8f80a4395b01f3b3f28c0a Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 16:34:03 +0700 Subject: [PATCH 46/63] don't use LAYOUT_DIR --- packages/server/next/app/api/layout/route.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/server/next/app/api/layout/route.ts b/packages/server/next/app/api/layout/route.ts index 58e04e2a2..af63cdc7a 100644 --- a/packages/server/next/app/api/layout/route.ts +++ b/packages/server/next/app/api/layout/route.ts @@ -5,7 +5,6 @@ const LAYOUT_TYPES = ["header", "footer"] as const; export async function GET(request: NextRequest) { const params = request.nextUrl.searchParams; - const directory = - params.get("layoutDir") || process.env.LAYOUT_DIR || "layout"; + const directory = params.get("layoutDir") || "layout"; return handleComponentRoute(directory, LAYOUT_TYPES); } From 9875737144c6aa47f04a9caf02813026bbd85fd3 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 16:42:29 +0700 Subject: [PATCH 47/63] update document --- packages/server/next/README.md | 57 +++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/packages/server/next/README.md b/packages/server/next/README.md index ed15f8555..f14da21ca 100644 --- a/packages/server/next/README.md +++ b/packages/server/next/README.md @@ -1,26 +1,61 @@ -This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`llamaindex-server`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/server). +This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) that is ejected from [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server) via `npm eject` command. -## Getting Started +## Quick Start -First, install the dependencies: - -``` +```bash npm install +npm run dev ``` -Second, generate the embeddings of the documents in the `./data` directory: +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -``` -npm run generate +## Configuration + +The same as [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server#configuration-options), you can customize the application via .env and public/config.js file. + +Here's the examples of how to migrate from LlamaIndexServer configs to Next.js project: + +```ts +// src/index.ts +new LlamaIndexServer({ + workflow: workflowFactory, + suggestNextQuestions: true, + uiConfig: { + devMode: true, + llamaCloudIndexSelector: true, + starterQuestions: ["Summarize the document", "What are the key points?"], + componentsDir: "components", + layoutDir: "layout", + }, +}).start(); ``` -Third, run the development server: +.env file: +```bash +SUGGEST_NEXT_QUESTIONS=true # Whether to suggest next questions +COMPONENTS_DIR=components # Directory for custom components ``` -npm run dev + +public/config.js file: + +```js +window.LLAMAINDEX = { + DEV_MODE: true, // whether to enable dev mode + STARTER_QUESTIONS: [], // initial questions to display in the chat + LLAMA_CLOUD_API: "/api/chat/config/llamacloud", // enable LlamaCloud for frontend +}; ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +For customizing layout, you can directly edit the layout files in the generated nextjs project (app/components/ui/chat/layout). + +## Useful Commands + +- Generate Datasource (in case having `./data` folder): `npm run generate` +- Typecheck: `npm run typecheck` +- Lint: `npm run lint` +- Format: `npm run format` +- Build & Start: `npm run build && npm run start` ## Learn More From a8d78c22c014847b55edc6844dca4ff20866c087 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 17:02:07 +0700 Subject: [PATCH 48/63] update imports in workflow file --- packages/server/bin/eject.cjs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 848b7e6d5..138acd872 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -84,6 +84,15 @@ async function eject() { const chatRouteDir = path.join(destDir, "app", "api", "chat"); await fs.cp(srcAppDir, path.join(chatRouteDir, "app"), { recursive: true }); + // nextjs project doesn't depend on @llamaindex/server anymore, we need to update the imports in workflow file + const workflowFile = path.join(chatRouteDir, "app", "workflow.ts"); + let workflowContent = await fs.readFile(workflowFile, "utf-8"); + workflowContent = workflowContent.replace( + "@llamaindex/server", + "../utils/events", + ); + await fs.writeFile(workflowFile, workflowContent); + // copy generate.ts if it exists await copy(generateFile, path.join(chatRouteDir, "generate.ts")); From 541cdd31f26818817a788153c3c770681fa8cd70 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 17:09:38 +0700 Subject: [PATCH 49/63] update document --- packages/server/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/server/README.md b/packages/server/README.md index 3b471690f..929ce7e39 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -309,6 +309,14 @@ By default, the ejected project will be in the `next` directory in the current w npm eject --output ``` +How eject works: + +1. Init nextjs project with eslint, prettier, postcss, tailwindcss, shadcn components, etc. +2. Copy your workflow definition and setting files in src/app/\* to the ejected project in app/api/chat +3. Copy your components, data, output, storage folders to the ejected project +4. Copy your current .env file to the ejected project +5. Clean up files that are no longer needed and update imports + ## API Reference - [LlamaIndexServer](https://ts.llamaindex.ai/docs/api/classes/LlamaIndexServer) From 9bcd6f3c35596bd59ec185dfd79b9e13a9baeb64 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 17:49:53 +0700 Subject: [PATCH 50/63] use NEXT_PUBLIC for frontend config --- packages/server/bin/eject.cjs | 57 +++++++++---------- packages/server/next/README.md | 19 +++---- .../app/components/ui/chat/chat-input.tsx | 6 +- .../app/components/ui/chat/chat-section.tsx | 2 +- .../app/components/ui/chat/chat-starter.tsx | 4 +- .../ui/chat/custom/events/loader.ts | 6 +- .../ui/chat/custom/llama-cloud-selector.tsx | 10 +++- .../app/components/ui/chat/dev-mode-panel.tsx | 3 +- 8 files changed, 58 insertions(+), 49 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 138acd872..f4b5d1819 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -28,26 +28,31 @@ const SERVER_CONFIG_VARS = [ { key: "COMPONENTS_DIR", defaultValue: "components", - description: "Directory for custom components", + description: "Directory for fetching custom components", + }, + { + key: "NEXT_PUBLIC_USE_COMPONENTS_DIR", + defaultValue: "true", + description: "Whether to enable components directory feature on frontend", + }, + { + key: "NEXT_PUBLIC_DEV_MODE", + defaultValue: "true", + description: "Whether to enable dev mode for frontend", + }, + { + key: "NEXT_PUBLIC_STARTER_QUESTIONS", + defaultValue: "", + description: "Initial questions to display in the chat", + }, + { + key: "NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR", + defaultValue: "false", + description: + "Whether to show LlamaCloud selector for frontend (need to set LLAMA_CLOUD_API_KEY in .env)", }, ]; -// The default frontend config.js file content -const DEFAULT_FRONTEND_CONFIG = ` -window.LLAMAINDEX = { - // these endpoints are fixed in the ejected nextjs project, don't change them - CHAT_API: '/api/chat', - COMPONENTS_API: '/api/components', - - // update these values to customize frontend - DEV_MODE: true, // whether to enable dev mode - STARTER_QUESTIONS: [], // initial questions to display in the chat - - // uncomment this to enable LlamaCloud (need to set LLAMA_CLOUD_API_KEY in .env) - // LLAMA_CLOUD_API: '/api/chat/config/llamacloud', -} -`.trim(); - async function eject() { try { // validate required directories (nextjs project template, src directory, src/app directory) @@ -119,18 +124,6 @@ async function eject() { } await fs.writeFile(path.join(destDir, ".env"), envFileContent); - // update frontend config.js file - const frontendConfigFile = path.join(destDir, "public", "config.js"); - let frontendConfigContent = DEFAULT_FRONTEND_CONFIG; - if (envFileContent.includes("LLAMA_CLOUD_API_KEY")) { - // if user has LLAMA_CLOUD_API_KEY in .env, auto enable LlamaCloud for frontend - frontendConfigContent = frontendConfigContent.replace( - "// LLAMA_CLOUD_API", - "LLAMA_CLOUD_API", - ); - } - await fs.writeFile(frontendConfigFile, frontendConfigContent); - // rename gitignore -> .gitignore await fs.rename( path.join(destDir, "gitignore"), @@ -143,7 +136,11 @@ async function eject() { force: true, }); - // clean up, remove no-needed files + // clean up & remove no-needed files + await fs.writeFile( + path.join(destDir, "public", "config.js"), + "window.LLAMAINDEX = {};", + ); await fs.unlink(path.join(destDir, "next-build.config.ts")); console.log("Successfully ejected @llamaindex/server to", destDir); diff --git a/packages/server/next/README.md b/packages/server/next/README.md index f14da21ca..e7788bb21 100644 --- a/packages/server/next/README.md +++ b/packages/server/next/README.md @@ -11,7 +11,7 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the ## Configuration -The same as [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server#configuration-options), you can customize the application via .env and public/config.js file. +The same as [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server#configuration-options), you can customize the application via .env file. Here's the examples of how to migrate from LlamaIndexServer configs to Next.js project: @@ -32,19 +32,14 @@ new LlamaIndexServer({ .env file: -```bash -SUGGEST_NEXT_QUESTIONS=true # Whether to suggest next questions -COMPONENTS_DIR=components # Directory for custom components ``` +SUGGEST_NEXT_QUESTIONS=true # Whether to suggest next questions (`suggestNextQuestions`) +COMPONENTS_DIR=components # Directory for custom components (`componentsDir`) -public/config.js file: - -```js -window.LLAMAINDEX = { - DEV_MODE: true, // whether to enable dev mode - STARTER_QUESTIONS: [], // initial questions to display in the chat - LLAMA_CLOUD_API: "/api/chat/config/llamacloud", // enable LlamaCloud for frontend -}; +NEXT_PUBLIC_DEV_MODE=true # Whether to enable dev mode (`devMode`) +NEXT_PUBLIC_STARTER_QUESTIONS=[] # Initial questions to display in the chat (`starterQuestions`) +NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR=true # Whether to show LlamaCloud selector for frontend (`llamaCloudIndexSelector`) +NEXT_PUBLIC_USE_COMPONENTS_DIR=true # Whether to use components directory for frontend ``` For customizing layout, you can directly edit the layout files in the generated nextjs project (app/components/ui/chat/layout). diff --git a/packages/server/next/app/components/ui/chat/chat-input.tsx b/packages/server/next/app/components/ui/chat/chat-input.tsx index 9a7fb7a95..7d8d8665f 100644 --- a/packages/server/next/app/components/ui/chat/chat-input.tsx +++ b/packages/server/next/app/components/ui/chat/chat-input.tsx @@ -8,7 +8,11 @@ import { LlamaCloudSelector } from "./custom/llama-cloud-selector"; export default function CustomChatInput() { const { requestData, isLoading, input } = useChatUI(); const uploadAPI = getConfig("UPLOAD_API") ?? ""; - const llamaCloudAPI = getConfig("LLAMA_CLOUD_API") ?? ""; + const llamaCloudAPI = + getConfig("LLAMA_CLOUD_API") ?? + (process.env.NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR === "true" + ? "/api/chat/config/llamacloud" + : ""); const { imageUrl, setImageUrl, diff --git a/packages/server/next/app/components/ui/chat/chat-section.tsx b/packages/server/next/app/components/ui/chat/chat-section.tsx index 6a5f27923..73c8c8c4f 100644 --- a/packages/server/next/app/components/ui/chat/chat-section.tsx +++ b/packages/server/next/app/components/ui/chat/chat-section.tsx @@ -17,7 +17,7 @@ import { ChatLayout } from "./layout"; export default function ChatSection() { const handler = useChat({ - api: getConfig("CHAT_API"), + api: getConfig("CHAT_API") || "/api/chat", onError: (error: unknown) => { if (!(error instanceof Error)) throw error; let errorMessage: string; diff --git a/packages/server/next/app/components/ui/chat/chat-starter.tsx b/packages/server/next/app/components/ui/chat/chat-starter.tsx index e3b072b7c..ff2f9ba6e 100644 --- a/packages/server/next/app/components/ui/chat/chat-starter.tsx +++ b/packages/server/next/app/components/ui/chat/chat-starter.tsx @@ -6,7 +6,9 @@ import { getConfig } from "../lib/utils"; export function ChatStarter({ className }: { className?: string }) { const { append, messages, requestData } = useChatUI(); - const starterQuestions = getConfig("STARTER_QUESTIONS") ?? []; + const starterQuestions = + getConfig("STARTER_QUESTIONS") ?? + (process.env.NEXT_PUBLIC_STARTER_QUESTIONS || []); if (starterQuestions.length === 0 || messages.length > 0) return null; return ( diff --git a/packages/server/next/app/components/ui/chat/custom/events/loader.ts b/packages/server/next/app/components/ui/chat/custom/events/loader.ts index 54b6450b6..4c944b91e 100644 --- a/packages/server/next/app/components/ui/chat/custom/events/loader.ts +++ b/packages/server/next/app/components/ui/chat/custom/events/loader.ts @@ -17,7 +17,11 @@ export async function fetchComponentDefinitions(): Promise<{ components: ComponentDef[]; errors: string[]; }> { - const endpoint = getConfig("COMPONENTS_API"); + const endpoint = + getConfig("COMPONENTS_API") ?? + (process.env.NEXT_PUBLIC_USE_COMPONENTS_DIR === "true" + ? "/api/components" + : undefined); if (!endpoint) { console.warn("/api/components endpoint is not defined in config"); return { components: [], errors: [] }; diff --git a/packages/server/next/app/components/ui/chat/custom/llama-cloud-selector.tsx b/packages/server/next/app/components/ui/chat/custom/llama-cloud-selector.tsx index f7db88464..6928ea797 100644 --- a/packages/server/next/app/components/ui/chat/custom/llama-cloud-selector.tsx +++ b/packages/server/next/app/components/ui/chat/custom/llama-cloud-selector.tsx @@ -65,8 +65,14 @@ export function LlamaCloudSelector({ ); useEffect(() => { - if (!config && getConfig("LLAMA_CLOUD_API")) { - fetch(getConfig("LLAMA_CLOUD_API")) + const llamaCloudAPI = + getConfig("LLAMA_CLOUD_API") ?? + (process.env.NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR === "true" + ? "/api/chat/config/llamacloud" + : ""); + + if (!config && llamaCloudAPI) { + fetch(llamaCloudAPI) .then((response) => { if (!response.ok) { return response.json().then((errorData) => { diff --git a/packages/server/next/app/components/ui/chat/dev-mode-panel.tsx b/packages/server/next/app/components/ui/chat/dev-mode-panel.tsx index 38658729c..1ab110ce4 100644 --- a/packages/server/next/app/components/ui/chat/dev-mode-panel.tsx +++ b/packages/server/next/app/components/ui/chat/dev-mode-panel.tsx @@ -19,7 +19,8 @@ type WorkflowFile = { }; export function DevModePanel() { - const devModeEnabled = getConfig("DEV_MODE"); + const devModeEnabled = + getConfig("DEV_MODE") ?? process.env.NEXT_PUBLIC_DEV_MODE === "true"; if (!devModeEnabled) return null; return ; } From 1811957d37e08e3993fd7eb5ef12b0b958a77cc9 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 18:01:48 +0700 Subject: [PATCH 51/63] fix: update workflow file path for ejected project --- packages/server/bin/eject.cjs | 5 +++++ packages/server/next/app/api/dev/files/workflow/route.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index f4b5d1819..e8fe78b52 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -30,6 +30,11 @@ const SERVER_CONFIG_VARS = [ defaultValue: "components", description: "Directory for fetching custom components", }, + { + key: "WORKFLOW_FILE_PATH", + defaultValue: "app/api/chat/app/workflow.ts", + description: "The path to the workflow file (will be updated in dev mode)", + }, { key: "NEXT_PUBLIC_USE_COMPONENTS_DIR", defaultValue: "true", diff --git a/packages/server/next/app/api/dev/files/workflow/route.ts b/packages/server/next/app/api/dev/files/workflow/route.ts index e7b471443..cdef75786 100644 --- a/packages/server/next/app/api/dev/files/workflow/route.ts +++ b/packages/server/next/app/api/dev/files/workflow/route.ts @@ -4,10 +4,10 @@ import { NextRequest, NextResponse } from "next/server"; import path from "path"; import { promisify } from "util"; -const DEFAULT_WORKFLOW_FILE_PATH = "src/app/workflow.ts"; // TODO: we can make it as a parameter in server later +const DEFAULT_WORKFLOW_FILE_PATH = "src/app/workflow.ts"; export async function GET(request: NextRequest) { - const filePath = DEFAULT_WORKFLOW_FILE_PATH; + const filePath = process.env.WORKFLOW_FILE_PATH || DEFAULT_WORKFLOW_FILE_PATH; const fileExists = await promisify(fs.exists)(DEFAULT_WORKFLOW_FILE_PATH); if (!fileExists) { From b011630babf434a46d533e660508b7df8d91de02 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 18:05:34 +0700 Subject: [PATCH 52/63] fix: path --- packages/server/next/app/api/dev/files/workflow/route.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/server/next/app/api/dev/files/workflow/route.ts b/packages/server/next/app/api/dev/files/workflow/route.ts index cdef75786..6f6eb9173 100644 --- a/packages/server/next/app/api/dev/files/workflow/route.ts +++ b/packages/server/next/app/api/dev/files/workflow/route.ts @@ -4,10 +4,11 @@ import { NextRequest, NextResponse } from "next/server"; import path from "path"; import { promisify } from "util"; -const DEFAULT_WORKFLOW_FILE_PATH = "src/app/workflow.ts"; +const DEFAULT_WORKFLOW_FILE_PATH = + process.env.WORKFLOW_FILE_PATH || "src/app/workflow.ts"; export async function GET(request: NextRequest) { - const filePath = process.env.WORKFLOW_FILE_PATH || DEFAULT_WORKFLOW_FILE_PATH; + const filePath = DEFAULT_WORKFLOW_FILE_PATH; const fileExists = await promisify(fs.exists)(DEFAULT_WORKFLOW_FILE_PATH); if (!fileExists) { From 91d1cb9352138a3bf5bd2dd50f4d8ce337eb7084 Mon Sep 17 00:00:00 2001 From: thucpn Date: Tue, 27 May 2025 18:16:48 +0700 Subject: [PATCH 53/63] fix: eject dest path --- packages/server/README.md | 4 ++-- packages/server/bin/eject.cjs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/server/README.md b/packages/server/README.md index 929ce7e39..8b91a2c57 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -303,10 +303,10 @@ The server always provides a chat interface at the root path (`/`) with: ### Eject Mode If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server. -By default, the ejected project will be in the `next` directory in the current working directory. You can change the output directory by providing the `--output` flag: +By default, the ejected project will be in the `next` directory in the current working directory. You can change the output directory by providing custom path after `eject` command: ```bash -npm eject --output +npm eject ``` How eject works: diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index e8fe78b52..56bf83507 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -73,13 +73,11 @@ async function eject() { } } - // Get destination directory from command line arguments (pnpm eject --output ) - const args = process.argv; - const outputIndex = args.indexOf("--output"); - const destDir = - outputIndex !== -1 && args[outputIndex + 1] - ? path.resolve(args[outputIndex + 1]) // Use provided path after --output - : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory + // Get destination directory from command line arguments (pnpm eject ) + const customPath = process.argv[process.argv.length - 1]; + const destDir = customPath + ? path.resolve(customPath) // Use provided path + : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory // remove destination directory if it exists await fs.rm(destDir, { recursive: true, force: true }); From 3f553ef29fac3d52ab26768abe35687f1f2334b1 Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 09:12:59 +0700 Subject: [PATCH 54/63] use single entry point for utils --- packages/server/next/app/api/chat/route.ts | 8 +++++--- packages/server/src/utils/index.ts | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 packages/server/src/utils/index.ts diff --git a/packages/server/next/app/api/chat/route.ts b/packages/server/next/app/api/chat/route.ts index 18f13c8f6..2eee69e86 100644 --- a/packages/server/next/app/api/chat/route.ts +++ b/packages/server/next/app/api/chat/route.ts @@ -4,9 +4,11 @@ import { type MessageType } from "llamaindex"; import { NextRequest, NextResponse } from "next/server"; // import chat utils -import { toDataStream } from "./utils/stream"; -import { sendSuggestedQuestionsEvent } from "./utils/suggestion"; -import { runWorkflow } from "./utils/workflow"; +import { + runWorkflow, + sendSuggestedQuestionsEvent, + toDataStream, +} from "./utils"; // import workflow factory and settings from local file import { initSettings } from "./app/settings"; diff --git a/packages/server/src/utils/index.ts b/packages/server/src/utils/index.ts new file mode 100644 index 000000000..8b5184b00 --- /dev/null +++ b/packages/server/src/utils/index.ts @@ -0,0 +1,8 @@ +export * from "./events"; +export * from "./file"; +export * from "./gen-ui"; +export * from "./prompts"; +export * from "./request"; +export * from "./stream"; +export * from "./suggestion"; +export * from "./workflow"; From bd167c160cfc5fc24e0cc72c953d78b687ee501d Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 09:33:09 +0700 Subject: [PATCH 55/63] update imports for generate.ts --- packages/server/bin/eject.cjs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 56bf83507..0f77dbcd2 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -95,14 +95,18 @@ async function eject() { // nextjs project doesn't depend on @llamaindex/server anymore, we need to update the imports in workflow file const workflowFile = path.join(chatRouteDir, "app", "workflow.ts"); let workflowContent = await fs.readFile(workflowFile, "utf-8"); - workflowContent = workflowContent.replace( - "@llamaindex/server", - "../utils/events", - ); + workflowContent = workflowContent.replace("@llamaindex/server", "../utils"); await fs.writeFile(workflowFile, workflowContent); // copy generate.ts if it exists - await copy(generateFile, path.join(chatRouteDir, "generate.ts")); + const genFilePath = path.join(chatRouteDir, "generate.ts"); + const genFileExists = await copy(generateFile, genFilePath); + if (genFileExists) { + // update the import @llamaindex/server in generate.ts + let genContent = await fs.readFile(genFilePath, "utf-8"); + genContent = genContent.replace("@llamaindex/server", "../utils"); + await fs.writeFile(genFilePath, genContent); + } // copy folders in root directory if exists const rootFolders = ["components", "data", "output", "storage"]; From adbd6710979be9025a21aca796bea0ffa4ee8743 Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 09:53:06 +0700 Subject: [PATCH 56/63] use argument after eject --- packages/server/bin/eject.cjs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 0f77dbcd2..e4c8aaf3e 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -74,10 +74,12 @@ async function eject() { } // Get destination directory from command line arguments (pnpm eject ) - const customPath = process.argv[process.argv.length - 1]; - const destDir = customPath - ? path.resolve(customPath) // Use provided path - : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory + const args = process.argv; + const outputIndex = args.indexOf("eject"); + const destDir = + outputIndex !== -1 && args[outputIndex + 1] + ? path.resolve(args[outputIndex + 1]) // Use provided path after eject + : path.join(process.cwd(), "next"); // Default to "next" folder in the current working directory // remove destination directory if it exists await fs.rm(destDir, { recursive: true, force: true }); From 97bdf67321a85cddc65fa3c5fc7ad388c7cf8b0f Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 10:03:07 +0700 Subject: [PATCH 57/63] fix path --- packages/server/bin/eject.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index e4c8aaf3e..44dd88a9e 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -106,7 +106,7 @@ async function eject() { if (genFileExists) { // update the import @llamaindex/server in generate.ts let genContent = await fs.readFile(genFilePath, "utf-8"); - genContent = genContent.replace("@llamaindex/server", "../utils"); + genContent = genContent.replace("@llamaindex/server", "./utils"); await fs.writeFile(genFilePath, genContent); } From 76cad7e66b33dfea18b8a57a776b7c2b5bebdfcc Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 10:24:53 +0700 Subject: [PATCH 58/63] fix: starter questions --- packages/server/bin/eject.cjs | 2 +- packages/server/next/app/components/ui/chat/chat-starter.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 44dd88a9e..48df874c6 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -47,7 +47,7 @@ const SERVER_CONFIG_VARS = [ }, { key: "NEXT_PUBLIC_STARTER_QUESTIONS", - defaultValue: "", + defaultValue: '["Summarize the document", "What are the key points?"]', description: "Initial questions to display in the chat", }, { diff --git a/packages/server/next/app/components/ui/chat/chat-starter.tsx b/packages/server/next/app/components/ui/chat/chat-starter.tsx index ff2f9ba6e..149d94253 100644 --- a/packages/server/next/app/components/ui/chat/chat-starter.tsx +++ b/packages/server/next/app/components/ui/chat/chat-starter.tsx @@ -8,7 +8,7 @@ export function ChatStarter({ className }: { className?: string }) { const { append, messages, requestData } = useChatUI(); const starterQuestions = getConfig("STARTER_QUESTIONS") ?? - (process.env.NEXT_PUBLIC_STARTER_QUESTIONS || []); + JSON.parse(process.env.NEXT_PUBLIC_STARTER_QUESTIONS || "[]"); if (starterQuestions.length === 0 || messages.length > 0) return null; return ( From 9b81778074c3b8a336e9efa37cd08d780c5d7acf Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 11:11:28 +0700 Subject: [PATCH 59/63] remove config.js instead of making it empty --- packages/server/bin/eject.cjs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 48df874c6..4b7efa156 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -145,11 +145,8 @@ async function eject() { force: true, }); - // clean up & remove no-needed files - await fs.writeFile( - path.join(destDir, "public", "config.js"), - "window.LLAMAINDEX = {};", - ); + // remove no-needed files + await fs.unlink(path.join(destDir, "public", "config.js")); await fs.unlink(path.join(destDir, "next-build.config.ts")); console.log("Successfully ejected @llamaindex/server to", destDir); From ebea49530aa47b7de819afe828dd57471cd9ff6a Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 12:28:23 +0700 Subject: [PATCH 60/63] update document --- packages/server/bin/eject.cjs | 11 +++---- packages/server/next/README.md | 53 ++++++++++++---------------------- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/packages/server/bin/eject.cjs b/packages/server/bin/eject.cjs index 4b7efa156..126860913 100644 --- a/packages/server/bin/eject.cjs +++ b/packages/server/bin/eject.cjs @@ -23,12 +23,12 @@ const SERVER_CONFIG_VARS = [ { key: "SUGGEST_NEXT_QUESTIONS", defaultValue: "true", - description: "Whether to suggest next questions", + description: "Whether to suggest next questions (`suggestNextQuestions`)", }, { key: "COMPONENTS_DIR", defaultValue: "components", - description: "Directory for fetching custom components", + description: "Directory for custom components (`componentsDir`)", }, { key: "WORKFLOW_FILE_PATH", @@ -43,18 +43,19 @@ const SERVER_CONFIG_VARS = [ { key: "NEXT_PUBLIC_DEV_MODE", defaultValue: "true", - description: "Whether to enable dev mode for frontend", + description: "Whether to enable dev mode (`devMode`)", }, { key: "NEXT_PUBLIC_STARTER_QUESTIONS", defaultValue: '["Summarize the document", "What are the key points?"]', - description: "Initial questions to display in the chat", + description: + "Initial questions to display in the chat (`starterQuestions`)", }, { key: "NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR", defaultValue: "false", description: - "Whether to show LlamaCloud selector for frontend (need to set LLAMA_CLOUD_API_KEY in .env)", + "Whether to show LlamaCloud selector for frontend (`llamaCloudIndexSelector`)", }, ]; diff --git a/packages/server/next/README.md b/packages/server/next/README.md index e7788bb21..e57488eb7 100644 --- a/packages/server/next/README.md +++ b/packages/server/next/README.md @@ -2,6 +2,8 @@ This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https ## Quick Start +As this is a Next.js project, you can use the following commands to start the development server: + ```bash npm install npm run dev @@ -9,48 +11,29 @@ npm run dev Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -## Configuration +## Useful Commands -The same as [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server#configuration-options), you can customize the application via .env file. - -Here's the examples of how to migrate from LlamaIndexServer configs to Next.js project: - -```ts -// src/index.ts -new LlamaIndexServer({ - workflow: workflowFactory, - suggestNextQuestions: true, - uiConfig: { - devMode: true, - llamaCloudIndexSelector: true, - starterQuestions: ["Summarize the document", "What are the key points?"], - componentsDir: "components", - layoutDir: "layout", - }, -}).start(); -``` +- Generate Datasource (in case you're having a `./data` folder): `npm run generate` +- Typecheck: `npm run typecheck` +- Lint: `npm run lint` +- Format: `npm run format` +- Build & Start: `npm run build && npm run start` -.env file: +## Deployment -``` -SUGGEST_NEXT_QUESTIONS=true # Whether to suggest next questions (`suggestNextQuestions`) -COMPONENTS_DIR=components # Directory for custom components (`componentsDir`) +The project can be deployed to any platform that supports Next.js like Vercel. -NEXT_PUBLIC_DEV_MODE=true # Whether to enable dev mode (`devMode`) -NEXT_PUBLIC_STARTER_QUESTIONS=[] # Initial questions to display in the chat (`starterQuestions`) -NEXT_PUBLIC_SHOW_LLAMACLOUD_SELECTOR=true # Whether to show LlamaCloud selector for frontend (`llamaCloudIndexSelector`) -NEXT_PUBLIC_USE_COMPONENTS_DIR=true # Whether to use components directory for frontend -``` +## Configuration -For customizing layout, you can directly edit the layout files in the generated nextjs project (app/components/ui/chat/layout). +Your original [`llamaindex-server`](https://github.com/run-llama/create-llama/tree/main/packages/server#configuration-options) configurations have been migrated to a [`.env`](.env) file. -## Useful Commands +Changing the `.env` file will change the behavior of the application, e.g. for changing the initial questions to display in the chat, you can do: -- Generate Datasource (in case having `./data` folder): `npm run generate` -- Typecheck: `npm run typecheck` -- Lint: `npm run lint` -- Format: `npm run format` -- Build & Start: `npm run build && npm run start` +``` +NEXT_PUBLIC_STARTER_QUESTIONS=['What is the capital of France?'] +``` + +Alternatively, you can also change the file referencing `process.env.NEXT_PUBLIC_STARTER_QUESTIONS` directly in the source code. ## Learn More From 65937c55366b684910b3f71e8abb535e30237df6 Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 15:50:14 +0700 Subject: [PATCH 61/63] add eject to e2e --- .../shared/llamaindexserver_template.spec.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts b/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts index f85db166c..ab6ae85c4 100644 --- a/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts +++ b/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from "@playwright/test"; -import { ChildProcess } from "child_process"; +import { ChildProcess, execSync } from "child_process"; import fs from "fs"; import path from "path"; import type { @@ -28,6 +28,7 @@ const templateUseCases = [ "deep_research", "code_generator", ]; +const ejectDir = "next"; for (const useCase of templateUseCases) { test.describe(`Test use case ${useCase} ${templateFramework} ${dataSource} ${templateUI} ${appType} ${templatePostInstallAction}`, async () => { @@ -110,6 +111,26 @@ for (const useCase of templateUseCases) { expect(response.ok()).toBeTruthy(); }); + test("Should successfully eject, install dependencies and build without errors", async () => { + test.skip( + templateFramework !== "nextjs" || useCase !== "code_generator", + "Eject test only applies to Next.js framework", + ); + + // Run eject command + execSync("npm run eject", { cwd: path.join(cwd, name) }); + + // Verify next directory exists + const nextDirExists = fs.existsSync(path.join(cwd, name, ejectDir)); + expect(nextDirExists).toBeTruthy(); + + // Install dependencies in next directory + execSync("npm install", { cwd: path.join(cwd, name, ejectDir) }); + + // Run build + execSync("npm run build", { cwd: path.join(cwd, name, ejectDir) }); + }); + // clean processes test.afterAll(async () => { appProcess?.kill(); From aba3879f0da704def6a3fe43c063db8b838cceb8 Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 16:14:47 +0700 Subject: [PATCH 62/63] import { OpenAI } from "@llamaindex/openai"; --- .../llamaindexserver/llamacloud/typescript/generate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/create-llama/templates/components/vectordbs/llamaindexserver/llamacloud/typescript/generate.ts b/packages/create-llama/templates/components/vectordbs/llamaindexserver/llamacloud/typescript/generate.ts index 63623e69d..5c07d42b1 100644 --- a/packages/create-llama/templates/components/vectordbs/llamaindexserver/llamacloud/typescript/generate.ts +++ b/packages/create-llama/templates/components/vectordbs/llamaindexserver/llamacloud/typescript/generate.ts @@ -1,8 +1,9 @@ +import { OpenAI } from "@llamaindex/openai"; import { generateEventComponent } from "@llamaindex/server"; import * as dotenv from "dotenv"; import "dotenv/config"; import * as fs from "fs/promises"; -import { LLamaCloudFileService, OpenAI } from "llamaindex"; +import { LLamaCloudFileService } from "llamaindex"; import * as path from "path"; import { getIndex } from "./app/data"; import { initSettings } from "./app/settings"; From 763bc23e5ac822e92f415fe3686d3138994bffff Mon Sep 17 00:00:00 2001 From: thucpn Date: Wed, 28 May 2025 16:46:08 +0700 Subject: [PATCH 63/63] disable eject test for llamacloud --- .../e2e/shared/llamaindexserver_template.spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts b/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts index ab6ae85c4..c99d5a3ba 100644 --- a/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts +++ b/packages/create-llama/e2e/shared/llamaindexserver_template.spec.ts @@ -113,8 +113,10 @@ for (const useCase of templateUseCases) { test("Should successfully eject, install dependencies and build without errors", async () => { test.skip( - templateFramework !== "nextjs" || useCase !== "code_generator", - "Eject test only applies to Next.js framework", + templateFramework !== "nextjs" || + useCase !== "code_generator" || + dataSource === "--llamacloud", + "Eject test only applies to Next.js framework, code generator use case, and non-llamacloud", ); // Run eject command