mirror of
https://github.com/run-llama/create-llama.git
synced 2026-07-02 19:14:28 -04:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bbae802bed | |||
| 25fba4381b | |||
| d0618fa2fa | |||
| f3fe3ffc9b | |||
| 6f75d4ab6e | |||
| 3242738fe4 | |||
| 17538eb0dd | |||
| d3772cb4a2 | |||
| 527075c086 | |||
| fb7d4da149 | |||
| 5c35b194bb | |||
| 85e5e7e662 | |||
| 58362542c0 | |||
| 6f44185f68 | |||
| afe9e9fc16 | |||
| 1b5a519f13 | |||
| f072308d03 | |||
| 1df8cfbdc2 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-llama": patch
|
||||
---
|
||||
|
||||
Fix typing check issue
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-llama": patch
|
||||
---
|
||||
|
||||
fix chromadb dependency issue
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-llama": patch
|
||||
---
|
||||
|
||||
fix: remove dead generated ai code
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"create-llama": patch
|
||||
---
|
||||
|
||||
Deprecate pro mode
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-python-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}
|
||||
name: playwright-report-python-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}-${{ matrix.template-types }}
|
||||
path: packages/create-llama/playwright-report/
|
||||
overwrite: true
|
||||
retention-days: 30
|
||||
@@ -136,6 +136,21 @@ jobs:
|
||||
run: pnpm run pack-install
|
||||
working-directory: packages/create-llama
|
||||
|
||||
- name: Build server
|
||||
run: pnpm run build
|
||||
working-directory: packages/server
|
||||
|
||||
- name: Pack @llamaindex/server package
|
||||
run: |
|
||||
pnpm pack --pack-destination "${{ runner.temp }}"
|
||||
if [ "${{ runner.os }}" == "Windows" ]; then
|
||||
file=$(find "${{ runner.temp }}" -name "llamaindex-server-*.tgz" | head -n 1)
|
||||
mv "$file" "${{ runner.temp }}/llamaindex-server.tgz"
|
||||
else
|
||||
mv ${{ runner.temp }}/llamaindex-server-*.tgz ${{ runner.temp }}/llamaindex-server.tgz
|
||||
fi
|
||||
working-directory: packages/server
|
||||
|
||||
- name: Run Playwright tests for TypeScript
|
||||
run: pnpm run e2e:typescript
|
||||
env:
|
||||
@@ -144,12 +159,13 @@ jobs:
|
||||
FRAMEWORK: ${{ matrix.frameworks }}
|
||||
DATASOURCE: ${{ matrix.datasources }}
|
||||
TEMPLATE_TYPE: ${{ matrix.template-types }}
|
||||
SERVER_PACKAGE_PATH: ${{ runner.temp }}/llamaindex-server.tgz
|
||||
working-directory: packages/create-llama
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-typescript-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}-node${{ matrix.node-version }}
|
||||
name: playwright-report-typescript-${{ matrix.os }}-${{ matrix.frameworks }}-${{ matrix.datasources }}-node${{ matrix.node-version }}-${{ matrix.template-types }}
|
||||
path: packages/create-llama/playwright-report/
|
||||
overwrite: true
|
||||
retention-days: 30
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# create-llama
|
||||
|
||||
## 0.5.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 6f75d4a: fix: unsupported language in code gen workflow
|
||||
- d0618fa: Fix LlamaCloud generate script issue
|
||||
|
||||
## 0.5.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 527075c: Enable dev mode that allows updating code directly in the UI
|
||||
|
||||
## 0.5.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1df8cfb: Split artifacts use case to document generator and code generator
|
||||
- 1b5a519: chore: improve dev experience with nodemon
|
||||
- b3eb0ba: Fix typing check issue
|
||||
- 556f33c: fix chromadb dependency issue
|
||||
- 2451539: fix: remove dead generated ai code
|
||||
- 7a70390: Deprecate pro mode
|
||||
|
||||
## 0.5.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -18,7 +18,8 @@ const useCases: TemplateUseCase[] = [
|
||||
"agentic_rag",
|
||||
"deep_research",
|
||||
"financial_report",
|
||||
"artifacts",
|
||||
"code_generator",
|
||||
"document_generator",
|
||||
];
|
||||
const dataSource: string = process.env.DATASOURCE
|
||||
? process.env.DATASOURCE
|
||||
|
||||
@@ -12,21 +12,30 @@ import { createTestDir, runCreateLlama, type AppType } from "../utils";
|
||||
const templateFramework: TemplateFramework = process.env.FRAMEWORK
|
||||
? (process.env.FRAMEWORK as TemplateFramework)
|
||||
: "fastapi";
|
||||
const dataSource: string = "--example-file";
|
||||
const dataSource: string = process.env.DATASOURCE
|
||||
? (process.env.DATASOURCE as string)
|
||||
: "--example-file";
|
||||
const llamaCloudProjectName = "create-llama";
|
||||
const llamaCloudIndexName = "e2e-test";
|
||||
|
||||
const templateUI: TemplateUI = "shadcn";
|
||||
const templatePostInstallAction: TemplatePostInstallAction = "runApp";
|
||||
const appType: AppType = "--frontend";
|
||||
const userMessage = "Write a blog post about physical standards for letters";
|
||||
const templateUseCases = ["financial_report", "agentic_rag", "deep_research"];
|
||||
const templateUseCases = [
|
||||
"agentic_rag",
|
||||
"financial_report",
|
||||
"deep_research",
|
||||
"code_generator",
|
||||
];
|
||||
|
||||
for (const useCase of templateUseCases) {
|
||||
test.describe(`Test use case ${useCase} ${templateFramework} ${dataSource} ${templateUI} ${appType} ${templatePostInstallAction}`, async () => {
|
||||
test.skip(
|
||||
process.platform !== "linux" ||
|
||||
process.env.DATASOURCE === "--no-files" ||
|
||||
templateFramework === "express",
|
||||
dataSource === "--no-files" || templateFramework === "express",
|
||||
"The llamaindexserver template currently only works with nextjs, fastapi. We also only run on Linux to speed up tests.",
|
||||
);
|
||||
const useLlamaParse = dataSource === "--llamacloud";
|
||||
let port: number;
|
||||
let cwd: string;
|
||||
let name: string;
|
||||
@@ -48,6 +57,9 @@ for (const useCase of templateUseCases) {
|
||||
templateUI,
|
||||
appType,
|
||||
useCase,
|
||||
llamaCloudProjectName,
|
||||
llamaCloudIndexName,
|
||||
useLlamaParse,
|
||||
});
|
||||
name = result.projectName;
|
||||
appProcess = result.appProcess;
|
||||
|
||||
@@ -23,7 +23,8 @@ const useCases: TemplateUseCase[] = [
|
||||
"agentic_rag",
|
||||
"deep_research",
|
||||
"financial_report",
|
||||
"artifacts",
|
||||
"code_generator",
|
||||
"document_generator",
|
||||
];
|
||||
const dataSource: string = process.env.DATASOURCE
|
||||
? process.env.DATASOURCE
|
||||
@@ -82,7 +83,7 @@ test.describe("Test resolve TS dependencies", () => {
|
||||
});
|
||||
});
|
||||
// Skipping llamacloud for the use case doesn't use index.
|
||||
if (useCase !== "artifacts") {
|
||||
if (useCase !== "code_generator" && useCase !== "document_generator") {
|
||||
test(`llamaParse - ${optionDescription}`, async () => {
|
||||
await runTest({
|
||||
templateType: templateType,
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
ModelConfig,
|
||||
TemplateDataSource,
|
||||
TemplateFramework,
|
||||
TemplateUseCase,
|
||||
TemplateVectorDB,
|
||||
} from "./types";
|
||||
import { installTSTemplate } from "./typescript";
|
||||
@@ -60,6 +61,7 @@ async function generateContextData(
|
||||
vectorDb?: TemplateVectorDB,
|
||||
llamaCloudKey?: string,
|
||||
useLlamaParse?: boolean,
|
||||
useCase?: TemplateUseCase,
|
||||
) {
|
||||
if (packageManager) {
|
||||
const runGenerate = `${cyan(
|
||||
@@ -96,7 +98,12 @@ async function generateContextData(
|
||||
}
|
||||
} else {
|
||||
console.log(`Running ${runGenerate} to generate the context data.`);
|
||||
await callPackageManager(packageManager, true, ["run", "generate"]);
|
||||
const shouldRunGenerate =
|
||||
useCase !== "code_generator" && useCase !== "document_generator"; // Artifact use case doesn't use index.
|
||||
|
||||
if (shouldRunGenerate) {
|
||||
await callPackageManager(packageManager, true, ["run", "generate"]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -224,6 +231,7 @@ export const installTemplate = async (
|
||||
props.vectorDb,
|
||||
props.llamaCloudKey,
|
||||
props.useLlamaParse,
|
||||
props.useCase,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ const getAdditionalDependencies = (
|
||||
if (observability === "traceloop") {
|
||||
dependencies.push({
|
||||
name: "traceloop-sdk",
|
||||
version: ">=0.15.11,<0.16.0",
|
||||
version: ">=0.15.11",
|
||||
});
|
||||
}
|
||||
if (observability === "llamatrace") {
|
||||
@@ -677,6 +677,7 @@ export const installPythonTemplate = async ({
|
||||
dataSources,
|
||||
tools,
|
||||
template,
|
||||
observability,
|
||||
);
|
||||
|
||||
await addDependencies(root, addOnDependencies);
|
||||
|
||||
@@ -58,7 +58,8 @@ export type TemplateUseCase =
|
||||
| "extractor"
|
||||
| "contract_review"
|
||||
| "agentic_rag"
|
||||
| "artifacts";
|
||||
| "code_generator"
|
||||
| "document_generator";
|
||||
// Config for both file and folder
|
||||
export type FileSourceConfig =
|
||||
| {
|
||||
|
||||
@@ -77,7 +77,7 @@ const installLlamaIndexServerTemplate = async ({
|
||||
}
|
||||
|
||||
// Simplify use case code
|
||||
if (useCase === "artifacts") {
|
||||
if (useCase === "code_generator" || useCase === "document_generator") {
|
||||
// Artifact use case doesn't use index.
|
||||
// We don't need data.ts, generate.ts
|
||||
await fs.rm(path.join(root, "src", "app", "data.ts"));
|
||||
@@ -387,7 +387,7 @@ const providerDependencies: {
|
||||
[key in ModelProvider]?: Record<string, string>;
|
||||
} = {
|
||||
openai: {
|
||||
"@llamaindex/openai": "^0.2.0",
|
||||
"@llamaindex/openai": "~0.4.0",
|
||||
},
|
||||
gemini: {
|
||||
"@llamaindex/google": "^0.2.0",
|
||||
@@ -513,7 +513,7 @@ async function updatePackageJson({
|
||||
if (backend) {
|
||||
packageJson.dependencies = {
|
||||
...packageJson.dependencies,
|
||||
"@llamaindex/readers": "^3.0.0",
|
||||
"@llamaindex/readers": "~3.1.4",
|
||||
};
|
||||
|
||||
if (vectorDb && vectorDb in vectorDbDependencies) {
|
||||
@@ -543,6 +543,16 @@ async function updatePackageJson({
|
||||
};
|
||||
}
|
||||
|
||||
// if having custom server package tgz file, use it for testing @llamaindex/server
|
||||
const serverPackagePath = process.env.SERVER_PACKAGE_PATH;
|
||||
if (serverPackagePath && template === "llamaindexserver") {
|
||||
const relativePath = path.relative(process.cwd(), serverPackagePath);
|
||||
packageJson.dependencies = {
|
||||
...packageJson.dependencies,
|
||||
"@llamaindex/server": `file:${relativePath}`,
|
||||
};
|
||||
}
|
||||
|
||||
await fs.writeFile(
|
||||
packageJsonFile,
|
||||
JSON.stringify(packageJson, null, 2) + os.EOL,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-llama",
|
||||
"version": "0.5.13",
|
||||
"version": "0.5.16",
|
||||
"description": "Create LlamaIndex-powered apps with one command",
|
||||
"keywords": [
|
||||
"rag",
|
||||
|
||||
@@ -6,7 +6,7 @@ const defaults: Omit<QuestionArgs, "modelConfig"> = {
|
||||
framework: "nextjs",
|
||||
ui: "shadcn",
|
||||
frontend: false,
|
||||
llamaCloudKey: "",
|
||||
llamaCloudKey: undefined,
|
||||
useLlamaParse: false,
|
||||
communityProjectConfig: undefined,
|
||||
llamapack: "",
|
||||
|
||||
@@ -10,7 +10,8 @@ type AppType =
|
||||
| "agentic_rag"
|
||||
| "financial_report"
|
||||
| "deep_research"
|
||||
| "artifacts";
|
||||
| "code_generator"
|
||||
| "document_generator";
|
||||
|
||||
type SimpleAnswers = {
|
||||
appType: AppType;
|
||||
@@ -47,10 +48,14 @@ export const askSimpleQuestions = async (
|
||||
"Researches and analyzes provided documents from multiple perspectives, generating a comprehensive report with citations to support key findings and insights.",
|
||||
},
|
||||
{
|
||||
title: "Artifacts",
|
||||
value: "artifacts",
|
||||
description:
|
||||
"Build your own Vercel's v0 or OpenAI's canvas-styled UI.",
|
||||
title: "Code Generator",
|
||||
value: "code_generator",
|
||||
description: "Build a Vercel v0 styled code generator.",
|
||||
},
|
||||
{
|
||||
title: "Document Generator",
|
||||
value: "document_generator",
|
||||
description: "Build a OpenAI canvas-styled document generator.",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -76,7 +81,7 @@ export const askSimpleQuestions = async (
|
||||
);
|
||||
language = newLanguage;
|
||||
|
||||
if (appType !== "artifacts") {
|
||||
if (appType !== "code_generator" && appType !== "document_generator") {
|
||||
const { useLlamaCloud: newUseLlamaCloud } = await prompts(
|
||||
{
|
||||
type: "toggle",
|
||||
@@ -153,7 +158,13 @@ const convertAnswers = async (
|
||||
tools: [],
|
||||
modelConfig: MODEL_GPT41,
|
||||
},
|
||||
artifacts: {
|
||||
code_generator: {
|
||||
template: "llamaindexserver",
|
||||
dataSources: [],
|
||||
tools: [],
|
||||
modelConfig: MODEL_GPT41,
|
||||
},
|
||||
document_generator: {
|
||||
template: "llamaindexserver",
|
||||
dataSources: [],
|
||||
tools: [],
|
||||
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"llama-index>=0.12.1",
|
||||
"llama-parse>=0.6.21,<0.7.0",
|
||||
"cachetools>=5.3.3",
|
||||
"reflex>=0.6.2.post1",
|
||||
"reflex==0.7.10",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -13,7 +13,7 @@ dependencies = [
|
||||
"llama-index>=0.12.1",
|
||||
"llama-parse>=0.6.21,<0.7.0",
|
||||
"cachetools>=5.3.3",
|
||||
"reflex>=0.6.2.post1",
|
||||
"reflex==0.7.10",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Markdown } from "@llamaindex/chat-ui/widgets";
|
||||
import { ListChecks, Loader2, Wand2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const STAGE_META = {
|
||||
plan: {
|
||||
icon: ListChecks,
|
||||
badgeText: "Step 1/2: Planning",
|
||||
gradient: "from-blue-100 via-blue-50 to-white",
|
||||
progress: 33,
|
||||
iconBg: "bg-blue-100 text-blue-600",
|
||||
badge: "bg-blue-100 text-blue-700",
|
||||
},
|
||||
generate: {
|
||||
icon: Wand2,
|
||||
badgeText: "Step 2/2: Generating",
|
||||
gradient: "from-violet-100 via-violet-50 to-white",
|
||||
progress: 66,
|
||||
iconBg: "bg-violet-100 text-violet-600",
|
||||
badge: "bg-violet-100 text-violet-700",
|
||||
},
|
||||
};
|
||||
|
||||
function ArtifactWorkflowCard({ event }) {
|
||||
const [visible, setVisible] = useState(event?.state !== "completed");
|
||||
const [fade, setFade] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (event?.state === "completed") {
|
||||
setVisible(false);
|
||||
} else {
|
||||
setVisible(true);
|
||||
setFade(false);
|
||||
}
|
||||
}, [event?.state]);
|
||||
|
||||
if (!event || !visible) return null;
|
||||
|
||||
const { state, requirement } = event;
|
||||
const meta = STAGE_META[state];
|
||||
|
||||
if (!meta) return null;
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[180px] w-full items-center justify-center py-2">
|
||||
<Card
|
||||
className={cn(
|
||||
"w-full rounded-xl shadow-md transition-all duration-500",
|
||||
"border-0",
|
||||
fade && "pointer-events-none opacity-0",
|
||||
`bg-gradient-to-br ${meta.gradient}`,
|
||||
)}
|
||||
style={{
|
||||
boxShadow:
|
||||
"0 2px 12px 0 rgba(80, 80, 120, 0.08), 0 1px 3px 0 rgba(80, 80, 120, 0.04)",
|
||||
}}
|
||||
>
|
||||
<CardHeader className="flex flex-row items-center gap-2 px-3 pb-1 pt-2">
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-full p-1",
|
||||
meta.iconBg,
|
||||
)}
|
||||
>
|
||||
<meta.icon className="h-5 w-5" />
|
||||
</div>
|
||||
<CardTitle className="flex items-center gap-2 text-base font-semibold">
|
||||
<Badge className={cn("ml-1", meta.badge, "px-2 py-0.5 text-xs")}>
|
||||
{meta.badgeText}
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="px-3 py-1">
|
||||
{state === "plan" && (
|
||||
<div className="flex flex-col items-center gap-2 py-2">
|
||||
<Loader2 className="mb-1 h-6 w-6 animate-spin text-blue-400" />
|
||||
<div className="text-center text-sm font-medium text-blue-900">
|
||||
Analyzing your request...
|
||||
</div>
|
||||
<Skeleton className="mt-1 h-3 w-1/2 rounded-full" />
|
||||
</div>
|
||||
)}
|
||||
{state === "generate" && (
|
||||
<div className="flex flex-col gap-2 py-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<Loader2 className="h-4 w-4 animate-spin text-violet-400" />
|
||||
<span className="text-sm font-medium text-violet-900">
|
||||
Working on the requirement:
|
||||
</span>
|
||||
</div>
|
||||
<div className="max-h-24 overflow-auto rounded-lg border border-violet-200 bg-violet-50 px-2 py-1 text-xs">
|
||||
{requirement ? (
|
||||
<Markdown content={requirement} />
|
||||
) : (
|
||||
<span className="italic text-violet-400">
|
||||
No requirements available yet.
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
<div className="px-3 pb-2 pt-1">
|
||||
<Progress
|
||||
value={meta.progress}
|
||||
className={cn(
|
||||
"h-1 rounded-full bg-gray-200",
|
||||
state === "plan" && "bg-blue-200",
|
||||
state === "generate" && "bg-violet-200",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Component({ events }) {
|
||||
const aggregateEvents = () => {
|
||||
if (!events || events.length === 0) return null;
|
||||
return events[events.length - 1];
|
||||
};
|
||||
|
||||
const event = aggregateEvents();
|
||||
|
||||
return <ArtifactWorkflowCard event={event} />;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
from app.code_workflow import CodeArtifactWorkflow
|
||||
|
||||
# from app.document_workflow import DocumentArtifactWorkflow to generate documents
|
||||
from llama_index.core.workflow import Workflow
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from llama_index.server.api.models import ChatRequest
|
||||
|
||||
|
||||
def create_workflow(chat_request: ChatRequest) -> Workflow:
|
||||
workflow = CodeArtifactWorkflow(
|
||||
llm=OpenAI(model="gpt-4.1"),
|
||||
chat_request=chat_request,
|
||||
timeout=120.0,
|
||||
)
|
||||
return workflow
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Workflows](https://docs.llamaindex.ai/en/stable/understanding/workflows/).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, setup the environment with uv:
|
||||
|
||||
> **_Note:_** This step is not needed if you are using the dev-container.
|
||||
|
||||
```shell
|
||||
uv sync
|
||||
```
|
||||
|
||||
Then check the parameters that have been pre-configured in the `.env` file in this directory.
|
||||
Make sure you have set the `OPENAI_API_KEY` for the LLM.
|
||||
|
||||
Then, run the development server:
|
||||
|
||||
```shell
|
||||
uv run fastapi dev
|
||||
```
|
||||
|
||||
Then open [http://localhost:8000](http://localhost:8000) with your browser to start the chat UI.
|
||||
|
||||
To start the app optimized for **production**, run:
|
||||
|
||||
```
|
||||
uv run fastapi run
|
||||
```
|
||||
|
||||
## Configure LLM and Embedding Model
|
||||
|
||||
You can configure [LLM model](https://docs.llamaindex.ai/en/stable/module_guides/models/llms) and [embedding model](https://docs.llamaindex.ai/en/stable/module_guides/models/embeddings) in [settings.py](app/settings.py).
|
||||
|
||||
## Use Case
|
||||
AI-powered code generator that can help you generate app with a chat interface, code editor and app preview.
|
||||
|
||||
To update the workflow, you can modify the code in [`workflow.py`](app/workflow.py).
|
||||
|
||||
You can start by sending an request on the [chat UI](http://localhost:8000) or you can test the `/api/chat` endpoint with the following curl request:
|
||||
|
||||
```
|
||||
curl --location 'localhost:8000/api/chat' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{ "messages": [{ "role": "user", "content": "Create a report comparing the finances of Apple and Tesla" }] }'
|
||||
```
|
||||
|
||||
## Customize the UI
|
||||
|
||||
To customize the UI, you can start by modifying the [./components/ui_event.jsx](./components/ui_event.jsx) file.
|
||||
|
||||
You can also generate a new code for the workflow using LLM by running the following command:
|
||||
|
||||
```
|
||||
uv run generate_ui
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about LlamaIndex, take a look at the following resources:
|
||||
|
||||
- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex.
|
||||
- [Workflows Introduction](https://docs.llamaindex.ai/en/stable/understanding/workflows/) - learn about LlamaIndex workflows.
|
||||
- [LlamaIndex Server](https://pypi.org/project/llama-index-server/)
|
||||
|
||||
You can check out [the LlamaIndex GitHub repository](https://github.com/run-llama/llama_index) - your feedback and contributions are welcome!
|
||||
+10
@@ -6,6 +6,7 @@ from llama_index.core.chat_engine.types import ChatMessage
|
||||
from llama_index.core.llms import LLM
|
||||
from llama_index.core.memory import ChatMemoryBuffer
|
||||
from llama_index.core.prompts import PromptTemplate
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from llama_index.core.workflow import (
|
||||
Context,
|
||||
Event,
|
||||
@@ -26,6 +27,15 @@ from llama_index.server.api.utils import get_last_artifact
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def create_workflow(chat_request: ChatRequest) -> Workflow:
|
||||
workflow = CodeArtifactWorkflow(
|
||||
llm=OpenAI(model="gpt-4.1"),
|
||||
chat_request=chat_request,
|
||||
timeout=120.0,
|
||||
)
|
||||
return workflow
|
||||
|
||||
|
||||
class Requirement(BaseModel):
|
||||
next_step: Literal["answering", "coding"]
|
||||
language: Optional[str] = None
|
||||
+2
-5
@@ -33,12 +33,9 @@ You can configure [LLM model](https://docs.llamaindex.ai/en/stable/module_guides
|
||||
|
||||
## Use Case
|
||||
|
||||
We have prepared two artifact workflows:
|
||||
AI-powered document generator that can help you generate documents with a chat interface and simple markdown editor.
|
||||
|
||||
- [Code Workflow](app/code_workflow.py): To generate code and display it in the UI like Vercel's v0.
|
||||
- [Document Workflow](app/document_workflow.py): Generate and update a document like OpenAI's canvas.
|
||||
|
||||
Modify the factory method in [`workflow.py`](app/workflow.py) to decide which artifact workflow to use. Without any changes the Code Workflow is used.
|
||||
To update the workflow, you can modify the code in [`workflow.py`](app/workflow.py).
|
||||
|
||||
You can start by sending an request on the [chat UI](http://localhost:8000) or you can test the `/api/chat` endpoint with the following curl request:
|
||||
|
||||
+10
@@ -4,6 +4,7 @@ from typing import Any, Literal, Optional
|
||||
|
||||
from llama_index.core.chat_engine.types import ChatMessage
|
||||
from llama_index.core.llms import LLM
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from llama_index.core.memory import ChatMemoryBuffer
|
||||
from llama_index.core.prompts import PromptTemplate
|
||||
from llama_index.core.workflow import (
|
||||
@@ -26,6 +27,15 @@ from llama_index.server.api.utils import get_last_artifact
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
def create_workflow(chat_request: ChatRequest) -> Workflow:
|
||||
workflow = DocumentArtifactWorkflow(
|
||||
llm=OpenAI(model="gpt-4.1"),
|
||||
chat_request=chat_request,
|
||||
timeout=120.0,
|
||||
)
|
||||
return workflow
|
||||
|
||||
|
||||
class DocumentRequirement(BaseModel):
|
||||
type: Literal["markdown", "html"]
|
||||
title: str
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import { createCodeArtifactWorkflow, UIEventSchema } from "./code-workflow";
|
||||
// import { createDocumentArtifactWorkflow, UIEventSchema } from "./doc-workflow";
|
||||
|
||||
export const workflowFactory = async (reqBody: any) => {
|
||||
// Uncomment the import and change to createDocumentArtifactWorkflow to use the document workflow
|
||||
const workflow = createCodeArtifactWorkflow(reqBody);
|
||||
|
||||
return workflow;
|
||||
};
|
||||
|
||||
// Re-export the UIEventSchema for generating the UI by `pnpm generate:ui` command
|
||||
export { UIEventSchema };
|
||||
+2
-5
@@ -30,12 +30,9 @@ npm run generate:ui
|
||||
|
||||
## Use Case
|
||||
|
||||
We have prepared two artifact workflows:
|
||||
AI-powered code generator that can help you generate app with a chat interface, code editor and app preview.
|
||||
|
||||
- [Code Workflow](app/code_workflow.ts): To generate code and display it in the UI like Vercel's v0.
|
||||
- [Document Workflow](app/document_workflow.ts): Generate and update a document like OpenAI's canvas.
|
||||
|
||||
Modify the factory method in [`workflow.ts`](app/workflow.ts) to decide which artifact workflow to use. Without any changes the Code Workflow is used.
|
||||
To update the workflow, you can modify the code in [`workflow.ts`](app/workflow.ts).
|
||||
|
||||
You can start by sending an request on the [chat UI](http://localhost:3000) or you can test the `/api/chat` endpoint with the following curl request:
|
||||
|
||||
+15
-25
@@ -1,5 +1,5 @@
|
||||
import { extractLastArtifact } from "@llamaindex/server";
|
||||
import { ChatMemoryBuffer, LLM, Settings } from "llamaindex";
|
||||
import { ChatMemoryBuffer, MessageContent, Settings } from "llamaindex";
|
||||
|
||||
import {
|
||||
agentStreamEvent,
|
||||
@@ -40,7 +40,7 @@ export const UIEventSchema = z.object({
|
||||
|
||||
export type UIEvent = z.infer<typeof UIEventSchema>;
|
||||
const planEvent = workflowEvent<{
|
||||
userInput: string;
|
||||
userInput: MessageContent;
|
||||
context?: string | undefined;
|
||||
}>();
|
||||
|
||||
@@ -65,34 +65,33 @@ const artifactEvent = workflowEvent<{
|
||||
};
|
||||
}>();
|
||||
|
||||
export function createCodeArtifactWorkflow(reqBody: any, llm?: LLM) {
|
||||
if (!llm) {
|
||||
llm = Settings.llm;
|
||||
}
|
||||
export function workflowFactory(reqBody: any) {
|
||||
const llm = Settings.llm;
|
||||
|
||||
const { withState, getContext } = createStatefulMiddleware(() => {
|
||||
return {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm,
|
||||
chatHistory: reqBody.chatHistory,
|
||||
}),
|
||||
memory: new ChatMemoryBuffer({ llm }),
|
||||
lastArtifact: extractLastArtifact(reqBody),
|
||||
};
|
||||
});
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
workflow.handle([startAgentEvent], async ({ data: { userInput } }) => {
|
||||
workflow.handle([startAgentEvent], async ({ data }) => {
|
||||
const { userInput, chatHistory = [] } = data;
|
||||
// Prepare chat history
|
||||
const { state } = getContext();
|
||||
// Put user input to the memory
|
||||
if (!userInput) {
|
||||
throw new Error("Missing user input to start the workflow");
|
||||
}
|
||||
state.memory.put({
|
||||
role: "user",
|
||||
content: userInput,
|
||||
});
|
||||
state.memory.set(chatHistory);
|
||||
state.memory.put({ role: "user", content: userInput });
|
||||
|
||||
return planEvent.with({
|
||||
userInput,
|
||||
userInput: userInput,
|
||||
context: state.lastArtifact
|
||||
? JSON.stringify(state.lastArtifact)
|
||||
: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -177,15 +176,6 @@ ${user_msg}
|
||||
throw new Error("No JSON block found in the response.");
|
||||
}
|
||||
const requirement = RequirementSchema.parse(JSON.parse(jsonBlock[1]));
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "generate",
|
||||
requirement: requirement.requirement,
|
||||
},
|
||||
}),
|
||||
);
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content: `The plan for next step: \n${response.text}`,
|
||||
+6
-4
@@ -10,12 +10,14 @@ import {
|
||||
import {
|
||||
ChatMemoryBuffer,
|
||||
LlamaCloudIndex,
|
||||
MessageContent,
|
||||
Metadata,
|
||||
MetadataMode,
|
||||
NodeWithScore,
|
||||
PromptTemplate,
|
||||
Settings,
|
||||
VectorStoreIndex,
|
||||
extractText,
|
||||
} from "llamaindex";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { z } from "zod";
|
||||
@@ -153,7 +155,7 @@ export function getWorkflow(index: VectorStoreIndex | LlamaCloudIndex) {
|
||||
chatHistory: [],
|
||||
}),
|
||||
contextNodes: [] as NodeWithScore<Metadata>[],
|
||||
userRequest: "",
|
||||
userRequest: "" as MessageContent,
|
||||
totalQuestions: 0,
|
||||
researchResults: [] as ResearchResult[],
|
||||
};
|
||||
@@ -178,7 +180,7 @@ export function getWorkflow(index: VectorStoreIndex | LlamaCloudIndex) {
|
||||
}),
|
||||
);
|
||||
|
||||
const retrievedNodes = await retriever.retrieve(userInput);
|
||||
const retrievedNodes = await retriever.retrieve({ query: userInput });
|
||||
|
||||
sendEvent(toSourceEvent(retrievedNodes));
|
||||
sendEvent(
|
||||
@@ -349,7 +351,7 @@ const createResearchPlan = async (
|
||||
memory: ChatMemoryBuffer,
|
||||
contextStr: string,
|
||||
enhancedPrompt: string,
|
||||
userRequest: string,
|
||||
userRequest: MessageContent,
|
||||
) => {
|
||||
const chatHistory = await memory.getMessages();
|
||||
|
||||
@@ -361,7 +363,7 @@ const createResearchPlan = async (
|
||||
context_str: contextStr,
|
||||
conversation_context: conversationContext,
|
||||
enhanced_prompt: enhancedPrompt,
|
||||
user_request: userRequest,
|
||||
user_request: extractText(userRequest),
|
||||
});
|
||||
|
||||
const responseFormat = z.object({
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
This is a [LlamaIndex](https://www.llamaindex.ai/) project bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, install the dependencies:
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
Third, run the development server:
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the chat UI.
|
||||
|
||||
## Configure LLM and Embedding Model
|
||||
|
||||
You can configure [LLM model](https://ts.llamaindex.ai/docs/llamaindex/modules/llms) in the [settings file](src/app/settings.ts).
|
||||
|
||||
## Custom UI Components
|
||||
|
||||
We have a custom component located in `components/ui_event.jsx`. This is used to display the state of artifact workflows in UI. You can regenerate a new UI component from the workflow event schema by running the following command:
|
||||
|
||||
```
|
||||
npm run generate:ui
|
||||
```
|
||||
|
||||
## Use Case
|
||||
|
||||
AI-powered document generator that can help you generate documents with a chat interface and simple markdown editor.
|
||||
|
||||
To update the workflow, you can modify the code in [`workflow.ts`](app/workflow.ts).
|
||||
|
||||
You can start by sending an request on the [chat UI](http://localhost:3000) or you can test the `/api/chat` endpoint with the following curl request:
|
||||
|
||||
```shell
|
||||
curl --location 'localhost:3000/api/chat' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{ "messages": [{ "role": "user", "content": "Compare the financial performance of Apple and Tesla" }] }'
|
||||
```
|
||||
|
||||
## 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/docs/llamaindex) - learn about LlamaIndex (Typescript features).
|
||||
- [Workflows Introduction](https://ts.llamaindex.ai/docs/llamaindex/modules/workflows) - learn about LlamaIndexTS workflows.
|
||||
|
||||
You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
|
||||
+11
-24
@@ -1,5 +1,5 @@
|
||||
import { extractLastArtifact } from "@llamaindex/server";
|
||||
import { ChatMemoryBuffer, LLM, Settings } from "llamaindex";
|
||||
import { ChatMemoryBuffer, MessageContent, Settings } from "llamaindex";
|
||||
|
||||
import {
|
||||
agentStreamEvent,
|
||||
@@ -40,7 +40,7 @@ export const UIEventSchema = z.object({
|
||||
export type UIEvent = z.infer<typeof UIEventSchema>;
|
||||
|
||||
const planEvent = workflowEvent<{
|
||||
userInput: string;
|
||||
userInput: MessageContent;
|
||||
context?: string | undefined;
|
||||
}>();
|
||||
|
||||
@@ -68,32 +68,28 @@ const artifactEvent = workflowEvent<{
|
||||
};
|
||||
}>();
|
||||
|
||||
export function createDocumentArtifactWorkflow(reqBody: any, llm?: LLM) {
|
||||
if (!llm) {
|
||||
llm = Settings.llm;
|
||||
}
|
||||
export function workflowFactory(reqBody: any) {
|
||||
const llm = Settings.llm;
|
||||
|
||||
const { withState, getContext } = createStatefulMiddleware(() => {
|
||||
return {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm,
|
||||
chatHistory: reqBody.chatHistory,
|
||||
}),
|
||||
memory: new ChatMemoryBuffer({ llm }),
|
||||
lastArtifact: extractLastArtifact(reqBody),
|
||||
};
|
||||
});
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
workflow.handle([startAgentEvent], async ({ data: { userInput } }) => {
|
||||
workflow.handle([startAgentEvent], async ({ data }) => {
|
||||
const { userInput, chatHistory = [] } = data;
|
||||
// Prepare chat history
|
||||
const { state } = getContext();
|
||||
// Put user input to the memory
|
||||
if (!userInput) {
|
||||
throw new Error("Missing user input to start the workflow");
|
||||
}
|
||||
state.memory.put({
|
||||
role: "user",
|
||||
content: userInput,
|
||||
});
|
||||
state.memory.set(chatHistory);
|
||||
state.memory.put({ role: "user", content: userInput });
|
||||
|
||||
return planEvent.with({
|
||||
userInput,
|
||||
context: state.lastArtifact
|
||||
@@ -175,15 +171,6 @@ export function createDocumentArtifactWorkflow(reqBody: any, llm?: LLM) {
|
||||
role: "assistant",
|
||||
content: `Planning for the document generation: \n${response.text}`,
|
||||
});
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "generate",
|
||||
requirement: requirement.requirement,
|
||||
},
|
||||
}),
|
||||
);
|
||||
return generateArtifactEvent.with({
|
||||
requirement,
|
||||
});
|
||||
+25
-3
@@ -12,11 +12,12 @@ from llama_index.server.services.llamacloud.generate import (
|
||||
load_to_llamacloud,
|
||||
)
|
||||
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
def generate_datasource():
|
||||
def generate_index():
|
||||
init_settings()
|
||||
logger.info("Generate index for the provided data")
|
||||
|
||||
@@ -27,5 +28,26 @@ def generate_datasource():
|
||||
load_to_llamacloud(index, logger=logger)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_datasource()
|
||||
def generate_ui_for_workflow():
|
||||
"""
|
||||
Generate UI for UIEventData event in app/workflow.py
|
||||
"""
|
||||
import asyncio
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from main import COMPONENT_DIR
|
||||
|
||||
# To generate UI components for additional event types,
|
||||
# import the corresponding data model (e.g., MyCustomEventData)
|
||||
# and run the generate_ui_for_workflow function with the imported model.
|
||||
# Make sure the output filename of the generated UI component matches the event type (here `ui_event`)
|
||||
try:
|
||||
from app.workflow import UIEventData # type: ignore
|
||||
except ImportError:
|
||||
raise ImportError("Couldn't generate UI component for the current workflow.")
|
||||
from llama_index.server.gen_ui import generate_event_component
|
||||
|
||||
# works also well with Claude 3.7 Sonnet or Gemini Pro 2.5
|
||||
llm = OpenAI(model="gpt-4.1")
|
||||
code = asyncio.run(generate_event_component(event_cls=UIEventData, llm=llm))
|
||||
with open(f"{COMPONENT_DIR}/ui_event.jsx", "w") as f:
|
||||
f.write(code)
|
||||
|
||||
+38
-2
@@ -1,7 +1,8 @@
|
||||
import { generateEventComponent } from "@llamaindex/server";
|
||||
import * as dotenv from "dotenv";
|
||||
import "dotenv/config";
|
||||
import * as fs from "fs/promises";
|
||||
import { LLamaCloudFileService } from "llamaindex";
|
||||
import { LLamaCloudFileService, OpenAI } from "llamaindex";
|
||||
import * as path from "path";
|
||||
import { getIndex } from "./app/data";
|
||||
import { initSettings } from "./app/settings";
|
||||
@@ -88,7 +89,7 @@ async function loadAndIndex() {
|
||||
console.log(`Successfully uploaded documents to LlamaCloud!`);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
async function generateDatasource() {
|
||||
try {
|
||||
checkRequiredEnvVars();
|
||||
initSettings();
|
||||
@@ -97,4 +98,39 @@ async function loadAndIndex() {
|
||||
} catch (error) {
|
||||
console.error("Error generating storage.", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function generateUi() {
|
||||
// Also works well with Claude 3.5 Sonnet and Google Gemini 2.5 Pro
|
||||
const llm = new OpenAI({ model: "gpt-4.1" });
|
||||
|
||||
const workflowModule = await import("./app/workflow");
|
||||
const UIEventSchema = (workflowModule as any).UIEventSchema;
|
||||
if (!UIEventSchema) {
|
||||
throw new Error(
|
||||
"To generate the UI, you must define a UIEventSchema in your workflow.",
|
||||
);
|
||||
}
|
||||
|
||||
const generatedCode = await generateEventComponent(UIEventSchema, llm);
|
||||
// Write the generated code to components/ui_event.ts
|
||||
await fs.writeFile("components/ui_event.jsx", generatedCode);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
initSettings();
|
||||
|
||||
if (command === "datasource") {
|
||||
await generateDatasource();
|
||||
} else if (command === "ui") {
|
||||
await generateUi();
|
||||
} else {
|
||||
console.error(
|
||||
'Invalid command. Please use "datasource" or "ui". Running "datasource" by default.',
|
||||
);
|
||||
await generateDatasource(); // Default behavior or could throw an error
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import os
|
||||
|
||||
from llama_index.core import Settings
|
||||
from llama_index.embeddings.openai import OpenAIEmbedding
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
|
||||
def init_settings():
|
||||
if os.getenv("OPENAI_API_KEY") is None:
|
||||
raise RuntimeError("OPENAI_API_KEY is missing in environment variables")
|
||||
Settings.llm = OpenAI(model="gpt-4o-mini")
|
||||
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
|
||||
|
||||
@@ -17,8 +17,10 @@ def create_app():
|
||||
ui_config=UIConfig(
|
||||
component_dir=COMPONENT_DIR,
|
||||
app_title="Chat App",
|
||||
dev_mode=True, # Please disable this in production
|
||||
),
|
||||
logger=logger,
|
||||
env="dev",
|
||||
)
|
||||
# You can also add custom FastAPI routes to app
|
||||
app.add_api_route("/api/health", lambda: {"message": "OK"}, status_code=200)
|
||||
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"pydantic<2.10",
|
||||
"aiostream>=0.5.2,<0.6.0",
|
||||
"llama-index-core>=0.12.28,<0.13.0",
|
||||
"llama-index-server>=0.1.15,<0.2.0",
|
||||
"llama-index-server>=0.1.16,<0.2.0",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"watch": ["src/**/*.ts"],
|
||||
"exec": "nodemon --exec tsx src/index.ts",
|
||||
"ext": "js ts",
|
||||
"ignore": ["src/app/workflow_*.ts"]
|
||||
}
|
||||
@@ -5,21 +5,22 @@
|
||||
"generate": "tsx src/generate.ts datasource",
|
||||
"generate:datasource": "tsx src/generate.ts datasource",
|
||||
"generate:ui": "tsx src/generate.ts ui",
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"dev": "nodemon",
|
||||
"start": "tsx src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@llamaindex/openai": "0.2.0",
|
||||
"@llamaindex/server": "0.2.0",
|
||||
"@llamaindex/workflow": "1.1.1",
|
||||
"@llamaindex/tools": "0.0.9",
|
||||
"@llamaindex/openai": "~0.4.0",
|
||||
"@llamaindex/server": "~0.2.1",
|
||||
"@llamaindex/workflow": "~1.1.3",
|
||||
"@llamaindex/tools": "~0.0.11",
|
||||
"llamaindex": "~0.11.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"zod": "^3.23.8",
|
||||
"llamaindex": "0.10.5"
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.3",
|
||||
"tsx": "^4.7.2",
|
||||
"typescript": "^5.3.2"
|
||||
"typescript": "^5.3.2",
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Settings } from "llamaindex";
|
||||
|
||||
export function initSettings() {
|
||||
Settings.llm = new OpenAI({
|
||||
model: "gpt-4o-mini",
|
||||
model: "gpt-4.1",
|
||||
});
|
||||
Settings.embedModel = new OpenAIEmbedding({
|
||||
model: "text-embedding-3-small",
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import "dotenv/config";
|
||||
import { SimpleDirectoryReader } from "@llamaindex/readers/directory";
|
||||
import {
|
||||
OpenAI,
|
||||
storageContextFromDefaults,
|
||||
VectorStoreIndex,
|
||||
} from "llamaindex";
|
||||
import { storageContextFromDefaults, VectorStoreIndex } from "llamaindex";
|
||||
import { initSettings } from "./app/settings";
|
||||
import fs from "fs";
|
||||
import { generateEventComponent } from "@llamaindex/server";
|
||||
import { UIEventSchema } from "./app/workflow";
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
|
||||
async function generateDatasource() {
|
||||
console.log(`Generating storage context...`);
|
||||
@@ -30,6 +26,14 @@ async function generateUi() {
|
||||
// Also works well with Claude 3.5 Sonnet and Google Gemini 2.5 Pro
|
||||
const llm = new OpenAI({ model: "gpt-4.1" });
|
||||
|
||||
const workflowModule = await import("./app/workflow");
|
||||
const UIEventSchema = (workflowModule as any).UIEventSchema;
|
||||
if (!UIEventSchema) {
|
||||
throw new Error(
|
||||
"To generate the UI, you must define a UIEventSchema in your workflow.",
|
||||
);
|
||||
}
|
||||
|
||||
// You can also generate for other workflow events
|
||||
const generatedCode = await generateEventComponent(UIEventSchema, llm);
|
||||
// Write the generated code to components/ui_event.ts
|
||||
|
||||
@@ -10,5 +10,6 @@ new LlamaIndexServer({
|
||||
uiConfig: {
|
||||
appTitle: "LlamaIndex App",
|
||||
componentsDir: "components",
|
||||
devMode: true,
|
||||
},
|
||||
}).start();
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
server/
|
||||
# server contains Nextjs frontend code (not compiled)
|
||||
server/
|
||||
|
||||
# temp is the copy of next folder but without API folder, used to build frontend static files
|
||||
temp/
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/server
|
||||
|
||||
## 0.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 25fba43: refactor: migrate to Nextjs Route Handler
|
||||
- 6f75d4a: fix: unsupported language in code gen workflow
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f072308: feat: add dev mode UI
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
+128
-5
@@ -4,10 +4,10 @@ LlamaIndexServer is a Next.js-based application that allows you to quickly launc
|
||||
|
||||
## Features
|
||||
|
||||
- Serving a workflow as a chatbot
|
||||
- Add a sophisticated chatbot UI to your LlamaIndex workflow
|
||||
- Edit code and document artifacts in an OpenAI Canvas-style UI
|
||||
- Extendable UI components for events and headers
|
||||
- Built on Next.js for high performance and easy API development
|
||||
- Optional built-in chat UI with extendable UI components
|
||||
- Prebuilt development code
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -21,9 +21,11 @@ Create an `index.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import { LlamaIndexServer } from "@llamaindex/server";
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { wiki } from "@llamaindex/tools"; // or any other tool
|
||||
|
||||
const createWorkflow = () => agent({ tools: [wiki()] });
|
||||
const createWorkflow = () => agent({ tools: [wiki()], llm: openai("gpt-4o") });
|
||||
|
||||
new LlamaIndexServer({
|
||||
workflow: createWorkflow,
|
||||
@@ -34,6 +36,8 @@ new LlamaIndexServer({
|
||||
}).start();
|
||||
```
|
||||
|
||||
The `createWorkflow` function is a factory function that creates an [Agent Workflow](https://ts.llamaindex.ai/docs/llamaindex/modules/agents/agent_workflow) with a tool that retrieves information from Wikipedia in this case. For more details, read about the [Workflow factory contract](#workflow-factory-contract).
|
||||
|
||||
## Running the Server
|
||||
|
||||
In the same directory as `index.ts`, run the following command to start the server:
|
||||
@@ -54,16 +58,75 @@ curl -X POST "http://localhost:3000/api/chat" -H "Content-Type: application/json
|
||||
|
||||
The `LlamaIndexServer` accepts the following configuration options:
|
||||
|
||||
- `workflow`: A callable function that creates a workflow instance for each request
|
||||
- `workflow`: A callable function that creates a workflow instance for each request. See [Workflow factory contract](#workflow-factory-contract) for more details.
|
||||
- `uiConfig`: An object to configure the chat UI containing the following properties:
|
||||
- `appTitle`: The title of the application (default: `"LlamaIndex App"`)
|
||||
- `starterQuestions`: List of starter questions for the chat UI (default: `[]`)
|
||||
- `componentsDir`: The directory for custom UI components rendering events emitted by the workflow. The default is undefined, which does not render custom UI components.
|
||||
- `llamaCloudIndexSelector`: Whether to show the LlamaCloud index selector in the chat UI (requires `LLAMA_CLOUD_API_KEY` to be set in the environment variables) (default: `false`)
|
||||
- `dev_mode`: When enabled, you can update workflow code in the UI and see the changes immediately. It's currently in beta and only supports updating workflow code at `app/src/workflow.ts`. Please start server in dev mode (`npm run dev`) to use see this reload feature enabled.
|
||||
|
||||
LlamaIndexServer accepts all the configuration options from Nextjs Custom Server such as `port`, `hostname`, `dev`, etc.
|
||||
See all Nextjs Custom Server options [here](https://nextjs.org/docs/app/building-your-application/configuring/custom-server).
|
||||
|
||||
## Workflow factory contract
|
||||
|
||||
The `workflow` provided will be called for each chat request to initialize a new workflow instance. The contract of the generated workflow must be the same as for the [Agent Workflow](https://ts.llamaindex.ai/docs/llamaindex/modules/agents/agent_workflow).
|
||||
|
||||
This means that the workflow must handle a `startAgentEvent` event, which is the entry point of the workflow and contains the following information in it's `data` property:
|
||||
|
||||
```typescript
|
||||
{
|
||||
userInput: MessageContent;
|
||||
chatHistory?: ChatMessage[] | undefined;
|
||||
};
|
||||
```
|
||||
|
||||
The `userInput` is the latest user message and the `chatHistory` is the list of messages exchanged between the user and the workflow so far.
|
||||
|
||||
Furthermore, the workflow must stop with a `stopAgentEvent` event to mark the end of the workflow. In between, the workflow can emit [UI events](##AI-generated-UI-Components) to render custom UI components and [Artifact events](##Sending-Artifacts-to-the-UI) to send structured data like generated documents or code snippets to the UI.
|
||||
|
||||
```ts
|
||||
import {
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
startAgentEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import { ChatMemoryBuffer, type ChatMessage, Settings } from "llamaindex";
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import { wiki } from "@llamaindex/tools";
|
||||
|
||||
Settings.llm = openai("gpt-4o");
|
||||
|
||||
export const workflowFactory = async () => {
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([startAgentEvent], async ({ data }) => {
|
||||
const { state, sendEvent } = getContext();
|
||||
const messages = data.chatHistory;
|
||||
|
||||
const toolCallResponse = await chatWithTools(
|
||||
Settings.llm,
|
||||
[wiki()],
|
||||
messages,
|
||||
);
|
||||
|
||||
// using result from tool call and use `sendEvent` to emit the next event...
|
||||
});
|
||||
|
||||
// define more workflow handling logic here...
|
||||
|
||||
// Finally stop with a `stopAgentEvent` event to mark the end of the workflow.
|
||||
// return stopAgentEvent.with({
|
||||
// result: "This is the end!",
|
||||
// });
|
||||
|
||||
return workflow;
|
||||
};
|
||||
```
|
||||
|
||||
To generate sophisticated examples of workflows, you best use the [create-llama](https://github.com/run-llama/create-llama) project.
|
||||
|
||||
## AI-generated UI Components
|
||||
|
||||
The LlamaIndex server provides support for rendering workflow events using custom UI components, allowing you to extend and customize the chat interface.
|
||||
@@ -137,6 +200,66 @@ new LlamaIndexServer({
|
||||
}).start();
|
||||
```
|
||||
|
||||
## Sending Artifacts to the UI
|
||||
|
||||
In addition to UI events for custom components, LlamaIndex Server supports a special `ArtifactEvent` to send structured data like generated documents or code snippets to the UI. These artifacts are displayed in a dedicated "Canvas" panel in the chat interface.
|
||||
|
||||
### Artifact Event Structure
|
||||
|
||||
To send an artifact, your workflow needs to emit an event with `type: "artifact"`. The `data` payload of this event should include:
|
||||
|
||||
- `type`: A string indicating the type of artifact (e.g., `"document"`, `"code"`).
|
||||
- `created_at`: A timestamp (e.g., `Date.now()`) indicating when the artifact was created.
|
||||
- `data`: An object containing the specific details of the artifact. The structure of this object depends on the artifact `type`.
|
||||
|
||||
### Defining and Sending an ArtifactEvent
|
||||
|
||||
First, define your artifact event using `workflowEvent` from `@llamaindex/workflow`:
|
||||
|
||||
```typescript
|
||||
import { workflowEvent } from "@llamaindex/workflow";
|
||||
|
||||
// Example for a document artifact
|
||||
const artifactEvent = workflowEvent<{
|
||||
type: "artifact"; // Must be "artifact"
|
||||
data: {
|
||||
type: "document"; // Custom type for your artifact (e.g., "document", "code")
|
||||
created_at: number;
|
||||
data: {
|
||||
// Specific data for the document artifact type
|
||||
title: string;
|
||||
content: string;
|
||||
type: "markdown" | "html"; // document format
|
||||
};
|
||||
};
|
||||
}>();
|
||||
```
|
||||
|
||||
Then, within your workflow logic, use `sendEvent` (obtained from `getContext()`) to emit the event:
|
||||
|
||||
```typescript
|
||||
// Assuming 'sendEvent' is available in your workflow handler
|
||||
// and 'documentDetails' contains the content for the artifact.
|
||||
|
||||
sendEvent(
|
||||
artifactEvent.with({
|
||||
type: "artifact", // This top-level type must be "artifact"
|
||||
data: {
|
||||
type: "document", // This is your specific artifact type
|
||||
created_at: Date.now(),
|
||||
data: {
|
||||
title: "My Generated Document",
|
||||
content: "# Hello World
|
||||
This is a markdown document.",
|
||||
type: "markdown",
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
This will send the artifact to the LlamaIndex Server UI, where it will be rendered in the [ChatCanvasPanel](/packages/server/next/app/components/ui/chat/canvas/panel.tsx) by a renderer depending on the artifact type. For type `document` this is using the [DocumentArtifactViewer](https://github.com/run-llama/chat-ui/blob/bacb75fc6edceacf742fba18632404a2483b5a81/packages/chat-ui/src/chat/canvas/artifacts/document.tsx#L17).
|
||||
|
||||
## Default Endpoints and Features
|
||||
|
||||
### Chat Endpoint
|
||||
|
||||
@@ -6,7 +6,7 @@ This directory contains examples of how to use the LlamaIndex Server.
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=your_openai_api_key
|
||||
npx tsx simple-workflow/calculator.ts
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
## Open browser at http://localhost:3000
|
||||
|
||||
@@ -39,5 +39,5 @@ new LlamaIndexServer({
|
||||
appTitle: "LlamaIndex App",
|
||||
starterQuestions: ["What is the color of the dog?"],
|
||||
},
|
||||
port: 4100,
|
||||
port: 3000,
|
||||
}).start();
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
This example shows how to use the dev mode of the server.
|
||||
|
||||
First, we need to set `devMode` to `true` in the `uiConfig` of the server.
|
||||
|
||||
```ts
|
||||
new LlamaIndexServer({
|
||||
workflow: workflowFactory,
|
||||
uiConfig: {
|
||||
appTitle: "Calculator",
|
||||
devMode: true,
|
||||
},
|
||||
port: 3000,
|
||||
}).start();
|
||||
```
|
||||
|
||||
Export OpenAI API key and start the server in dev mode.
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=<your-openai-api-key>
|
||||
npx nodemon --exec tsx index.ts --ignore src/app/workflow_*.ts
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LlamaIndexServer } from "@llamaindex/server";
|
||||
import { workflowFactory } from "./src/app/workflow";
|
||||
|
||||
new LlamaIndexServer({
|
||||
workflow: workflowFactory,
|
||||
uiConfig: {
|
||||
appTitle: "Calculator",
|
||||
devMode: true,
|
||||
starterQuestions: [
|
||||
"What is the weather in Tokyo?",
|
||||
"What is the weather in New York?",
|
||||
],
|
||||
},
|
||||
port: 3000,
|
||||
}).start();
|
||||
@@ -0,0 +1,16 @@
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
export const workflowFactory = async () => {
|
||||
return agent({
|
||||
tools: [
|
||||
tool({
|
||||
name: "weather",
|
||||
description: "Get the weather in a specific city",
|
||||
parameters: z.object({ city: z.string() }),
|
||||
execute: ({ city }) => `The weather in ${city} is sunny`,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "tsx simple-workflow/calculator.ts"
|
||||
"dev": "nodemon --exec tsx simple-workflow/calculator.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@llamaindex/openai": "^0.2.0",
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.3",
|
||||
"nodemon": "^3.1.10",
|
||||
"tsx": "^4.7.2",
|
||||
"typescript": "^5.3.2"
|
||||
}
|
||||
|
||||
@@ -20,5 +20,5 @@ new LlamaIndexServer({
|
||||
appTitle: "Calculator",
|
||||
starterQuestions: ["1 + 1", "2 + 2"],
|
||||
},
|
||||
port: 4000,
|
||||
port: 3000,
|
||||
}).start();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { LLamaCloudFileService } from "llamaindex";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function GET(request: NextRequest): Promise<NextResponse> {
|
||||
if (!getEnv("LLAMA_CLOUD_API_KEY")) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "env variable LLAMA_CLOUD_API_KEY is required to use LlamaCloud",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const config = {
|
||||
projects: await LLamaCloudFileService.getAllProjectsWithPipelines(),
|
||||
pipeline: {
|
||||
pipeline: getEnv("LLAMA_CLOUD_INDEX_NAME"),
|
||||
project: getEnv("LLAMA_CLOUD_PROJECT_NAME"),
|
||||
},
|
||||
};
|
||||
return NextResponse.json(config, { status: 200 });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: "Failed to fetch LlamaCloud configuration",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { type AgentInputData } from "@llamaindex/workflow";
|
||||
import { type Message } from "ai";
|
||||
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 workflow factory from local file
|
||||
import { workflowFactory } from "../../../../app/workflow";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const reqBody = await req.json();
|
||||
const { messages } = reqBody as { messages: Message[] };
|
||||
const chatHistory = messages.map((message) => ({
|
||||
role: message.role as MessageType,
|
||||
content: message.content,
|
||||
}));
|
||||
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
if (lastMessage?.role !== "user") {
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: "Messages cannot be empty and last message must be from user",
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
const workflowInput: AgentInputData = {
|
||||
userInput: lastMessage.content,
|
||||
chatHistory,
|
||||
};
|
||||
|
||||
const abortController = new AbortController();
|
||||
req.signal.addEventListener("abort", () =>
|
||||
abortController.abort("Connection closed"),
|
||||
);
|
||||
|
||||
const workflow = await workflowFactory(reqBody);
|
||||
const workflowEventStream = await runWorkflow(
|
||||
workflow,
|
||||
workflowInput,
|
||||
abortController.signal,
|
||||
);
|
||||
|
||||
const dataStream = toDataStream(workflowEventStream, {
|
||||
callbacks: {
|
||||
onFinal: async (completion, dataStreamWriter) => {
|
||||
chatHistory.push({
|
||||
role: "assistant" as MessageType,
|
||||
content: completion,
|
||||
});
|
||||
await sendSuggestedQuestionsEvent(dataStreamWriter, chatHistory);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(dataStream, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
"X-Vercel-AI-Data-Stream": "v1",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Chat handler error:", error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: (error as Error).message || "Internal server error",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-13
@@ -1,20 +1,19 @@
|
||||
import fs from "fs";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import path from "path";
|
||||
import { promisify } from "util";
|
||||
import { sendJSONResponse } from "../utils/request";
|
||||
|
||||
export const getComponents = async (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
componentsDir: string,
|
||||
) => {
|
||||
export async function GET(request: NextRequest) {
|
||||
const params = request.nextUrl.searchParams;
|
||||
const componentsDir = params.get("componentsDir") || "components";
|
||||
|
||||
try {
|
||||
const exists = await promisify(fs.exists)(componentsDir);
|
||||
if (!exists) {
|
||||
return sendJSONResponse(res, 404, {
|
||||
error: "Components directory not found",
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: "Components directory not found" },
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
const files = await promisify(fs.readdir)(componentsDir);
|
||||
@@ -40,12 +39,15 @@ export const getComponents = async (
|
||||
}),
|
||||
);
|
||||
|
||||
sendJSONResponse(res, 200, components);
|
||||
return NextResponse.json(components, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error("Error reading components:", error);
|
||||
sendJSONResponse(res, 500, { error: "Failed to read components" });
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to read components" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function filterDuplicateComponents(files: string[]) {
|
||||
const compMap = new Map<string, string>();
|
||||
@@ -0,0 +1,96 @@
|
||||
import { exec } from "child_process";
|
||||
import fs from "fs";
|
||||
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
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const filePath = DEFAULT_WORKFLOW_FILE_PATH;
|
||||
|
||||
const fileExists = await promisify(fs.exists)(DEFAULT_WORKFLOW_FILE_PATH);
|
||||
if (!fileExists) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: `Dev mode is currently in beta. It only supports updating workflow file at ${filePath}`,
|
||||
},
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
const content = await promisify(fs.readFile)(filePath, "utf-8");
|
||||
const last_modified = fs.statSync(filePath).mtime.getTime();
|
||||
|
||||
return NextResponse.json(
|
||||
{ content, file_path: filePath, last_modified },
|
||||
{ status: 200 },
|
||||
);
|
||||
}
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
const filePath = DEFAULT_WORKFLOW_FILE_PATH;
|
||||
const { content } = await request.json();
|
||||
|
||||
const fileExists = await promisify(fs.exists)(filePath);
|
||||
if (!fileExists) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: `Dev mode is currently in beta. It only supports updating workflow file at ${DEFAULT_WORKFLOW_FILE_PATH}`,
|
||||
},
|
||||
{ status: 404 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const resolvedFilePath = path.resolve(DEFAULT_WORKFLOW_FILE_PATH);
|
||||
const result = await validateTypeScriptFile(resolvedFilePath, content);
|
||||
|
||||
if (!result.isValid) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: result.errors.join("\n"),
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
await promisify(fs.writeFile)(filePath, content);
|
||||
return NextResponse.json({ content }, { status: 200 });
|
||||
} catch (error) {
|
||||
console.error("Error updating workflow file:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to update workflow file" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// use typescript package to validate the file syntax and imports
|
||||
async function validateTypeScriptFile(filePath: string, content: string) {
|
||||
// Update workflow file directly will cause the server restart immediately.
|
||||
// So we create a temporary file with the same content in the same directory as the workflow file
|
||||
// This file will be used to validate the file syntax and imports. It will be deleted after validation.
|
||||
const tempFilePath = path.join(
|
||||
path.dirname(filePath),
|
||||
`workflow_${Date.now()}.ts`,
|
||||
);
|
||||
fs.writeFileSync(tempFilePath, content);
|
||||
|
||||
const errors = [];
|
||||
try {
|
||||
const tscCommand = `npx tsc ${tempFilePath} --noEmit --skipLibCheck true`;
|
||||
await promisify(exec)(tscCommand);
|
||||
} catch (error) {
|
||||
const errorMessage = (error as { stdout: string })?.stdout;
|
||||
errors.push(errorMessage);
|
||||
} finally {
|
||||
// Clean up temporary file
|
||||
if (fs.existsSync(tempFilePath)) fs.unlinkSync(tempFilePath);
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors: errors,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import fs from "fs";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { promisify } from "util";
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ slug: string[] }> },
|
||||
) {
|
||||
const filePath = (await params).slug.join("/");
|
||||
|
||||
if (!filePath.startsWith("output") && !filePath.startsWith("data")) {
|
||||
return NextResponse.json({ error: "No permission" }, { status: 400 });
|
||||
}
|
||||
|
||||
const decodedFilePath = decodeURIComponent(filePath);
|
||||
const fileExists = await promisify(fs.exists)(decodedFilePath);
|
||||
|
||||
if (fileExists) {
|
||||
const fileBuffer = await promisify(fs.readFile)(decodedFilePath);
|
||||
return new NextResponse(fileBuffer);
|
||||
} else {
|
||||
return NextResponse.json({ error: "File not found" }, { status: 404 });
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import CustomChatMessages from "./chat-messages";
|
||||
import { DynamicEventsErrors } from "./custom/events/dynamic-events-errors";
|
||||
import { fetchComponentDefinitions } from "./custom/events/loader";
|
||||
import { ComponentDef } from "./custom/events/types";
|
||||
import { DevModePanel } from "./dev-mode-panel";
|
||||
|
||||
export default function ChatSection() {
|
||||
const handler = useChat({
|
||||
@@ -35,12 +36,13 @@ export default function ChatSection() {
|
||||
<ChatHeader />
|
||||
<ChatUI
|
||||
handler={handler}
|
||||
className="flex min-h-0 flex-1 flex-row justify-center gap-4 px-4 py-0"
|
||||
className="relative flex min-h-0 flex-1 flex-row justify-center gap-4 px-4 py-0"
|
||||
>
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ChatSectionPanel />
|
||||
<ChatCanvasPanel />
|
||||
</ResizablePanelGroup>
|
||||
<DevModePanel />
|
||||
</ChatUI>
|
||||
</div>
|
||||
<ChatInjection />
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
CodeEditor,
|
||||
fileExtensionToEditorLang,
|
||||
} from "@llamaindex/chat-ui/widgets";
|
||||
import { AlertCircle, Loader2 } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Button } from "../button";
|
||||
import { getConfig } from "../lib/utils";
|
||||
|
||||
const API_PATH = "/api/dev/files/workflow";
|
||||
const POLLING_TIMEOUT = 30_000; // 30 seconds
|
||||
|
||||
type WorkflowFile = {
|
||||
last_modified: number;
|
||||
file_path: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export function DevModePanel() {
|
||||
const devModeEnabled = getConfig("DEV_MODE");
|
||||
if (!devModeEnabled) return null;
|
||||
return <DevModePanelComp />;
|
||||
}
|
||||
|
||||
function DevModePanelComp() {
|
||||
const [devModeOpen, setDevModeOpen] = useState(false);
|
||||
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [fetchingError, setFetchingError] = useState<string | null>();
|
||||
const [workflowFile, setWorkflowFile] = useState<WorkflowFile | null>(null);
|
||||
|
||||
const [updatedCode, setUpdatedCode] = useState<string | null>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
|
||||
const [isPolling, setIsPolling] = useState(false);
|
||||
const [pollingError, setPollingError] = useState<string | null>(null);
|
||||
|
||||
async function fetchWorkflowCode() {
|
||||
try {
|
||||
setIsFetching(true);
|
||||
const response = await fetch(API_PATH);
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data?.detail ?? "Unknown error");
|
||||
}
|
||||
|
||||
setWorkflowFile(data);
|
||||
setFetchingError(null);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
setFetchingError(errorMessage);
|
||||
console.warn("Error fetching workflow code:", error);
|
||||
} finally {
|
||||
setIsFetching(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function restartingWorkflow() {
|
||||
if (!workflowFile) return;
|
||||
|
||||
const initialLastModified = workflowFile.last_modified;
|
||||
setIsPolling(true);
|
||||
setPollingError(null);
|
||||
|
||||
const pollStartTime = Date.now();
|
||||
|
||||
// interval refetching the updated workflow code
|
||||
const poll = async () => {
|
||||
if (Date.now() - pollStartTime > POLLING_TIMEOUT) {
|
||||
setPollingError(
|
||||
`Server not responding after ${POLLING_TIMEOUT / 1000} seconds.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const pollResponse = await fetch(API_PATH);
|
||||
const pollData = (await pollResponse.json()) as WorkflowFile;
|
||||
if (pollData.last_modified !== initialLastModified) {
|
||||
setWorkflowFile(pollData);
|
||||
setUpdatedCode(pollData.content);
|
||||
setIsPolling(false);
|
||||
setPollingError(null);
|
||||
setDevModeOpen(false);
|
||||
} else {
|
||||
setTimeout(poll, 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.info("Polling error", error);
|
||||
setTimeout(poll, 2000);
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(poll, 2000);
|
||||
}
|
||||
|
||||
const handleResetCode = () => {
|
||||
setUpdatedCode(workflowFile?.content ?? null);
|
||||
setSaveError(null);
|
||||
};
|
||||
|
||||
const handleSaveCode = async () => {
|
||||
if (!workflowFile) return;
|
||||
|
||||
try {
|
||||
setIsSaving(true);
|
||||
const response = await fetch(API_PATH, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: updatedCode,
|
||||
file_path: workflowFile.file_path,
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (!response.ok) {
|
||||
throw new Error(data?.detail ?? "Unknown error");
|
||||
}
|
||||
setSaveError(null);
|
||||
await restartingWorkflow();
|
||||
} catch (error) {
|
||||
console.warn("Error saving workflow code:", error);
|
||||
setSaveError(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Unknown error happened when saving workflow code",
|
||||
);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (devModeOpen) {
|
||||
fetchWorkflowCode();
|
||||
}
|
||||
}, [devModeOpen]);
|
||||
|
||||
const codeEditorLanguage = useMemo(() => {
|
||||
if (!workflowFile?.file_path) return undefined;
|
||||
return fileExtensionToEditorLang(
|
||||
workflowFile.file_path.split(".").pop() ?? "",
|
||||
);
|
||||
}, [workflowFile]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => setDevModeOpen(!devModeOpen)}
|
||||
className="fixed right-2 top-1/2 origin-right -translate-y-1/2 rotate-90 transform rounded-l-md shadow-md transition-transform hover:-translate-x-1"
|
||||
>
|
||||
Dev Mode
|
||||
</Button>
|
||||
|
||||
{isPolling && (
|
||||
<div className="fixed inset-0 z-50 flex flex-col items-center justify-center bg-black/50 backdrop-blur-sm">
|
||||
{!pollingError && (
|
||||
<>
|
||||
<Loader2 className="mb-4 h-16 w-16 animate-spin text-white" />
|
||||
<p className="text-lg font-semibold text-white">
|
||||
Applying changes and restarting server...
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-slate-300">
|
||||
Please wait for a while then you can start chatting with the
|
||||
updated workflow.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{pollingError && (
|
||||
<div className="bg-destructive/20 text-destructive-foreground mt-4 max-w-md rounded-md p-4 text-center">
|
||||
<div className="mb-2 flex items-center justify-center gap-2">
|
||||
<AlertCircle className="shrink-0" size={16} />
|
||||
<h6 className="text-sm font-medium">Server Starting Error</h6>
|
||||
</div>
|
||||
<p className="text-sm">{pollingError}</p>
|
||||
|
||||
<p className="text-sm">
|
||||
Please reload the page and check server logs.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`border-border fixed right-0 top-0 z-10 h-full w-full border-l shadow-xl transition-all duration-300 ease-in-out ${
|
||||
devModeOpen ? "translate-x-0 bg-black/50" : "translate-x-full"
|
||||
}`}
|
||||
onClick={() => setDevModeOpen(false)}
|
||||
>
|
||||
<div
|
||||
className={`bg-background ml-auto flex h-full w-[800px] flex-col p-4`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold">Workflow Editor</h2>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{isFetching ? (
|
||||
"Loading..."
|
||||
) : workflowFile ? (
|
||||
<>
|
||||
Edit the code of <b>{workflowFile.file_path}</b> and save to
|
||||
apply changes to your workflow.
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setDevModeOpen(false)}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
{fetchingError ? (
|
||||
<div className="bg-destructive/10 text-destructive/70 mb-4 flex items-center gap-2 rounded-md p-4">
|
||||
<AlertCircle className="shrink-0" size={16} />
|
||||
<p className="text-sm font-medium">{fetchingError}</p>
|
||||
</div>
|
||||
) : (
|
||||
<CodeEditor
|
||||
code={updatedCode ?? workflowFile?.content ?? ""}
|
||||
onChange={setUpdatedCode}
|
||||
language={codeEditorLanguage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex flex-col">
|
||||
{saveError && (
|
||||
<div className="bg-destructive/10 text-destructive/70 mb-4 rounded-md p-4">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<AlertCircle className="shrink-0" size={16} />
|
||||
<h6 className="text-sm font-medium">Error Saving Code</h6>
|
||||
</div>
|
||||
<p className="whitespace-pre-wrap text-sm">{saveError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="mr-2"
|
||||
onClick={handleResetCode}
|
||||
>
|
||||
Reset Code
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSaveCode}
|
||||
disabled={isSaving || !updatedCode || !workflowFile}
|
||||
>
|
||||
Save & Restart Server
|
||||
{isSaving && <Loader2 className="ml-2 h-4 w-4 animate-spin" />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
},
|
||||
"target": "ES2017"
|
||||
"target": "ES2022"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/server",
|
||||
"description": "LlamaIndex Server",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
@@ -27,21 +27,25 @@
|
||||
"directory": "packages/server"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf ./dist ./server next/.next next/out",
|
||||
"dev": "bunchee --watch",
|
||||
"clean": "rm -rf ./dist ./server next/.next next/out ./temp",
|
||||
"prebuild": "pnpm clean",
|
||||
"build": "bunchee",
|
||||
"postbuild": "pnpm copy:next-src && pnpm build:static && pnpm copy:static",
|
||||
"copy:next-src": "cp -r ./next ./server && pnpm build:css && rm -rf ./server/postcss.config.js",
|
||||
"build:css": "postcss server/app/globals.css -o server/app/globals.css",
|
||||
"build:static": "cd ./next && next build",
|
||||
"copy:static": "cp -r ./next/out ./dist/static",
|
||||
"dev": "bunchee --watch"
|
||||
"postbuild": "pnpm prepare:ts-server && pnpm prepare:py-static",
|
||||
"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: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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/babel__standalone": "^7.1.9",
|
||||
"@types/babel__traverse": "^7.20.7",
|
||||
"llamaindex": "0.10.2",
|
||||
"llamaindex": "~0.11.0",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"tailwindcss": "^4",
|
||||
@@ -55,7 +59,7 @@
|
||||
"@babel/traverse": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@llamaindex/chat-ui": "0.4.3",
|
||||
"@llamaindex/chat-ui": "0.4.4",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.3",
|
||||
@@ -103,9 +107,9 @@
|
||||
"vaul": "^1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@llamaindex/env": "^0.1.29",
|
||||
"@llamaindex/workflow": "^1.1.0",
|
||||
"llamaindex": "^0.10.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"
|
||||
},
|
||||
|
||||
@@ -155,10 +155,12 @@ export function extractAllArtifacts(messages: Message[]): Artifact[] {
|
||||
const artifacts =
|
||||
message.annotations
|
||||
?.filter(
|
||||
(annotation) =>
|
||||
(
|
||||
annotation,
|
||||
): annotation is z.infer<typeof artifactAnnotationSchema> =>
|
||||
artifactAnnotationSchema.safeParse(annotation).success,
|
||||
)
|
||||
.map((artifact) => artifact as Artifact) ?? [];
|
||||
.map((annotation) => annotation.data as Artifact) ?? [];
|
||||
|
||||
allArtifacts.push(...artifacts);
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { LLamaCloudFileService } from "llamaindex";
|
||||
import { sendJSONResponse } from "../utils/request";
|
||||
|
||||
export const getLlamaCloudConfig = async (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
) => {
|
||||
if (!getEnv("LLAMA_CLOUD_API_KEY")) {
|
||||
return sendJSONResponse(res, 500, {
|
||||
error: "env variable LLAMA_CLOUD_API_KEY is required to use LlamaCloud",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const config = {
|
||||
projects: await LLamaCloudFileService.getAllProjectsWithPipelines(),
|
||||
pipeline: {
|
||||
pipeline: getEnv("LLAMA_CLOUD_INDEX_NAME"),
|
||||
project: getEnv("LLAMA_CLOUD_PROJECT_NAME"),
|
||||
},
|
||||
};
|
||||
return sendJSONResponse(res, 200, config);
|
||||
} catch (error) {
|
||||
return sendJSONResponse(res, 500, {
|
||||
error: "Failed to fetch LlamaCloud configuration",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
import fs from "fs";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { promisify } from "util";
|
||||
import { sendJSONResponse } from "../utils/request";
|
||||
|
||||
export const handleServeFiles = async (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
pathname: string,
|
||||
) => {
|
||||
const filePath = pathname.substring("/api/files/".length);
|
||||
if (!filePath.startsWith("output") && !filePath.startsWith("data")) {
|
||||
return sendJSONResponse(res, 400, { error: "No permission" });
|
||||
}
|
||||
const decodedFilePath = decodeURIComponent(filePath);
|
||||
const fileExists = await promisify(fs.exists)(decodedFilePath);
|
||||
if (fileExists) {
|
||||
const fileStream = fs.createReadStream(decodedFilePath);
|
||||
fileStream.pipe(res);
|
||||
} else {
|
||||
return sendJSONResponse(res, 404, { error: "File not found" });
|
||||
}
|
||||
};
|
||||
@@ -7,10 +7,8 @@ import path from "path";
|
||||
import { parse } from "url";
|
||||
import { promisify } from "util";
|
||||
import { handleChat } from "./handlers/chat";
|
||||
import { getLlamaCloudConfig } from "./handlers/cloud";
|
||||
import { getComponents } from "./handlers/components";
|
||||
import { handleServeFiles } from "./handlers/files";
|
||||
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";
|
||||
@@ -44,6 +42,7 @@ export class LlamaIndexServer {
|
||||
? "/api/chat/config/llamacloud"
|
||||
: undefined;
|
||||
const componentsApi = this.componentsDir ? "/api/components" : undefined;
|
||||
const devMode = uiConfig?.devMode ?? false;
|
||||
|
||||
// content in javascript format
|
||||
const content = `
|
||||
@@ -52,7 +51,8 @@ export class LlamaIndexServer {
|
||||
APP_TITLE: ${JSON.stringify(appTitle)},
|
||||
LLAMA_CLOUD_API: ${JSON.stringify(llamaCloudApi)},
|
||||
STARTER_QUESTIONS: ${JSON.stringify(starterQuestions)},
|
||||
COMPONENTS_API: ${JSON.stringify(componentsApi)}
|
||||
COMPONENTS_API: ${JSON.stringify(componentsApi)},
|
||||
DEV_MODE: ${JSON.stringify(devMode)}
|
||||
}
|
||||
`;
|
||||
fs.writeFileSync(configFile, content);
|
||||
@@ -71,33 +71,25 @@ export class LlamaIndexServer {
|
||||
const server = createServer((req, res) => {
|
||||
const parsedUrl = parse(req.url!, true);
|
||||
const pathname = parsedUrl.pathname;
|
||||
const query = parsedUrl.query;
|
||||
|
||||
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
|
||||
return handleChat(req, res, this.workflowFactory);
|
||||
}
|
||||
|
||||
if (pathname?.startsWith("/api/files") && req.method === "GET") {
|
||||
return handleServeFiles(req, res, pathname);
|
||||
}
|
||||
|
||||
if (
|
||||
this.componentsDir &&
|
||||
pathname === "/api/components" &&
|
||||
req.method === "GET"
|
||||
) {
|
||||
return getComponents(req, res, this.componentsDir);
|
||||
}
|
||||
|
||||
if (
|
||||
getEnv("LLAMA_CLOUD_API_KEY") &&
|
||||
pathname === "/api/chat/config/llamacloud" &&
|
||||
req.method === "GET"
|
||||
) {
|
||||
return getLlamaCloudConfig(req, res);
|
||||
query.componentsDir = this.componentsDir;
|
||||
}
|
||||
|
||||
const handle = this.app.getRequestHandler();
|
||||
handle(req, res, parsedUrl);
|
||||
handle(req, res, { ...parsedUrl, query });
|
||||
});
|
||||
|
||||
server.listen(this.port, () => {
|
||||
|
||||
@@ -17,6 +17,7 @@ export type UIConfig = {
|
||||
starterQuestions?: string[];
|
||||
componentsDir?: string;
|
||||
llamaCloudIndexSelector?: boolean;
|
||||
devMode?: boolean;
|
||||
};
|
||||
|
||||
export type LlamaIndexServerOptions = NextAppOptions & {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./next/app/api",
|
||||
"outDir": "./server/app/api",
|
||||
"emitDeclarationOnly": false
|
||||
},
|
||||
"include": ["./next/app/api"],
|
||||
"exclude": ["./next/app/api/chat/route.ts"]
|
||||
}
|
||||
Generated
+243
-32
@@ -181,14 +181,14 @@ importers:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(react-hook-form@7.56.1(react@19.1.0))
|
||||
'@llamaindex/chat-ui':
|
||||
specifier: 0.4.3
|
||||
version: 0.4.3(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.7)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(codemirror@6.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
specifier: 0.4.4
|
||||
version: 0.4.4(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.7)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(codemirror@6.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@llamaindex/env':
|
||||
specifier: ^0.1.29
|
||||
version: 0.1.29
|
||||
specifier: ~0.1.30
|
||||
version: 0.1.30
|
||||
'@llamaindex/workflow':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.3)
|
||||
specifier: ~1.1.3
|
||||
version: 1.1.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)
|
||||
'@radix-ui/react-accordion':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.8(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@@ -341,8 +341,8 @@ importers:
|
||||
specifier: ^7.20.7
|
||||
version: 7.20.7
|
||||
llamaindex:
|
||||
specifier: 0.10.2
|
||||
version: 0.10.2(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.24.3)
|
||||
specifier: ~0.11.0
|
||||
version: 0.11.1(@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.24.3)
|
||||
postcss:
|
||||
specifier: ^8.5.3
|
||||
version: 8.5.3
|
||||
@@ -378,7 +378,7 @@ importers:
|
||||
version: 0.0.4(openapi-types@12.1.3)
|
||||
'@llamaindex/workflow':
|
||||
specifier: 1.1.0
|
||||
version: 1.1.0(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.3)
|
||||
version: 1.1.0(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.5.0
|
||||
@@ -392,6 +392,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^20.10.3
|
||||
version: 20.17.30
|
||||
nodemon:
|
||||
specifier: ^3.1.10
|
||||
version: 3.1.10
|
||||
tsx:
|
||||
specifier: ^4.7.2
|
||||
version: 4.19.3
|
||||
@@ -1177,8 +1180,8 @@ packages:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@llamaindex/chat-ui@0.4.3':
|
||||
resolution: {integrity: sha512-JeS5pzEXZRRzTL70lHRtbWbaPjeZuMMHlqkTNBgYqc2eMM+o9VMOzVKKsXoYfOeE+wunqJd+cBrtkECGEmhRZg==}
|
||||
'@llamaindex/chat-ui@0.4.4':
|
||||
resolution: {integrity: sha512-sE3mJxlmAV3eiIaqOnioUNYYBLJJL8sy2mdFP4xXDf3PfkcEn0wT2Om/ButpVgIXHlPG9lONtv8mzw7hWq2Atg==}
|
||||
peerDependencies:
|
||||
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
@@ -1188,12 +1191,22 @@ packages:
|
||||
'@llamaindex/core': 0.6.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
|
||||
'@llamaindex/cloud@4.0.9':
|
||||
resolution: {integrity: sha512-+tIPghKAzXFvKEPmbJZpcFJgzmxLq/HfSKlJAIinRdrSDBC489oAJsqkt6StxmlFUhWS3ATOtTWpqWZIQ9HtzA==}
|
||||
peerDependencies:
|
||||
'@llama-flow/core': ^0.4.1
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
|
||||
'@llamaindex/core@0.6.1':
|
||||
resolution: {integrity: sha512-OsY8JV2nbfWYjp24HEKDbtM6vOa0TNUreMjDMxUkw5Wet1TxKxjVq0YHY01RoqMbbTJ+Jsxa42FhzTaDV2gMkg==}
|
||||
|
||||
'@llamaindex/core@0.6.2':
|
||||
resolution: {integrity: sha512-e0NH7X1C8yq9sCqj1Ys9jL9rITCXBKfZwmKnTHYxpXorPeuNmlFmuoogahJTwLNsILBDTOVAUexO88SqmAFUrA==}
|
||||
|
||||
'@llamaindex/core@0.6.6':
|
||||
resolution: {integrity: sha512-UplAAxsv3LnU1EP2kPuyqDtRQmQqYtsI00rVDeexOwgqwNTfGddI0yPRrFTBSNe0q2ttBuPsB7D3M7j4UY3yXQ==}
|
||||
|
||||
'@llamaindex/env@0.1.29':
|
||||
resolution: {integrity: sha512-GoITt+QLDNIhu2i1sGsPH8tHH13Gxp4i4ofMFlefrHagytvF761MG7nvTAQVIqP9kxBYk4dnSQ3lQnVPFAfMZQ==}
|
||||
peerDependencies:
|
||||
@@ -1205,6 +1218,17 @@ packages:
|
||||
gpt-tokenizer:
|
||||
optional: true
|
||||
|
||||
'@llamaindex/env@0.1.30':
|
||||
resolution: {integrity: sha512-y6kutMcCevzbmexUgz+HXf7KiZemzAoFEYSjAILfR+cG6FmYSF8XvLbGOB34Kx8mlRi7EI8rZXpezJ5qCqOyZg==}
|
||||
peerDependencies:
|
||||
'@huggingface/transformers': ^3.5.0
|
||||
gpt-tokenizer: ^2.5.0
|
||||
peerDependenciesMeta:
|
||||
'@huggingface/transformers':
|
||||
optional: true
|
||||
gpt-tokenizer:
|
||||
optional: true
|
||||
|
||||
'@llamaindex/node-parser@2.0.2':
|
||||
resolution: {integrity: sha512-pWqEfFRMW5TFzbDyEDWqhipR9P/HBF2C+pEeTroyRACZtKJLbeg3R3e2g4gmfnPxmxQIXkGn2UFK8Io4nwVNOg==}
|
||||
peerDependencies:
|
||||
@@ -1213,6 +1237,14 @@ packages:
|
||||
tree-sitter: ^0.22.0
|
||||
web-tree-sitter: ^0.24.3
|
||||
|
||||
'@llamaindex/node-parser@2.0.6':
|
||||
resolution: {integrity: sha512-8vJ7hsh1FKfAfO90nNe/G+/E2hM31dT/+1+0cPpZp02Ez98Jhwj+vTcCRlvyZznCn6DzHNBuw+uP1wKCqylSnQ==}
|
||||
peerDependencies:
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
tree-sitter: ^0.22.0
|
||||
web-tree-sitter: ^0.24.3
|
||||
|
||||
'@llamaindex/openai@0.2.1':
|
||||
resolution: {integrity: sha512-gSBYL1vMaEihXm5K5gIHpUmvDS78ACPgQPlwf/ZnE82UD/vFNOWOqQbwESJKcygC0lHrwhrKJAuBAzXSNcpznA==}
|
||||
|
||||
@@ -1252,6 +1284,13 @@ packages:
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: ^3.23.8
|
||||
|
||||
'@llamaindex/workflow@1.1.3':
|
||||
resolution: {integrity: sha512-B9wlN4xMtfbnIQW84wTC0GdyIaLGXYnZ4I0LENV2UM/0wqlXKI7YoBTO3cJvi6pO1sPA+K29Tqw822VsGF+GkA==}
|
||||
peerDependencies:
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
zod: ^3.23.8
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
|
||||
|
||||
@@ -2481,6 +2520,9 @@ packages:
|
||||
'@types/responselike@1.0.3':
|
||||
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
|
||||
|
||||
'@types/retry@0.12.2':
|
||||
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
|
||||
|
||||
'@types/retry@0.12.5':
|
||||
resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==}
|
||||
|
||||
@@ -3882,6 +3924,10 @@ packages:
|
||||
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
has-flag@3.0.0:
|
||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3987,6 +4033,9 @@ packages:
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
ignore-by-default@1.0.1:
|
||||
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -4117,6 +4166,10 @@ packages:
|
||||
is-module@1.0.0:
|
||||
resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
|
||||
|
||||
is-network-error@1.1.0:
|
||||
resolution: {integrity: sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
is-number-object@1.1.1:
|
||||
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4387,6 +4440,10 @@ packages:
|
||||
resolution: {integrity: sha512-tUbb+HYaQ+WP1f4NrYnEOIgPPRUePh3sPnOf/wbxTYAyZTnREpSiwCfLUGgYOiIYKK5PS7Oo/eTZCP7jpi7gSA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
llamaindex@0.11.1:
|
||||
resolution: {integrity: sha512-d+sMHc6b/Lw8S1HhTVtR5hgEkg4uCpADpgZDcpHa/QlSQXvCmSkEdI9DOhoTDMhNWWatKnQZADPDuVzkyysQ3w==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
locate-path@5.0.0:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4805,6 +4862,11 @@ packages:
|
||||
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
|
||||
hasBin: true
|
||||
|
||||
nodemon@3.1.10:
|
||||
resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
nopt@5.0.0:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4962,6 +5024,10 @@ packages:
|
||||
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
p-retry@6.2.1:
|
||||
resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==}
|
||||
engines: {node: '>=16.17'}
|
||||
|
||||
p-timeout@3.2.0:
|
||||
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5258,6 +5324,9 @@ packages:
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
pstree.remy@1.1.8:
|
||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||
|
||||
pump@3.0.2:
|
||||
resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==}
|
||||
|
||||
@@ -5503,6 +5572,10 @@ packages:
|
||||
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
retry@0.13.1:
|
||||
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
reusify@1.1.0:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
@@ -5674,6 +5747,10 @@ packages:
|
||||
simple-swizzle@0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
sisteransi@1.0.5:
|
||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||
|
||||
@@ -5832,6 +5909,10 @@ packages:
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5928,6 +6009,10 @@ packages:
|
||||
to-vfile@6.1.0:
|
||||
resolution: {integrity: sha512-BxX8EkCxOAZe+D/ToHdDsJcVI4HqQfmw0tCkp31zf3dNP/XWIAjU4CmeuSwsSoOzOTqHPOL0KUzyZqJplkD0Qw==}
|
||||
|
||||
touch@3.1.1:
|
||||
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||
hasBin: true
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
@@ -6018,6 +6103,9 @@ packages:
|
||||
resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
undefsafe@2.0.5:
|
||||
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
|
||||
|
||||
underscore@1.13.7:
|
||||
resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==}
|
||||
|
||||
@@ -6348,6 +6436,9 @@ packages:
|
||||
zod@3.24.3:
|
||||
resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==}
|
||||
|
||||
zod@3.25.13:
|
||||
resolution: {integrity: sha512-Q8mvk2iWi7rTDfpQBsu4ziE7A6AxgzJ5hzRyRYQkoV3A3niYsXVwDaP1Kbz3nWav6S+VZ6k2OznFn8ZyDHvIrg==}
|
||||
|
||||
zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
@@ -6462,7 +6553,7 @@ snapshots:
|
||||
'@babel/parser': 7.27.0
|
||||
'@babel/template': 7.27.0
|
||||
'@babel/types': 7.27.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
globals: 11.12.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -6891,7 +6982,7 @@ snapshots:
|
||||
'@eslint/config-array@0.19.2':
|
||||
dependencies:
|
||||
'@eslint/object-schema': 2.1.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -6909,7 +7000,7 @@ snapshots:
|
||||
'@eslint/eslintrc@3.3.1':
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
espree: 10.3.0
|
||||
globals: 14.0.0
|
||||
ignore: 5.3.2
|
||||
@@ -7108,12 +7199,13 @@ snapshots:
|
||||
'@lezer/highlight': 1.2.1
|
||||
'@lezer/lr': 1.4.2
|
||||
|
||||
'@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.3)':
|
||||
'@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)':
|
||||
optionalDependencies:
|
||||
next: 15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
p-retry: 6.2.1
|
||||
zod: 3.24.3
|
||||
|
||||
'@llamaindex/chat-ui@0.4.3(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.7)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(codemirror@6.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
'@llamaindex/chat-ui@0.4.4(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.0)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.7)(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(codemirror@6.0.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@codemirror/lang-css': 6.3.1
|
||||
'@codemirror/lang-html': 6.4.9
|
||||
@@ -7173,6 +7265,14 @@ snapshots:
|
||||
'@llamaindex/core': 0.6.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
|
||||
'@llamaindex/cloud@4.0.9(@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3))(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)':
|
||||
dependencies:
|
||||
'@llama-flow/core': 0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
p-retry: 6.2.1
|
||||
zod: 3.25.13
|
||||
|
||||
'@llamaindex/core@0.6.1':
|
||||
dependencies:
|
||||
'@llamaindex/env': 0.1.29
|
||||
@@ -7195,12 +7295,29 @@ snapshots:
|
||||
- '@huggingface/transformers'
|
||||
- gpt-tokenizer
|
||||
|
||||
'@llamaindex/core@0.6.6':
|
||||
dependencies:
|
||||
'@llamaindex/env': 0.1.30
|
||||
'@types/node': 22.15.2
|
||||
magic-bytes.js: 1.12.1
|
||||
zod: 3.24.3
|
||||
zod-to-json-schema: 3.24.5(zod@3.24.3)
|
||||
transitivePeerDependencies:
|
||||
- '@huggingface/transformers'
|
||||
- gpt-tokenizer
|
||||
|
||||
'@llamaindex/env@0.1.29':
|
||||
dependencies:
|
||||
'@aws-crypto/sha256-js': 5.2.0
|
||||
js-tiktoken: 1.0.20
|
||||
pathe: 1.1.2
|
||||
|
||||
'@llamaindex/env@0.1.30':
|
||||
dependencies:
|
||||
'@aws-crypto/sha256-js': 5.2.0
|
||||
js-tiktoken: 1.0.20
|
||||
pathe: 1.1.2
|
||||
|
||||
'@llamaindex/node-parser@2.0.2(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.2
|
||||
@@ -7209,6 +7326,14 @@ snapshots:
|
||||
tree-sitter: 0.22.4
|
||||
web-tree-sitter: 0.24.7
|
||||
|
||||
'@llamaindex/node-parser@2.0.6(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
html-to-text: 9.0.5
|
||||
tree-sitter: 0.22.4
|
||||
web-tree-sitter: 0.24.7
|
||||
|
||||
'@llamaindex/openai@0.2.1':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.1
|
||||
@@ -7286,9 +7411,15 @@ snapshots:
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: 3.24.3
|
||||
|
||||
'@llamaindex/workflow@1.1.0(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.3)':
|
||||
'@llamaindex/workflow@1.0.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(zod@3.24.3)':
|
||||
dependencies:
|
||||
'@llama-flow/core': 0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(zod@3.24.3)
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
zod: 3.24.3
|
||||
|
||||
'@llamaindex/workflow@1.1.0(@llamaindex/core@0.6.2)(@llamaindex/env@0.1.29)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)':
|
||||
dependencies:
|
||||
'@llama-flow/core': 0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)
|
||||
'@llamaindex/core': 0.6.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: 3.24.3
|
||||
@@ -7299,6 +7430,19 @@ snapshots:
|
||||
- p-retry
|
||||
- rxjs
|
||||
|
||||
'@llamaindex/workflow@1.1.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)':
|
||||
dependencies:
|
||||
'@llama-flow/core': 0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3)
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
zod: 3.24.3
|
||||
transitivePeerDependencies:
|
||||
- '@modelcontextprotocol/sdk'
|
||||
- hono
|
||||
- next
|
||||
- p-retry
|
||||
- rxjs
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.0
|
||||
@@ -8495,6 +8639,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 20.17.30
|
||||
|
||||
'@types/retry@0.12.2': {}
|
||||
|
||||
'@types/retry@0.12.5': {}
|
||||
|
||||
'@types/tar@6.1.5':
|
||||
@@ -8531,7 +8677,7 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3)
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
@@ -8546,7 +8692,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3)
|
||||
'@typescript-eslint/utils': 8.31.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
ts-api-utils: 2.1.0(typescript@5.8.3)
|
||||
typescript: 5.8.3
|
||||
@@ -8559,7 +8705,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
minimatch: 9.0.5
|
||||
@@ -8743,7 +8889,7 @@ snapshots:
|
||||
|
||||
agent-base@6.0.2:
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
@@ -9101,7 +9247,7 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
fsevents: 2.3.3
|
||||
|
||||
chownr@1.1.4:
|
||||
optional: true
|
||||
@@ -9297,9 +9443,11 @@ snapshots:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
debug@4.4.0:
|
||||
debug@4.4.0(supports-color@5.5.0):
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
optionalDependencies:
|
||||
supports-color: 5.5.0
|
||||
|
||||
decimal.js-light@2.5.1: {}
|
||||
|
||||
@@ -9665,7 +9813,7 @@ snapshots:
|
||||
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
get-tsconfig: 4.10.0
|
||||
is-bun-module: 2.0.0
|
||||
@@ -9789,7 +9937,7 @@ snapshots:
|
||||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 8.3.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
@@ -10168,6 +10316,8 @@ snapshots:
|
||||
|
||||
has-bigints@1.1.0: {}
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
|
||||
has-flag@4.0.0: {}
|
||||
|
||||
has-property-descriptors@1.0.2:
|
||||
@@ -10278,7 +10428,7 @@ snapshots:
|
||||
https-proxy-agent@5.0.1:
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
@@ -10304,6 +10454,8 @@ snapshots:
|
||||
ieee754@1.2.1:
|
||||
optional: true
|
||||
|
||||
ignore-by-default@1.0.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
@@ -10428,6 +10580,8 @@ snapshots:
|
||||
|
||||
is-module@1.0.0: {}
|
||||
|
||||
is-network-error@1.1.0: {}
|
||||
|
||||
is-number-object@1.1.1:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@@ -10664,7 +10818,7 @@ snapshots:
|
||||
dependencies:
|
||||
chalk: 5.4.1
|
||||
commander: 13.1.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
execa: 8.0.1
|
||||
lilconfig: 3.1.3
|
||||
listr2: 8.3.2
|
||||
@@ -10706,6 +10860,26 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
llamaindex@0.11.1(@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.24.3):
|
||||
dependencies:
|
||||
'@llamaindex/cloud': 4.0.9(@llama-flow/core@0.4.1(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.3))(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)
|
||||
'@llamaindex/core': 0.6.6
|
||||
'@llamaindex/env': 0.1.30
|
||||
'@llamaindex/node-parser': 2.0.6(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)
|
||||
'@llamaindex/workflow': 1.0.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(zod@3.24.3)
|
||||
'@types/lodash': 4.17.16
|
||||
'@types/node': 22.15.2
|
||||
ajv: 8.17.1
|
||||
lodash: 4.17.21
|
||||
magic-bytes.js: 1.12.1
|
||||
transitivePeerDependencies:
|
||||
- '@huggingface/transformers'
|
||||
- '@llama-flow/core'
|
||||
- gpt-tokenizer
|
||||
- tree-sitter
|
||||
- web-tree-sitter
|
||||
- zod
|
||||
|
||||
locate-path@5.0.0:
|
||||
dependencies:
|
||||
p-locate: 4.1.0
|
||||
@@ -11115,7 +11289,7 @@ snapshots:
|
||||
micromark@3.2.0:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.12
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
decode-named-character-reference: 1.1.0
|
||||
micromark-core-commonmark: 1.1.0
|
||||
micromark-factory-space: 1.1.0
|
||||
@@ -11260,6 +11434,19 @@ snapshots:
|
||||
|
||||
node-gyp-build@4.8.4: {}
|
||||
|
||||
nodemon@3.1.10:
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
ignore-by-default: 1.0.1
|
||||
minimatch: 3.1.2
|
||||
pstree.remy: 1.1.8
|
||||
semver: 7.7.1
|
||||
simple-update-notifier: 2.0.0
|
||||
supports-color: 5.5.0
|
||||
touch: 3.1.1
|
||||
undefsafe: 2.0.5
|
||||
|
||||
nopt@5.0.0:
|
||||
dependencies:
|
||||
abbrev: 1.1.1
|
||||
@@ -11430,6 +11617,12 @@ snapshots:
|
||||
|
||||
p-map@2.1.0: {}
|
||||
|
||||
p-retry@6.2.1:
|
||||
dependencies:
|
||||
'@types/retry': 0.12.2
|
||||
is-network-error: 1.1.0
|
||||
retry: 0.13.1
|
||||
|
||||
p-timeout@3.2.0:
|
||||
dependencies:
|
||||
p-finally: 1.0.0
|
||||
@@ -11672,6 +11865,8 @@ snapshots:
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
pstree.remy@1.1.8: {}
|
||||
|
||||
pump@3.0.2:
|
||||
dependencies:
|
||||
end-of-stream: 1.4.4
|
||||
@@ -11992,6 +12187,8 @@ snapshots:
|
||||
|
||||
retry@0.12.0: {}
|
||||
|
||||
retry@0.13.1: {}
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
@@ -12235,6 +12432,10 @@ snapshots:
|
||||
is-arrayish: 0.3.2
|
||||
optional: true
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
dependencies:
|
||||
semver: 7.7.1
|
||||
|
||||
sisteransi@1.0.5: {}
|
||||
|
||||
slash@3.0.0: {}
|
||||
@@ -12393,6 +12594,10 @@ snapshots:
|
||||
client-only: 0.0.1
|
||||
react: 19.1.0
|
||||
|
||||
supports-color@5.5.0:
|
||||
dependencies:
|
||||
has-flag: 3.0.0
|
||||
|
||||
supports-color@7.2.0:
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
@@ -12489,6 +12694,8 @@ snapshots:
|
||||
is-buffer: 2.0.5
|
||||
vfile: 4.2.1
|
||||
|
||||
touch@3.1.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tree-sitter@0.22.4:
|
||||
@@ -12593,6 +12800,8 @@ snapshots:
|
||||
has-symbols: 1.1.0
|
||||
which-boxed-primitive: 1.1.1
|
||||
|
||||
undefsafe@2.0.5: {}
|
||||
|
||||
underscore@1.13.7: {}
|
||||
|
||||
undici-types@5.26.5: {}
|
||||
@@ -12820,7 +13029,7 @@ snapshots:
|
||||
vite-node@2.1.9(@types/node@20.17.30)(lightningcss@1.29.2):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 1.1.2
|
||||
vite: 5.4.18(@types/node@20.17.30)(lightningcss@1.29.2)
|
||||
@@ -12855,7 +13064,7 @@ snapshots:
|
||||
'@vitest/spy': 2.1.9
|
||||
'@vitest/utils': 2.1.9
|
||||
chai: 5.2.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
expect-type: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
pathe: 1.1.2
|
||||
@@ -12886,7 +13095,7 @@ snapshots:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
commander: 9.5.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.0(supports-color@5.5.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -13023,4 +13232,6 @@ snapshots:
|
||||
|
||||
zod@3.24.3: {}
|
||||
|
||||
zod@3.25.13: {}
|
||||
|
||||
zwitch@2.0.4: {}
|
||||
|
||||
@@ -72,7 +72,7 @@ app = LlamaIndexServer(
|
||||
|
||||
The LlamaIndexServer accepts the following configuration parameters:
|
||||
|
||||
- `workflow_factory`: A callable that creates a workflow instance for each request
|
||||
- `workflow_factory`: A callable that creates a workflow instance for each request. See [Workflow factory contract](#workflow-factory-contract) for more details.
|
||||
- `logger`: Optional logger instance (defaults to uvicorn logger)
|
||||
- `use_default_routers`: Whether to include default routers (chat, static file serving)
|
||||
- `env`: Environment setting ('dev' enables CORS and UI by default)
|
||||
@@ -83,10 +83,36 @@ The LlamaIndexServer accepts the following configuration parameters:
|
||||
- `ui_path`: Path for downloaded UI static files (default: ".ui")
|
||||
- `component_dir`: The directory for custom UI components rendering events emitted by the workflow. The default is None, which does not render custom UI components.
|
||||
- `llamacloud_index_selector`: Whether to show the LlamaCloud index selector in the chat UI (default: False). Requires `LLAMA_CLOUD_API_KEY` to be set.
|
||||
- `dev_mode`: When enabled, you can update workflow code in the UI and see the changes immediately. It's currently in beta and only supports updating workflow code at `app/workflow.py`. You might also need to set `env="dev"` and start the server with the reload feature enabled.
|
||||
- `verbose`: Enable verbose logging
|
||||
- `api_prefix`: API route prefix (default: "/api")
|
||||
- `server_url`: The deployment URL of the server (default is None)
|
||||
|
||||
## Workflow factory contract
|
||||
|
||||
The `workflow_factory` provided will be called for each chat request to initialize a new workflow instance. Additionally, we provide the [ChatRequest](https://github.com/run-llama/create-llama/blob/afe9e9fc16427d20e1dfb635a45e7ed4b46285cb/python/llama-index-server/llama_index/server/api/models.py#L32) object, which includes the request information that is helpful for initializing the workflow. For example:
|
||||
```python
|
||||
def create_workflow(chat_request: ChatRequest) -> Workflow:
|
||||
# using messages from the chat request to initialize the workflow
|
||||
return MyCustomWorkflow(chat_request.messages)
|
||||
```
|
||||
|
||||
Your workflow will be executed once for each chat request with the following input parameters are included in workflow's `StartEvent`:
|
||||
- `user_msg` [str]: The current user message
|
||||
- `chat_history` [list[[ChatMessage](https://docs.llamaindex.ai/en/stable/api_reference/prompts/#llama_index.core.prompts.ChatMessage)]]: All the previous messages of the conversation
|
||||
|
||||
Example:
|
||||
```python
|
||||
@step
|
||||
def handle_start_event(ev: StartEvent) -> MyNextEvent:
|
||||
user_msg = ev.user_msg
|
||||
chat_history = ev.chat_history
|
||||
...
|
||||
```
|
||||
|
||||
Your workflows can emit `UIEvent` events to render [Custom UI Components](https://github.com/run-llama/create-llama/blob/main/python/llama-index-server/docs/custom_ui_component.md) in the chat UI to improve the user experience.
|
||||
Furthermore, you can send `ArtifactEvent` events to render code or document [Artifacts](https://github.com/run-llama/create-llama/blob/main/python/llama-index-server/docs/custom_artifact_event.md) in a dedicated Canvas panel in the chat UI.
|
||||
|
||||
## Default Routers and Features
|
||||
|
||||
### Chat Router
|
||||
@@ -107,11 +133,6 @@ When enabled, the server provides a chat interface at the root path (`/`) with:
|
||||
- Real-time chat interface
|
||||
- API endpoint integration
|
||||
|
||||
### Custom UI Components
|
||||
|
||||
You can add custom UI components for your workflow by providing `component_dir` config and adding custom .jsx or .tsx files to the directory.
|
||||
See [Custom UI Components](https://github.com/run-llama/create-llama/blob/main/llama-index-server/docs/custom_ui_component.md) for more details.
|
||||
|
||||
## Development Mode
|
||||
|
||||
In development mode (`env="dev"`), the server:
|
||||
@@ -120,6 +141,20 @@ In development mode (`env="dev"`), the server:
|
||||
- Automatically includes the chat UI
|
||||
- Provides more verbose logging
|
||||
|
||||
### Workflow Editor (Beta)
|
||||
|
||||
In development mode, you can set `dev_mode` to `True` in the UI configuration to enable the workflow editor, which allows you to edit the workflow code directly in the browser.
|
||||
|
||||
```python
|
||||
app = LlamaIndexServer(
|
||||
workflow_factory=create_workflow,
|
||||
env="dev",
|
||||
ui_config={"dev_mode": True},
|
||||
)
|
||||
```
|
||||
|
||||
**Note**: The workflow editor is currently in beta and only supports updating LlamaIndexServer projects created with [create-llama](https://github.com/run-llama/create-llama/). You also need to start the server via `fastapi dev` so that the server can hot reload the workflow code.
|
||||
|
||||
## API Endpoints
|
||||
|
||||
The server provides the following default endpoints:
|
||||
@@ -130,11 +165,10 @@ The server provides the following default endpoints:
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Always provide a workflow factory that creates fresh workflow instances
|
||||
2. Use environment variables for sensitive configuration
|
||||
3. Enable verbose logging during development
|
||||
4. Configure CORS appropriately for your deployment environment
|
||||
5. Use starter questions to guide users in the chat UI
|
||||
1. Use environment variables for sensitive configuration
|
||||
2. Enable verbose logging during development
|
||||
3. Configure CORS appropriately for your deployment environment
|
||||
4. Use starter questions to guide users in the chat UI
|
||||
|
||||
## Getting Started with a New Project
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Sending Artifacts to the UI
|
||||
|
||||
In addition to UI events for custom components, LlamaIndex Server supports a special `ArtifactEvent` to send structured data like generated documents or code snippets to the UI. These artifacts are displayed in a dedicated "Canvas" panel in the chat interface.
|
||||
|
||||
## Artifact Event Structure
|
||||
|
||||
To send an artifact, your workflow needs to emit an event with `type: "artifact"`. The `data` payload of this event should include:
|
||||
|
||||
- `type`: An `ArtifactType` enum indicating the type of artifact (e.g., `ArtifactType.DOCUMENT`, `ArtifactType.CODE`).
|
||||
- `created_at`: A timestamp (e.g., `int(time.time())`) indicating when the artifact was created.
|
||||
- `data`: An object containing the specific details of the artifact. The structure of this object depends on the artifact `type`. For example, `DocumentArtifactData` or `CodeArtifactData`.
|
||||
|
||||
## Defining and Sending an ArtifactEvent
|
||||
|
||||
First, import the necessary classes:
|
||||
|
||||
```python
|
||||
import time
|
||||
from llama_index.server.api.models import (
|
||||
Artifact,
|
||||
ArtifactEvent,
|
||||
ArtifactType,
|
||||
DocumentArtifactData,
|
||||
# CodeArtifactData, # Import if sending code artifacts
|
||||
)
|
||||
```
|
||||
|
||||
Then, within your workflow logic, use `ctx.write_event_to_stream` to emit the event. Here's an example of sending a document artifact, taken from [document_workflow.py](/python/llama-index-server/examples/artifact/document_workflow.py):
|
||||
|
||||
```python
|
||||
# Assuming 'ctx' is the workflow Context and 'content' is a markdown string
|
||||
|
||||
ctx.write_event_to_stream(
|
||||
ArtifactEvent(
|
||||
data=Artifact(
|
||||
type=ArtifactType.DOCUMENT,
|
||||
created_at=int(time.time()),
|
||||
data=DocumentArtifactData(
|
||||
title="My cooking recipes",
|
||||
content=content,
|
||||
type="markdown",
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
This will send the artifact to the LlamaIndex Server UI, where it will be rendered in the Canvas panel by a renderer depending on the artifact type. For `ArtifactType.DOCUMENT`, this uses a `DocumentArtifactViewer`.
|
||||
|
||||
## Available Artifact Types
|
||||
|
||||
LlamaIndex Server currently supports the following artifact types:
|
||||
|
||||
- `ArtifactType.DOCUMENT`: For text-based documents like Markdown or HTML.
|
||||
- `data` should be an instance of `DocumentArtifactData` which includes `title: str`, `content: str`, and `type: Literal["markdown", "html"]`.
|
||||
- `ArtifactType.CODE`: For code snippets.
|
||||
- `data` should be an instance of `CodeArtifactData` which includes `title: str`, `code: str`, and `language: str`.
|
||||
|
||||
Ensure you provide the correct data model corresponding to the `ArtifactType` you are sending. You can find these data models in `llama_index.server.api.models`.
|
||||
@@ -0,0 +1,38 @@
|
||||
# A simple chat app
|
||||
|
||||
This guide explains how to set up and use the LlamaIndex server with a simple chatbot agent.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [uv](https://github.com/astral-sh/uv) installed (a fast Python package manager and runner)
|
||||
- An OpenAI API key
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Set the OpenAI API Key**
|
||||
|
||||
Export your OpenAI API key as an environment variable:
|
||||
|
||||
```sh
|
||||
export OPENAI_API_KEY=your_openai_api_key_here
|
||||
```
|
||||
|
||||
2. **Run the Server Using uv**
|
||||
|
||||
Start the server with the following command:
|
||||
|
||||
```sh
|
||||
uv run workflow.py
|
||||
```
|
||||
|
||||
This will launch the FastAPI server using the workflow defined in `main.py`.
|
||||
|
||||
3. **Access the Application**
|
||||
|
||||
Open your browser and go to:
|
||||
|
||||
```
|
||||
http://localhost:8000
|
||||
```
|
||||
|
||||
You will see the LlamaIndex chat app UI, where you can interact with the agent.
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing import Optional
|
||||
|
||||
from llama_index.core.agent.workflow import AgentWorkflow
|
||||
from llama_index.core.settings import Settings
|
||||
from llama_index.llms.openai import OpenAI
|
||||
from llama_index.server.api.models import ChatRequest
|
||||
|
||||
|
||||
def create_workflow(chat_request: Optional[ChatRequest] = None) -> AgentWorkflow:
|
||||
return AgentWorkflow.from_tools_or_functions(
|
||||
tools_or_functions=[],
|
||||
llm=Settings.llm or OpenAI(model="gpt-4o-mini"),
|
||||
system_prompt="You are a helpful assistant that can tell a joke about Llama.",
|
||||
)
|
||||
@@ -0,0 +1,29 @@
|
||||
from app.workflow import create_workflow
|
||||
from fastapi import FastAPI
|
||||
|
||||
from llama_index.server import LlamaIndexServer, UIConfig
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
app = LlamaIndexServer(
|
||||
workflow_factory=create_workflow,
|
||||
ui_config=UIConfig(
|
||||
app_title="Artifact",
|
||||
starter_questions=[
|
||||
"Tell me a funny joke.",
|
||||
"Tell me some jokes about AI.",
|
||||
],
|
||||
component_dir="components",
|
||||
dev_mode=True, # To show the dev UI, should disable this in production
|
||||
),
|
||||
)
|
||||
return app
|
||||
|
||||
|
||||
app = create_app()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
|
||||
@@ -1,4 +1,9 @@
|
||||
from llama_index.server.api.routers.chat import chat_router
|
||||
from llama_index.server.api.routers.ui import custom_components_router
|
||||
from llama_index.server.api.routers.dev import dev_router
|
||||
|
||||
__all__ = ["chat_router", "custom_components_router"]
|
||||
__all__ = [
|
||||
"chat_router",
|
||||
"custom_components_router",
|
||||
"dev_router",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_index.server.settings import server_settings
|
||||
from llama_index.server.utils.workflow_validation import validate_workflow_file
|
||||
|
||||
|
||||
class WorkflowFile(BaseModel):
|
||||
last_modified: int
|
||||
file_path: str = Field(
|
||||
default="app/workflow.py",
|
||||
description="Relative path to the workflow file",
|
||||
)
|
||||
content: str
|
||||
|
||||
|
||||
class WorkflowFileUpdate(BaseModel):
|
||||
content: str
|
||||
file_path: str = Field(
|
||||
default="app/workflow.py",
|
||||
description="Relative path to the workflow file",
|
||||
)
|
||||
|
||||
|
||||
class WorkflowValidationResult(BaseModel):
|
||||
valid: bool
|
||||
error: str
|
||||
|
||||
|
||||
def dev_router() -> APIRouter:
|
||||
# Use a prefix here to avoid conflicts with other routers
|
||||
# but we probably don't need to do this
|
||||
router = APIRouter(prefix="/dev", tags=["dev"])
|
||||
|
||||
default_workflow_file_path = "app/workflow.py"
|
||||
|
||||
@router.get("/files/workflow")
|
||||
async def get_workflow_file() -> WorkflowFile:
|
||||
"""
|
||||
Fetch the current workflow code
|
||||
"""
|
||||
# Check if the file exists
|
||||
if not os.path.exists(default_workflow_file_path):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Dev mode is currently in beta. It only supports updating workflow file at 'app/workflow.py'",
|
||||
)
|
||||
stat = os.stat(default_workflow_file_path)
|
||||
with open(default_workflow_file_path, "r") as f:
|
||||
return WorkflowFile(
|
||||
last_modified=int(stat.st_mtime),
|
||||
file_path=default_workflow_file_path,
|
||||
content=f.read(),
|
||||
)
|
||||
|
||||
@router.post("/files/workflow/validate")
|
||||
async def validate_workflow(file: WorkflowFileUpdate) -> WorkflowValidationResult:
|
||||
"""
|
||||
Validate the current workflow code
|
||||
"""
|
||||
try:
|
||||
if file.file_path != default_workflow_file_path:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"Updating {file.file_path} is not allowed"
|
||||
)
|
||||
validate_workflow_file(
|
||||
workflow_content=file.content,
|
||||
factory_signature=server_settings.workflow_factory_signature,
|
||||
)
|
||||
return WorkflowValidationResult(valid=True, error="")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
@router.put("/files/workflow")
|
||||
async def put_workflow_file(update: WorkflowFileUpdate) -> None:
|
||||
"""
|
||||
Update the current workflow code
|
||||
"""
|
||||
# Validations
|
||||
if update.file_path != default_workflow_file_path:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"Updating {update.file_path} is not allowed"
|
||||
)
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as tmp:
|
||||
tmp.write(update.content)
|
||||
tmp_path = tmp.name
|
||||
try:
|
||||
# Validate workflow file using the actual callable name from the workflow_factory
|
||||
factory_func_name = server_settings.workflow_factory_signature
|
||||
validate_workflow_file(
|
||||
workflow_path=tmp_path, factory_signature=factory_func_name
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
# If all checks pass, overwrite the real file
|
||||
with open(default_workflow_file_path, "w") as f:
|
||||
f.write(update.content)
|
||||
|
||||
return router
|
||||
@@ -5,7 +5,7 @@ from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
CHAT_UI_VERSION = "0.1.6"
|
||||
CHAT_UI_VERSION = "0.2.1"
|
||||
|
||||
|
||||
def download_chat_ui(
|
||||
|
||||
@@ -10,7 +10,11 @@ from fastapi.staticfiles import StaticFiles
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_index.core.workflow import Workflow
|
||||
from llama_index.server.api.routers import chat_router, custom_components_router
|
||||
from llama_index.server.api.routers import (
|
||||
chat_router,
|
||||
custom_components_router,
|
||||
dev_router,
|
||||
)
|
||||
from llama_index.server.chat_ui import download_chat_ui
|
||||
from llama_index.server.settings import server_settings
|
||||
|
||||
@@ -33,6 +37,9 @@ class UIConfig(BaseModel):
|
||||
component_dir: Optional[str] = Field(
|
||||
default=None, description="The directory to custom UI components code"
|
||||
)
|
||||
dev_mode: bool = Field(
|
||||
default=False, description="Whether to enable the UI dev mode"
|
||||
)
|
||||
|
||||
def get_config_content(self) -> str:
|
||||
return json.dumps(
|
||||
@@ -46,6 +53,7 @@ class UIConfig(BaseModel):
|
||||
"COMPONENTS_API": f"{server_settings.api_url}/components"
|
||||
if self.component_dir
|
||||
else None,
|
||||
"DEV_MODE": self.dev_mode,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
@@ -100,24 +108,33 @@ class LlamaIndexServer(FastAPI):
|
||||
server_settings.set_url(server_url)
|
||||
if api_prefix:
|
||||
server_settings.set_api_prefix(api_prefix)
|
||||
|
||||
if self.use_default_routers:
|
||||
self.add_default_routers()
|
||||
server_settings.set_workflow_factory(workflow_factory.__name__)
|
||||
|
||||
if str(env).lower() == "dev":
|
||||
self.allow_cors("*")
|
||||
if self.ui_config.enabled is None:
|
||||
self.ui_config.enabled = True
|
||||
|
||||
else:
|
||||
if self.ui_config.enabled and self.ui_config.dev_mode:
|
||||
raise ValueError(
|
||||
"UI dev mode requires the environment variable for LlamaIndexServer to be set to 'dev' and start the FastAPI app in dev mode."
|
||||
)
|
||||
if self.ui_config.enabled is None:
|
||||
self.ui_config.enabled = False
|
||||
|
||||
# Routers
|
||||
if self.use_default_routers:
|
||||
self.add_default_routers()
|
||||
|
||||
# Should mount ui at the end
|
||||
if self.ui_config.enabled:
|
||||
self.mount_ui()
|
||||
|
||||
# Default routers
|
||||
def add_default_routers(self) -> None:
|
||||
self.add_chat_router()
|
||||
if self.ui_config.enabled and self.ui_config.dev_mode:
|
||||
self.include_router(dev_router(), prefix=server_settings.api_prefix)
|
||||
self.mount_data_dir()
|
||||
self.mount_output_dir()
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ class ServerSettings(BaseSettings):
|
||||
default="/api",
|
||||
description="The prefix for the API endpoints",
|
||||
)
|
||||
workflow_factory_signature: str = Field(
|
||||
default="",
|
||||
description="The signature of the workflow factory function",
|
||||
)
|
||||
|
||||
@property
|
||||
def file_server_url_prefix(self) -> str:
|
||||
@@ -40,6 +44,9 @@ class ServerSettings(BaseSettings):
|
||||
self.api_prefix = v
|
||||
self.validate_api_prefix(v) # type: ignore
|
||||
|
||||
def set_workflow_factory(self, v: str) -> None:
|
||||
self.workflow_factory_signature = v
|
||||
|
||||
class Config:
|
||||
env_file_encoding = "utf-8"
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Utilities for validating workflow.py files (syntax and import checks only).
|
||||
"""
|
||||
|
||||
import ast
|
||||
import importlib.util
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def validate_workflow_file(
|
||||
workflow_path: Optional[str] = None,
|
||||
workflow_content: Optional[str] = None,
|
||||
factory_signature: Optional[str] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Validate that the workflow file is syntactically correct, can be imported, and defines a callable factory function with the given name.
|
||||
Raises an exception if invalid.
|
||||
"""
|
||||
if workflow_path is None and workflow_content is None:
|
||||
raise ValueError("Either workflow_path or workflow_content must be provided")
|
||||
|
||||
# 1. Syntax check
|
||||
if workflow_path is not None:
|
||||
with open(workflow_path, "r") as f:
|
||||
content = f.read()
|
||||
else:
|
||||
if workflow_content is None:
|
||||
raise ValueError(
|
||||
"workflow_content must be provided if workflow_path is not specified"
|
||||
)
|
||||
content = workflow_content
|
||||
try:
|
||||
ast.parse(content)
|
||||
except SyntaxError as e:
|
||||
raise ValueError(f"Syntax error in workflow: {e}")
|
||||
|
||||
# 2. Import check (will catch missing modules, etc.)
|
||||
spec = importlib.util.spec_from_file_location("workflow", workflow_path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ValueError(f"Could not load module specification for {workflow_path}")
|
||||
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
try:
|
||||
spec.loader.exec_module(mod)
|
||||
except Exception as e:
|
||||
raise ValueError(f"Import error: {e}")
|
||||
|
||||
# 3. Contract validation: require the given factory function name
|
||||
if factory_signature:
|
||||
if not hasattr(mod, factory_signature):
|
||||
raise ValueError(f"Missing required function: '{factory_signature}'")
|
||||
obj = getattr(mod, factory_signature)
|
||||
if not callable(obj):
|
||||
raise ValueError(f"'{factory_signature}' is not callable")
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "llama-index-server"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
description = "llama-index fastapi server"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
Generated
+2678
-2677
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user