Compare commits

..

28 Commits

Author SHA1 Message Date
leehuwuj 193298ed8d bump chat-ui for eject project 2025-06-06 15:31:18 +07:00
leehuwuj a0af27a8ac bump chat-ui 2025-06-06 15:24:52 +07:00
leehuwuj 70b808bfcd add changeset 2025-06-06 11:31:51 +07:00
leehuwuj f71353f604 chore: fix issues 2025-06-06 10:48:31 +07:00
leehuwuj 5870403e7a fix unstoppable 2025-06-06 10:43:00 +07:00
leehuwuj 4fdaa8d4f0 Enhance file handling in workflows by updating README and example files. Introduce a new workflowFactory structure to support file attachments, and improve the extractFileAttachments function for better clarity and usability. Update descriptions in tools to reflect changes in file ID handling. 2025-06-06 10:30:56 +07:00
leehuwuj cd1ed64cef Refactor file handling in workflows by updating the create_file_tool function to accept file attachments directly. Introduce a new ServerFileResponse model for better file response handling. Update chat router to utilize the new FileUpload model for file uploads. Clean up imports and ensure consistent file attachment processing across workflows. 2025-06-06 09:55:20 +07:00
leehuwuj e6183c765f Enhance README with detailed instructions for file upload in chat UI. Update custom workflow to handle file attachments and modify chat router to remove unused attachment handling. Refactor create_workflow to pass attachments from chat request. 2025-06-06 09:28:10 +07:00
leehuwuj 24d7e4c1dc update example 2025-06-05 16:25:02 +07:00
leehuwuj 2929913f2e Merge remote-tracking branch 'origin/main' into lee/private-file 2025-06-05 14:46:54 +07:00
leehuwuj b6174532a4 update doc 2025-06-05 14:14:28 +07:00
leehuwuj 656280fb2f Enhance README and add file upload examples for LlamaIndex Server. Updated instructions for running examples and added new workflows for handling uploaded files. Included detailed notes on using file attachments in workflows. 2025-06-05 14:09:47 +07:00
leehuwuj 0198c00045 support agent workflow for ts 2025-06-05 13:28:55 +07:00
leehuwuj 36cdd6d0d0 remove redundant change 2025-06-05 11:22:01 +07:00
leehuwuj 89c40362de feat: Add file upload support and enhance chat workflow in LlamaIndexServer 2025-06-05 11:16:52 +07:00
leehuwuj 2105e26c24 Merge remote-tracking branch 'origin/main' into lee/private-file 2025-06-05 09:51:05 +07:00
leehuwuj 7d50b60673 fix example, improve model 2025-06-05 09:50:37 +07:00
leehuwuj ee3057e76c attach file to user message 2025-06-05 09:22:16 +07:00
leehuwuj 095badc813 update example 2025-06-04 15:30:04 +07:00
leehuwuj 07ab5532e7 refactor: Simplify file storage logic in helpers.ts 2025-06-04 12:48:03 +07:00
Marcus Schiesser 8ebafc9f74 add get_file to fileservice 2025-06-04 12:41:33 +07:00
leehuwuj 667da52300 feat: Add file upload support to LlamaIndexServer TS 2025-06-04 12:39:34 +07:00
leehuwuj 99cef089ec fix test 2025-06-04 09:59:45 +07:00
leehuwuj c94ef19941 add readme example 2025-06-03 16:57:47 +07:00
leehuwuj e873b639e5 Merge remote-tracking branch 'origin/main' into lee/private-file 2025-06-03 16:34:49 +07:00
leehuwuj 91a4173400 add process base64 and update examples 2025-06-03 16:24:38 +07:00
leehuwuj d64ce7479e feat: Add private file handling and upload support in FastAPI
- Introduced `main.py` to set up the FastAPI application with file upload capabilities.
- Created `workflow.py` to manage file reading and tool creation for uploaded files.
- Updated `server.py` to include upload API configuration.
- Modified chat router to handle file uploads and return server file metadata.
- Refactored chat models to support new file handling structure.
- Enhanced file service to manage private file storage and retrieval.
2025-06-03 15:42:39 +07:00
leehuwuj f99d8ac6bb init private support for python BE 2025-06-03 08:29:40 +07:00
61 changed files with 119 additions and 1821 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@create-llama/llama-index-server": patch
---
Add support for upload file
+7
View File
@@ -0,0 +1,7 @@
---
"create-llama": patch
"@llamaindex/server": patch
"@create-llama/llama-index-server": patch
---
feat: bump chat-ui with inline artifact
+5
View File
@@ -0,0 +1,5 @@
---
"@llamaindex/server": patch
---
Add support for chat upload file
-13
View File
@@ -1,18 +1,5 @@
# create-llama
## 0.5.22
### Patch Changes
- e2486eb: feat: support human in the loop for TS
## 0.5.21
### Patch Changes
- af9ad3c: feat: show document artifact after generating report
- a543a27: feat: bump chat-ui with inline artifact
## 0.5.20
### Patch Changes
@@ -20,7 +20,6 @@ const useCases: TemplateUseCase[] = [
"financial_report",
"code_generator",
"document_generator",
"hitl",
];
const dataSource: string = process.env.DATASOURCE
? process.env.DATASOURCE
@@ -27,7 +27,6 @@ const templateUseCases = [
"financial_report",
"deep_research",
"code_generator",
// "hitl",
];
const ejectDir = "next";
@@ -3,7 +3,6 @@ import { exec } from "child_process";
import fs from "fs";
import path from "path";
import util from "util";
import { NO_DATA_USE_CASES } from "../../helpers/constant";
import {
TemplateFramework,
TemplateType,
@@ -26,7 +25,6 @@ const useCases: TemplateUseCase[] = [
"financial_report",
"code_generator",
"document_generator",
"hitl",
];
const dataSource: string = process.env.DATASOURCE
? process.env.DATASOURCE
@@ -85,7 +83,7 @@ test.describe("Test resolve TS dependencies", () => {
});
});
// Skipping llamacloud for the use case doesn't use index.
if (!useCase || !NO_DATA_USE_CASES.includes(useCase)) {
if (useCase !== "code_generator" && useCase !== "document_generator") {
test(`llamaParse - ${optionDescription}`, async () => {
await runTest({
templateType: templateType,
@@ -1,15 +1,6 @@
import { TemplateUseCase } from "./types";
export const COMMUNITY_OWNER = "run-llama";
export const COMMUNITY_REPO = "create_llama_projects";
export const LLAMA_PACK_OWNER = "run-llama";
export const LLAMA_PACK_REPO = "llama_index";
export const LLAMA_PACK_FOLDER = "llama-index-packs";
export const LLAMA_PACK_FOLDER_PATH = `${LLAMA_PACK_OWNER}/${LLAMA_PACK_REPO}/main/${LLAMA_PACK_FOLDER}`;
// these use cases don't have data folder, so no need to run generate and no need to getIndex
export const NO_DATA_USE_CASES: TemplateUseCase[] = [
"code_generator",
"document_generator",
"hitl",
];
+1 -3
View File
@@ -4,7 +4,6 @@ import path from "path";
import picocolors, { cyan } from "picocolors";
import fsExtra from "fs-extra";
import { NO_DATA_USE_CASES } from "./constant";
import { writeLoadersConfig } from "./datasources";
import { createBackendEnvFile, createFrontendEnvFile } from "./env-variables";
import { PackageManager } from "./get-pkg-manager";
@@ -99,9 +98,8 @@ async function generateContextData(
}
} else {
console.log(`Running ${runGenerate} to generate the context data.`);
const shouldRunGenerate =
!useCase || !NO_DATA_USE_CASES.includes(useCase);
useCase !== "code_generator" && useCase !== "document_generator"; // Artifact use case doesn't use index.
if (shouldRunGenerate) {
await callPackageManager(packageManager, true, ["run", "generate"]);
+1 -2
View File
@@ -59,8 +59,7 @@ export type TemplateUseCase =
| "contract_review"
| "agentic_rag"
| "code_generator"
| "document_generator"
| "hitl";
| "document_generator";
// Config for both file and folder
export type FileSourceConfig =
| {
+1 -2
View File
@@ -4,7 +4,6 @@ import path from "path";
import { bold, cyan, red, yellow } from "picocolors";
import { assetRelocator, copy } from "../helpers/copy";
import { callPackageManager } from "../helpers/install";
import { NO_DATA_USE_CASES } from "./constant";
import { templatesDir } from "./dir";
import { PackageManager } from "./get-pkg-manager";
import { InstallTemplateArgs, ModelProvider, TemplateVectorDB } from "./types";
@@ -84,7 +83,7 @@ const installLlamaIndexServerTemplate = async ({
}
// Simplify use case code
if (useCase && NO_DATA_USE_CASES.includes(useCase)) {
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"));
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-llama",
"version": "0.5.22",
"version": "0.5.20",
"description": "Create LlamaIndex-powered apps with one command",
"keywords": [
"rag",
+2 -17
View File
@@ -1,5 +1,4 @@
import prompts from "prompts";
import { NO_DATA_USE_CASES } from "../helpers/constant";
import { EXAMPLE_10K_SEC_FILES, EXAMPLE_FILE } from "../helpers/datasources";
import { askModelConfig } from "../helpers/providers";
import { getTools } from "../helpers/tools";
@@ -12,8 +11,7 @@ type AppType =
| "financial_report"
| "deep_research"
| "code_generator"
| "document_generator"
| "hitl";
| "document_generator";
type SimpleAnswers = {
appType: AppType;
@@ -59,12 +57,6 @@ export const askSimpleQuestions = async (
value: "document_generator",
description: "Build a OpenAI canvas-styled document generator.",
},
{
title: "Human in the Loop",
value: "hitl",
description:
"Build a CLI command workflow that is reviewed by a human before execution",
},
],
},
questionHandlers,
@@ -89,8 +81,7 @@ export const askSimpleQuestions = async (
);
language = newLanguage;
const shouldAskLlamaCloud = !NO_DATA_USE_CASES.includes(appType);
if (shouldAskLlamaCloud) {
if (appType !== "code_generator" && appType !== "document_generator") {
const { useLlamaCloud: newUseLlamaCloud } = await prompts(
{
type: "toggle",
@@ -179,12 +170,6 @@ const convertAnswers = async (
tools: [],
modelConfig: MODEL_GPT41,
},
hitl: {
template: "llamaindexserver",
dataSources: [],
tools: [],
modelConfig: MODEL_GPT41,
},
};
const results = lookup[answers.appType];
@@ -1,95 +0,0 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { JSONValue, useChatUI } from "@llamaindex/chat-ui";
import React, { FC, useState } from "react";
import { z } from "zod";
// This schema is equivalent to the CLICommand model defined in events.py
const CLIInputEventSchema = z.object({
command: z.string(),
});
type CLIInputEvent = z.infer<typeof CLIInputEventSchema>;
const CLIHumanInput: FC<{
events: JSONValue[];
}> = ({ events }) => {
const inputEvent = (events || [])
.map((ev) => {
const parseResult = CLIInputEventSchema.safeParse(ev);
return parseResult.success ? parseResult.data : null;
})
.filter((ev): ev is CLIInputEvent => ev !== null)
.at(-1);
const { append } = useChatUI();
const [confirmedValue, setConfirmedValue] = useState<boolean | null>(null);
const [editableCommand, setEditableCommand] = useState<string | undefined>(
inputEvent?.command,
);
// Update editableCommand if inputEvent changes (e.g. new event comes in)
React.useEffect(() => {
setEditableCommand(inputEvent?.command);
}, [inputEvent?.command]);
const handleConfirm = () => {
append({
content: "Yes",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: true,
command: editableCommand, // Use editable command
},
},
],
});
setConfirmedValue(true);
};
const handleCancel = () => {
append({
content: "No",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: false,
command: inputEvent?.command,
},
},
],
});
setConfirmedValue(false);
};
return (
<Card className="my-4">
<CardContent className="pt-6">
<p className="text-sm text-gray-700">
Do you want to execute the following command?
</p>
<input
disabled
type="text"
value={editableCommand || ""}
onChange={(e) => setEditableCommand(e.target.value)}
className="my-2 w-full overflow-x-auto rounded border border-gray-300 bg-gray-100 p-3 font-mono text-xs text-gray-800"
/>
</CardContent>
{confirmedValue === null ? (
<CardFooter className="flex justify-end gap-2">
<>
<Button onClick={handleConfirm}>Yes</Button>
<Button onClick={handleCancel}>No</Button>
</>
</CardFooter>
) : null}
</Card>
);
};
export default CLIHumanInput;
@@ -23,18 +23,7 @@ from llama_index.core.workflow import (
Workflow,
step,
)
from llama_index.server.api.models import (
ArtifactEvent,
ArtifactType,
ChatRequest,
SourceNodesEvent,
UIEvent,
Artifact,
DocumentArtifactData,
DocumentArtifactSource,
)
import time
from llama_index.server.utils.stream import write_response_to_stream
from llama_index.server.api.models import ChatRequest, SourceNodesEvent, UIEvent
from pydantic import BaseModel, Field
logger = logging.getLogger("uvicorn")
@@ -376,31 +365,8 @@ class DeepResearchWorkflow(Workflow):
user_request=self.user_request,
stream=self.stream,
)
final_response = await write_response_to_stream(res, ctx)
ctx.write_event_to_stream(
ArtifactEvent(
data=Artifact(
type=ArtifactType.DOCUMENT,
created_at=int(time.time()),
data=DocumentArtifactData(
title="DeepResearch Report",
content=final_response,
type="markdown",
sources=[
DocumentArtifactSource(
id=node.id_,
)
for node in self.context_nodes
],
),
),
)
)
return StopEvent(
result="",
result=res,
)
@@ -1,109 +0,0 @@
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
This example shows how to use the LlamaIndexServer with a human in the loop. It allows you to start CLI commands that are reviewed by a human before execution.
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": "Show me the files in the current directory" }] }'
```
## How does HITL work?
### Events
The human-in-the-loop approach used here is based on a simple idea: the workflow pauses and waits for a human response before proceeding to the next step.
To do this, you will need to implement two custom events:
- [HumanInputEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is used to request input from the user.
- [HumanResponseEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is sent to the workflow to resume execution with input from the user.
In this example, we have implemented these two custom events in [`events.ts`](src/app/events.ts):
- `cliHumanInputEvent` to request input from the user for CLI command execution.
- `cliHumanResponseEvent` to resume the workflow with the response from the user.
```typescript
export const cliHumanInputEvent = humanInputEvent<{
type: "cli_human_input";
data: { command: string };
response: typeof cliHumanResponseEvent;
}>();
export const cliHumanResponseEvent = humanResponseEvent<{
type: "human_response";
data: { execute: boolean; command: string };
}>();
```
### UI Component
HITL also needs a custom UI component, that is shown when the LlamaIndexServer receives the `cliHumanInputEvent`. The name of the component is defined in the `type` field of the `cliHumanInputEvent` - in our case, it is `cli_human_input`, which corresponds to the [cli_human_input.tsx](./components/cli_human_input.tsx) component.
The custom component must use `append` to send a message with a `human_response` annotation. The data of the annotation must be in the format of the response event `cliHumanResponseEvent`, in our case, for sending to execute the command `ls -l`, we would send:
```tsx
append({
content: "Yes",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: true,
command: "ls -l", // The command to execute
},
},
],
});
```
This component displays the command to execute and the user can choose to execute or cancel the command execution.
## 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!
@@ -1,34 +0,0 @@
from typing import Type
from pydantic import BaseModel, Field
from llama_index.server.models import HumanInputEvent, HumanResponseEvent
class CLIHumanResponseEvent(HumanResponseEvent):
execute: bool = Field(
description="True if the human wants to execute the command, False otherwise."
)
command: str = Field(description="The command to execute.")
class CLICommand(BaseModel):
command: str = Field(description="The command to execute.")
# We need an event that extends from HumanInputEvent for HITL feature
class CLIHumanInputEvent(HumanInputEvent):
"""
CLIInputRequiredEvent is sent when the agent needs permission from the user to execute the CLI command or not.
Render this event by showing the command and a boolean button to execute the command or not.
"""
event_type: str = (
"cli_human_input" # used by UI to render with appropriate component
)
response_event_type: Type = (
CLIHumanResponseEvent # used by workflow to resume with the correct event
)
data: CLICommand = Field( # the data that sent to the UI for rendering
description="The command to execute.",
)
@@ -1,87 +0,0 @@
import platform
import subprocess
from typing import Any
from app.events import CLICommand, CLIHumanInputEvent, CLIHumanResponseEvent
from llama_index.core.prompts import PromptTemplate
from llama_index.core.settings import Settings
from llama_index.core.workflow import (
Context,
StartEvent,
StopEvent,
Workflow,
step,
)
def create_workflow() -> Workflow:
return CLIWorkflow()
class CLIWorkflow(Workflow):
"""
A workflow has ability to execute command line tool with human in the loop for confirmation.
"""
default_prompt = PromptTemplate(
template="""
You are a helpful assistant who can write CLI commands to execute using {cli_language}.
Your task is to analyze the user's request and write a CLI command to execute.
## User Request
{user_request}
Don't be verbose, only respond with the CLI command without any other text.
"""
)
def __init__(self, **kwargs: Any) -> None:
# HITL Workflow should disable timeout otherwise, we will get a timeout error from callback
kwargs["timeout"] = None
super().__init__(**kwargs)
@step
async def start(self, ctx: Context, ev: StartEvent) -> CLIHumanInputEvent:
user_msg = ev.user_msg
if user_msg is None:
raise ValueError("Missing user_msg in StartEvent")
await ctx.set("user_msg", user_msg)
# Request LLM to generate a CLI command
os_name = platform.system()
if os_name == "Linux" or os_name == "Darwin":
cli_language = "bash"
else:
cli_language = "cmd"
prompt = self.default_prompt.format(
user_request=user_msg, cli_language=cli_language
)
llm = Settings.llm
if llm is None:
raise ValueError("Missing LLM in Settings")
response = await llm.acomplete(prompt, formatted=True)
command = response.text.strip()
if command == "":
raise ValueError("Couldn't generate a command")
# Send the command to the user for confirmation
await ctx.set("command", command)
return CLIHumanInputEvent( # type: ignore
data=CLICommand(command=command),
response_event_type=CLIHumanResponseEvent,
)
@step
async def handle_human_response(
self,
ctx: Context,
ev: CLIHumanResponseEvent, # This event is sent by LlamaIndexServer when user response
) -> StopEvent:
# If we have human response, check the confirmation and execute the command
if ev.execute:
command = ev.command or ""
if command == "":
raise ValueError("Missing command in CLIExecutionEvent")
res = subprocess.run(command, shell=True, capture_output=True, text=True)
return StopEvent(result=res.stdout or res.stderr)
else:
return StopEvent(result=None)
@@ -1,4 +1,4 @@
import { artifactEvent, toSourceEvent } from "@llamaindex/server";
import { toSourceEvent } from "@llamaindex/server";
import {
agentStreamEvent,
createStatefulMiddleware,
@@ -339,26 +339,6 @@ export function getWorkflow(index: VectorStoreIndex | LlamaCloudIndex) {
}),
);
}
// Open the generated report in Canvas
sendEvent(
artifactEvent.with({
type: "artifact",
data: {
type: "document",
created_at: Date.now(),
data: {
title: "DeepResearch Report",
content: response,
type: "markdown",
sources: state.contextNodes.map((node) => ({
id: node.node.id_,
})),
},
},
}),
);
return stopAgentEvent.with({
result: response,
});
@@ -1,106 +0,0 @@
This is a [LlamaIndex](https://www.llamaindex.ai/) project bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
## Getting Started
First, install the dependencies:
```
npm install
```
Second, 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).
## Use Case
This example shows how to use the LlamaIndexServer with a human in the loop. It allows you to start CLI commands that are reviewed by a human before execution.
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": "Show me the files in the current directory" }] }'
```
## How does HITL work?
### Events
The human-in-the-loop approach used here is based on a simple idea: the workflow pauses and waits for a human response before proceeding to the next step.
To do this, you will need to implement two custom events:
- [HumanInputEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is used to request input from the user.
- [HumanResponseEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is sent to the workflow to resume execution with input from the user.
In this example, we have implemented these two custom events in [`events.ts`](src/app/events.ts):
- `cliHumanInputEvent` to request input from the user for CLI command execution.
- `cliHumanResponseEvent` to resume the workflow with the response from the user.
```typescript
export const cliHumanInputEvent = humanInputEvent<{
type: "cli_human_input";
data: { command: string };
response: typeof cliHumanResponseEvent;
}>();
export const cliHumanResponseEvent = humanResponseEvent<{
type: "human_response";
data: { execute: boolean; command: string };
}>();
```
### UI Component
HITL also needs a custom UI component, that is shown when the LlamaIndexServer receives the `cliHumanInputEvent`. The name of the component is defined in the `type` field of the `cliHumanInputEvent` - in our case, it is `cli_human_input`, which corresponds to the [cli_human_input.tsx](./components/cli_human_input.tsx) component.
The custom component must use `append` to send a message with a `human_response` annotation. The data of the annotation must be in the format of the response event `cliHumanResponseEvent`, in our case, for sending to execute the command `ls -l`, we would send:
```tsx
append({
content: "Yes",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: true,
command: "ls -l", // The command to execute
},
},
],
});
```
This component displays the command to execute and the user can choose to execute or cancel the command execution.
## Eject Mode
If you want to fully customize the server UI and routes, you can use `npm eject`. It will create a normal Next.js project with the same functionality as @llamaindex/server.
```bash
npm run eject
```
## 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!
@@ -1,12 +0,0 @@
import { humanInputEvent, humanResponseEvent } from "@llamaindex/server";
export const cliHumanInputEvent = humanInputEvent<{
type: "cli_human_input";
data: { command: string };
response: typeof cliHumanResponseEvent;
}>();
export const cliHumanResponseEvent = humanResponseEvent<{
type: "human_response";
data: { execute: boolean; command: string };
}>();
@@ -1,20 +0,0 @@
import { execSync } from "child_process";
import { tool } from "llamaindex";
import { z } from "zod";
export const cliExecutor = tool({
name: "cli_executor",
description: "This tool executes a command and returns the output.",
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
const output = execSync(command, {
encoding: "utf-8",
});
return output;
} catch (error) {
console.error(error);
return "Command failed";
}
},
});
@@ -1,101 +0,0 @@
import { toAgentRunEvent, writeResponseToStream } from "@llamaindex/server";
import { chatWithTools } from "@llamaindex/tools";
import {
createWorkflow,
getContext,
startAgentEvent,
stopAgentEvent,
withSnapshot,
workflowEvent,
} from "@llamaindex/workflow";
import { ChatMessage, Settings, ToolCallLLM } from "llamaindex";
import { cliHumanInputEvent, cliHumanResponseEvent } from "./events";
import { cliExecutor } from "./tools";
const summaryEvent = workflowEvent<string>(); // simple event to summarize the result
export const workflowFactory = (body: unknown) => {
const llm = Settings.llm as ToolCallLLM;
if (!llm.supportToolCall) {
throw new Error("LLM is not a ToolCallLLM");
}
const { messages } = body as { messages: ChatMessage[] };
const workflow = withSnapshot(createWorkflow());
workflow.handle([startAgentEvent], async ({ data }) => {
const { userInput, chatHistory = [] } = data;
if (!userInput) {
throw new Error("User input is required");
}
// in this example, we use chatWithTools to decide should perform a tool call or not
// if cli executor is called, emit HumanInputEvent to ask user for permission
const toolCallResponse = await chatWithTools(
llm,
[cliExecutor],
chatHistory.concat({ role: "user", content: userInput }),
);
const cliExecutorToolCall = toolCallResponse.toolCalls.find(
(toolCall) => toolCall.name === cliExecutor.metadata.name,
);
const command = cliExecutorToolCall?.input?.command as string;
if (command) {
return cliHumanInputEvent.with({
type: "cli_human_input",
data: { command },
response: cliHumanResponseEvent,
});
}
// if no tool call, just response as normal
return summaryEvent.with("");
});
// do actions after getting response from human
workflow.handle([cliHumanResponseEvent], async ({ data }) => {
const { sendEvent } = getContext();
const { command, execute } = data.data;
if (!execute) {
// stop the workflow if user reject to execute the command
return summaryEvent.with(`User reject to execute the command ${command}`);
}
sendEvent(
toAgentRunEvent({
agent: "CLI Executor",
text: `Execute the command "${command}" and return the result`,
type: "text",
}),
);
const result = (await cliExecutor.call({ command })) as string;
return summaryEvent.with(
`Executed the command ${command} and got the result: ${result}`,
);
});
workflow.handle([summaryEvent], async ({ data: summaryResult }) => {
const { sendEvent } = getContext();
const chatHistory = messages;
if (summaryResult) {
chatHistory.push({ role: "user", content: summaryResult });
}
const stream = await llm.chat({
messages: chatHistory,
stream: true,
});
const result = await writeResponseToStream(stream, sendEvent);
return stopAgentEvent.with({ result });
});
return workflow;
};
@@ -12,7 +12,7 @@
"dependencies": {
"@llamaindex/openai": "~0.4.0",
"@llamaindex/server": "~0.2.1",
"@llamaindex/workflow": "~1.1.8",
"@llamaindex/workflow": "~1.1.3",
"@llamaindex/tools": "~0.0.11",
"llamaindex": "~0.11.0",
"dotenv": "^16.4.7",
@@ -20,7 +20,7 @@
},
"devDependencies": {
"@types/node": "^20.10.3",
"tsx": "4.7.2",
"tsx": "^4.7.2",
"typescript": "^5.3.2",
"nodemon": "^3.1.10"
}
-14
View File
@@ -1,19 +1,5 @@
# @llamaindex/server
## 0.2.8
### Patch Changes
- e2486eb: feat: support human in the loop for TS
## 0.2.7
### Patch Changes
- af9ad3c: feat: show document artifact after generating report
- a543a27: feat: bump chat-ui with inline artifact
- 1ff6eaf: Add support for chat upload file
## 0.2.6
### Patch Changes
-1
View File
@@ -8,7 +8,6 @@ LlamaIndexServer is a Next.js-based application that allows you to quickly launc
- 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
- Human-in-the-loop (HITL) support, check out the [Human-in-the-loop](https://github.com/run-llama/create-llama/blob/main/packages/server/examples/hitl/README.md) documentation for more details.
## Installation
-172
View File
@@ -1,172 +0,0 @@
# Human in the Loop
This example shows how to use the LlamaIndexServer with a human in the loop. It allows you to start CLI commands that are reviewed by a human before execution.
## Getting Started
### Environment Setup
Export your OpenAI API key:
```bash
export OPENAI_API_KEY=<your-openai-api-key>
```
### Starting the Server
Run the server in development mode:
```bash
npx nodemon --exec tsx index.ts --ignore output/*
```
### Access the Application
Open your browser and go to:
```
http://localhost:3000
```
You will see the LlamaIndexServer UI, where you can interact with the HITL agent. Try "List all files in the current directory" and see how the agent pauses and waits for a human response before executing the command.
## How does HITL work?
### Events
The human-in-the-loop approach used here is based on a simple idea: the workflow pauses and waits for a human response before proceeding to the next step.
To do this, you will need to implement two custom events:
- [HumanInputEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is used to request input from the user.
- [HumanResponseEvent](https://github.com/run-llama/create-llama/blob/main/packages/server/src/utils/hitl/events.ts): This event is sent to the workflow to resume execution with input from the user.
In this example, we have implemented these two custom events in [`events.ts`](src/app/events.ts):
- `cliHumanInputEvent` to request input from the user for CLI command execution.
- `cliHumanResponseEvent` to resume the workflow with the response from the user.
```typescript
export const cliHumanInputEvent = humanInputEvent<{
type: "cli_human_input";
data: { command: string };
response: typeof cliHumanResponseEvent;
}>();
export const cliHumanResponseEvent = humanResponseEvent<{
type: "human_response";
data: { execute: boolean; command: string };
}>();
```
### UI Component
HITL also needs a custom UI component, that is shown when the LlamaIndexServer receives the `cliHumanInputEvent`. The name of the component is defined in the `type` field of the `cliHumanInputEvent` - in our case, it is `cli_human_input`, which corresponds to the [cli_human_input.tsx](./components/cli_human_input.tsx) component.
The custom component must use `append` to send a message with a `human_response` annotation. The data of the annotation must be in the format of the response event `cliHumanResponseEvent`, in our case, for sending to execute the command `ls -l`, we would send:
```tsx
append({
content: "Yes",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: true,
command: "ls -l", // The command to execute
},
},
],
});
```
This component displays the command to execute and the user can choose to execute or cancel the command execution.
### Workflow Implementation
The workflow is implemented in [`workflow.ts`](src/app/workflow.ts) using LlamaIndex workflows. The workflow handles three main steps:
1. **Initial Request Handling**: When a user input is received, the workflow uses `chatWithTools` to determine if a CLI command should be executed. If so, it emits a `cliHumanInputEvent` to request user permission.
```typescript
workflow.handle([startAgentEvent], async ({ data }) => {
const { userInput, chatHistory = [] } = data;
const toolCallResponse = await chatWithTools(
llm,
[cliExecutor],
chatHistory.concat({ role: "user", content: userInput }),
);
const cliExecutorToolCall = toolCallResponse.toolCalls.find(
(toolCall) => toolCall.name === cliExecutor.metadata.name,
);
const command = cliExecutorToolCall?.input?.command as string;
if (command) {
return cliHumanInputEvent.with({
type: "cli_human_input",
data: { command },
response: cliHumanResponseEvent,
});
}
return summaryEvent.with("");
});
```
2. **Human Response Handling**: After receiving human input, the workflow either executes the command or cancels based on the user's choice.
```typescript
workflow.handle([cliHumanResponseEvent], async ({ data }) => {
const { command, execute } = data.data;
if (!execute) {
return summaryEvent.with(`User reject to execute the command ${command}`);
}
const result = (await cliExecutor.call({ command })) as string;
return summaryEvent.with(
`Executed the command ${command} and got the result: ${result}`,
);
});
```
3. **Final Response**: The workflow generates a final response based on the execution result and streams it back to the user.
### Tools
The CLI executor tool is defined in [`tools.ts`](src/app/tools.ts):
```typescript
export const cliExecutor = tool({
name: "cli_executor",
description: "This tool executes a command and returns the output.",
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
const output = execSync(command, {
encoding: "utf-8",
});
return output;
} catch (error) {
console.error(error);
return "Command failed";
}
},
});
```
## Architecture
The HITL implementation consists of:
1. **Workflow Factory** (`workflow.ts`): Creates and configures the workflow with event handlers
2. **Events** (`events.ts`): Defines typed events for human input and response
3. **Tools** (`tools.ts`): Implements the CLI executor tool
4. **UI Component** (`components/cli_human_input.tsx`): Provides the user interface for human approval
5. **Server Entry** (`index.ts`): Configures and starts the LlamaIndexServer
This architecture ensures that dangerous operations like CLI command execution require explicit human approval before proceeding.
@@ -1,95 +0,0 @@
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { JSONValue, useChatUI } from "@llamaindex/chat-ui";
import React, { FC, useState } from "react";
import { z } from "zod";
// This schema is equivalent to the CLICommand model defined in events.py
const CLIInputEventSchema = z.object({
command: z.string(),
});
type CLIInputEvent = z.infer<typeof CLIInputEventSchema>;
const CLIHumanInput: FC<{
events: JSONValue[];
}> = ({ events }) => {
const inputEvent = (events || [])
.map((ev) => {
const parseResult = CLIInputEventSchema.safeParse(ev);
return parseResult.success ? parseResult.data : null;
})
.filter((ev): ev is CLIInputEvent => ev !== null)
.at(-1);
const { append } = useChatUI();
const [confirmedValue, setConfirmedValue] = useState<boolean | null>(null);
const [editableCommand, setEditableCommand] = useState<string | undefined>(
inputEvent?.command,
);
// Update editableCommand if inputEvent changes (e.g. new event comes in)
React.useEffect(() => {
setEditableCommand(inputEvent?.command);
}, [inputEvent?.command]);
const handleConfirm = () => {
append({
content: "Yes",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: true,
command: editableCommand, // Use editable command
},
},
],
});
setConfirmedValue(true);
};
const handleCancel = () => {
append({
content: "No",
role: "user",
annotations: [
{
type: "human_response",
data: {
execute: false,
command: inputEvent?.command,
},
},
],
});
setConfirmedValue(false);
};
return (
<Card className="my-4">
<CardContent className="pt-6">
<p className="text-sm text-gray-700">
Do you want to execute the following command?
</p>
<input
disabled
type="text"
value={editableCommand || ""}
onChange={(e) => setEditableCommand(e.target.value)}
className="my-2 w-full overflow-x-auto rounded border border-gray-300 bg-gray-100 p-3 font-mono text-xs text-gray-800"
/>
</CardContent>
{confirmedValue === null ? (
<CardFooter className="flex justify-end gap-2">
<>
<Button onClick={handleConfirm}>Yes</Button>
<Button onClick={handleCancel}>No</Button>
</>
</CardFooter>
) : null}
</Card>
);
};
export default CLIHumanInput;
-20
View File
@@ -1,20 +0,0 @@
import { OpenAI } from "@llamaindex/openai";
import { LlamaIndexServer } from "@llamaindex/server";
import { Settings } from "llamaindex";
import { workflowFactory } from "./src/app/workflow";
Settings.llm = new OpenAI({
model: "gpt-4o-mini",
});
new LlamaIndexServer({
workflow: workflowFactory,
uiConfig: {
starterQuestions: [
"Check status of git in the current directory",
"List all files in the current directory",
],
componentsDir: "components",
},
port: 3000,
}).start();
@@ -1,12 +0,0 @@
import { humanInputEvent, humanResponseEvent } from "@llamaindex/server";
export const cliHumanInputEvent = humanInputEvent<{
type: "cli_human_input";
data: { command: string };
response: typeof cliHumanResponseEvent;
}>();
export const cliHumanResponseEvent = humanResponseEvent<{
type: "human_response";
data: { execute: boolean; command: string };
}>();
@@ -1,20 +0,0 @@
import { execSync } from "child_process";
import { tool } from "llamaindex";
import { z } from "zod";
export const cliExecutor = tool({
name: "cli_executor",
description: "This tool executes a command and returns the output.",
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => {
try {
const output = execSync(command, {
encoding: "utf-8",
});
return output;
} catch (error) {
console.error(error);
return "Command failed";
}
},
});
@@ -1,106 +0,0 @@
import { OpenAI } from "@llamaindex/openai";
import { toAgentRunEvent, writeResponseToStream } from "@llamaindex/server";
import { chatWithTools } from "@llamaindex/tools";
import {
createWorkflow,
getContext,
startAgentEvent,
stopAgentEvent,
withSnapshot,
workflowEvent,
} from "@llamaindex/workflow";
import { ChatMessage, Settings, ToolCallLLM } from "llamaindex";
import { cliHumanInputEvent, cliHumanResponseEvent } from "./events";
import { cliExecutor } from "./tools";
Settings.llm = new OpenAI({
model: "gpt-4o-mini",
});
const summaryEvent = workflowEvent<string>(); // simple event to summarize the result
export const workflowFactory = (body: unknown) => {
const llm = Settings.llm as ToolCallLLM;
if (!llm.supportToolCall) {
throw new Error("LLM is not a ToolCallLLM");
}
const { messages } = body as { messages: ChatMessage[] };
const workflow = withSnapshot(createWorkflow());
workflow.handle([startAgentEvent], async ({ data }) => {
const { userInput, chatHistory = [] } = data;
if (!userInput) {
throw new Error("User input is required");
}
// in this example, we use chatWithTools to decide should perform a tool call or not
// if cli executor is called, emit HumanInputEvent to ask user for permission
const toolCallResponse = await chatWithTools(
llm,
[cliExecutor],
chatHistory.concat({ role: "user", content: userInput }),
);
const cliExecutorToolCall = toolCallResponse.toolCalls.find(
(toolCall) => toolCall.name === cliExecutor.metadata.name,
);
const command = cliExecutorToolCall?.input?.command as string;
if (command) {
return cliHumanInputEvent.with({
type: "cli_human_input",
data: { command },
response: cliHumanResponseEvent,
});
}
// if no tool call, just response as normal
return summaryEvent.with("");
});
// do actions after getting response from human
workflow.handle([cliHumanResponseEvent], async ({ data }) => {
const { sendEvent } = getContext();
const { command, execute } = data.data;
if (!execute) {
// stop the workflow if user reject to execute the command
return summaryEvent.with(`User reject to execute the command ${command}`);
}
sendEvent(
toAgentRunEvent({
agent: "CLI Executor",
text: `Execute the command "${command}" and return the result`,
type: "text",
}),
);
const result = (await cliExecutor.call({ command })) as string;
return summaryEvent.with(
`Executed the command ${command} and got the result: ${result}`,
);
});
workflow.handle([summaryEvent], async ({ data: summaryResult }) => {
const { sendEvent } = getContext();
const chatHistory = messages;
if (summaryResult) {
chatHistory.push({ role: "user", content: summaryResult });
}
const stream = await llm.chat({
messages: chatHistory,
stream: true,
});
const result = await writeResponseToStream(stream, sendEvent);
return stopAgentEvent.with({ result });
});
return workflow;
};
+1 -1
View File
@@ -18,7 +18,7 @@
"devDependencies": {
"@types/node": "^20.10.3",
"nodemon": "^3.1.10",
"tsx": "4.7.2",
"tsx": "^4.7.2",
"typescript": "^5.3.2"
}
}
+1 -1
View File
@@ -10,5 +10,5 @@
"outDir": "dist"
},
"include": ["**/*"],
"exclude": ["node_modules", "dist", "custom-layout/layout", "hitl/components"]
"exclude": ["node_modules", "dist", "custom-layout/layout"]
}
+13 -24
View File
@@ -1,19 +1,16 @@
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 {
getHumanResponsesFromMessage,
pauseForHumanInput,
processWorkflowStream,
runWorkflow,
sendSuggestedQuestionsEvent,
toDataStream,
} from "./utils";
// import workflow factory and settings from local file
import { stopAgentEvent } from "@llamaindex/workflow";
import { initSettings } from "./app/settings";
import { workflowFactory } from "./app/workflow";
@@ -24,10 +21,7 @@ export async function POST(req: NextRequest) {
const reqBody = await req.json();
const suggestNextQuestions = process.env.SUGGEST_NEXT_QUESTIONS === "true";
const { messages, id: requestId } = reqBody as {
messages: Message[];
id?: string;
};
const { messages } = reqBody as { messages: Message[] };
const chatHistory = messages.map((message) => ({
role: message.role as MessageType,
content: message.content,
@@ -42,31 +36,25 @@ export async function POST(req: NextRequest) {
{ status: 400 },
);
}
const workflowInput: AgentInputData = {
userInput: lastMessage.content,
chatHistory,
};
const abortController = new AbortController();
req.signal.addEventListener("abort", () =>
abortController.abort("Connection closed"),
);
const context = await runWorkflow({
workflow: await workflowFactory(reqBody),
input: { userInput: lastMessage.content, chatHistory },
human: {
snapshotId: requestId, // use requestId to restore snapshot
responses: getHumanResponsesFromMessage(lastMessage),
},
});
const stream = processWorkflowStream(context.stream).until(
(event) =>
abortController.signal.aborted || stopAgentEvent.include(event),
const workflow = await workflowFactory(reqBody);
const workflowEventStream = await runWorkflow(
workflow,
workflowInput,
abortController.signal,
);
const dataStream = toDataStream(stream, {
const dataStream = toDataStream(workflowEventStream, {
callbacks: {
onPauseForHumanInput: async (responseEvent) => {
await pauseForHumanInput(context, responseEvent, requestId); // use requestId to save snapshot
},
onFinal: async (completion, dataStreamWriter) => {
chatHistory.push({
role: "assistant" as MessageType,
@@ -78,6 +66,7 @@ export async function POST(req: NextRequest) {
},
},
});
return new Response(dataStream, {
status: 200,
headers: {
-1
View File
@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "@llamaindex/chat-ui/styles/editor.css";
import "@llamaindex/chat-ui/styles/markdown.css";
import "@llamaindex/chat-ui/styles/pdf.css";
import "./globals.css";
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@llamaindex/server",
"description": "LlamaIndex Server",
"version": "0.2.8",
"version": "0.2.6",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
@@ -114,7 +114,7 @@
},
"peerDependencies": {
"@llamaindex/env": "~0.1.30",
"@llamaindex/workflow": "~1.1.8",
"@llamaindex/workflow": "~1.1.3",
"llamaindex": "~0.11.0",
"zod": "^3.24.2",
"zod-to-json-schema": "^3.23.3"
@@ -18,7 +18,6 @@ const eslintConfig = [
"react-hooks/exhaustive-deps": "off",
"@next/next/no-img-element": "off",
"@next/next/no-assign-module-variable": "off",
"@typescript-eslint/no-empty-object-type": "off",
},
},
{
+2 -2
View File
@@ -31,7 +31,7 @@
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^4",
"tsx": "4.7.2",
"tsx": "^4.19.3",
"tw-animate-css": "1.2.5",
"typescript": "^5"
},
@@ -46,7 +46,7 @@
"@llamaindex/openai": "~0.4.0",
"@llamaindex/readers": "~3.1.4",
"@llamaindex/tools": "~0.0.11",
"@llamaindex/workflow": "~1.1.8",
"@llamaindex/workflow": "~1.1.3",
"@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-alert-dialog": "^1.1.7",
"@radix-ui/react-aspect-ratio": "^1.1.3",
+23 -37
View File
@@ -1,20 +1,16 @@
import { stopAgentEvent } from "@llamaindex/workflow";
import type { AgentInputData } from "@llamaindex/workflow";
import { type Message } from "ai";
import { IncomingMessage, ServerResponse } from "http";
import type { MessageType } from "llamaindex";
import { type WorkflowFactory } from "../types";
import { sendSuggestedQuestionsEvent } from "../utils";
import {
getHumanResponsesFromMessage,
pauseForHumanInput,
} from "../utils/hitl";
import {
parseRequestBody,
pipeStreamToResponse,
sendJSONResponse,
} from "../utils/request";
import { toDataStream } from "../utils/stream";
import { processWorkflowStream, runWorkflow } from "../utils/workflow";
import { sendSuggestedQuestionsEvent } from "../utils/suggestion";
import { runWorkflow } from "../utils/workflow";
export const handleChat = async (
req: IncomingMessage,
@@ -22,47 +18,37 @@ export const handleChat = async (
workflowFactory: WorkflowFactory,
suggestNextQuestions: boolean,
) => {
const abortController = new AbortController();
res.on("close", () => abortController.abort("Connection closed"));
try {
const body = await parseRequestBody(req);
const { messages, id: requestId } = body as {
messages: Message[];
id?: string;
};
const lastMessage = messages[messages.length - 1];
if (lastMessage?.role !== "user" || !lastMessage.content) {
return sendJSONResponse(res, 400, {
error: "Messages cannot be empty and last message must be from user",
});
}
const { messages } = body as { messages: Message[] };
const chatHistory = messages.map((message) => ({
role: message.role as MessageType,
content: message.content,
}));
const context = await runWorkflow({
workflow: await workflowFactory(body),
input: { userInput: lastMessage.content, chatHistory },
human: {
snapshotId: requestId, // use requestId to restore snapshot
responses: getHumanResponsesFromMessage(lastMessage),
},
});
const lastMessage = messages[messages.length - 1];
if (lastMessage?.role !== "user") {
return sendJSONResponse(res, 400, {
error: "Messages cannot be empty and last message must be from user",
});
}
const workflowInput: AgentInputData = {
userInput: lastMessage.content,
chatHistory,
};
const stream = processWorkflowStream(context.stream).until(
(event) =>
abortController.signal.aborted || stopAgentEvent.include(event),
const abortController = new AbortController();
res.on("close", () => abortController.abort("Connection closed"));
const workflow = await workflowFactory(body);
const workflowEventStream = await runWorkflow(
workflow,
workflowInput,
abortController.signal,
);
const dataStream = toDataStream(stream, {
const dataStream = toDataStream(workflowEventStream, {
callbacks: {
onPauseForHumanInput: async (responseEvent) => {
await pauseForHumanInput(context, responseEvent, requestId); // use requestId to save snapshot
},
onFinal: async (completion, dataStreamWriter) => {
chatHistory.push({
role: "assistant" as MessageType,
-2
View File
@@ -3,7 +3,5 @@ export * from "./types";
export * from "./utils/events";
export { getStoredFilePath } from "./utils/file";
export { generateEventComponent } from "./utils/gen-ui";
export * from "./utils/hitl";
export * from "./utils/inline";
export * from "./utils/prompts";
export * from "./utils/stream";
-1
View File
@@ -110,7 +110,6 @@ export type DocumentArtifactData = {
title: string;
content: string;
type: string; // markdown, html,...
sources?: { id: string }[]; // sources that are used to render citation numbers in the document
};
export type CodeArtifact = Artifact<CodeArtifactData> & {
-64
View File
@@ -1,64 +0,0 @@
import {
type WorkflowEvent,
type WorkflowEventData,
workflowEvent,
} from "@llamaindex/workflow";
import type { Message } from "ai";
import type { JSONValue } from "llamaindex";
import z from "zod";
export type HumanInputEventData = {
type: string;
data?: JSONValue;
response: WorkflowEvent<HumanResponseEventData>;
};
export const humanInputEvent = workflowBaseEvent<HumanInputEventData>();
export type HumanResponseEventData = {
type: "human_response";
data?: JSONValue;
};
export const humanResponseEvent = workflowBaseEvent<HumanResponseEventData>();
// helper function to extract human responses from message annotations
export const getHumanResponsesFromMessage = (message: Message) => {
const schema = z.object({ type: z.literal("human_response"), data: z.any() });
return (
message.annotations?.filter(
(annotation): annotation is z.infer<typeof schema> =>
schema.safeParse(annotation).success,
) ?? []
);
};
// TODO: move to llama-flow package
export type BaseEvent<K> = (<T extends K>() => WorkflowEvent<T>) &
WorkflowEvent<K>;
export function workflowBaseEvent<K = unknown>(): BaseEvent<K> {
const baseEvent = workflowEvent<K>();
const derivedEvents = new Set<WorkflowEvent<unknown>>();
function eventFn<T>(): WorkflowEvent<T> {
const event = workflowEvent<T>();
derivedEvents.add(event);
return event;
}
const originalInclude = baseEvent.include;
const enhancedBaseEvent = Object.assign(baseEvent, {
include: (
instance: WorkflowEventData<unknown>,
): instance is WorkflowEventData<void> => {
// Base event accepts its own instances OR instances from any derived events
return (
originalInclude(instance) ||
Array.from(derivedEvents).some((e) => e.include(instance))
);
},
});
return Object.assign(eventFn, enhancedBaseEvent) as typeof eventFn &
typeof baseEvent;
}
-4
View File
@@ -1,4 +0,0 @@
export * from "./events";
export * from "./pause";
export * from "./resume";
export * from "./snapshot";
-25
View File
@@ -1,25 +0,0 @@
import {
request,
type WorkflowContext,
type WorkflowEvent,
} from "@llamaindex/workflow";
import { randomUUID } from "node:crypto";
import type { HumanResponseEventData } from "./events";
import { ensureSnapshotWorkflowContext, saveSnapshot } from "./snapshot";
// pause the workflow and save the snapshot
export const pauseForHumanInput = async (
context: WorkflowContext,
responseEvent: WorkflowEvent<HumanResponseEventData>,
snapshotId: string = randomUUID(), // automatically generate a request id if not provided
) => {
const snapshotWorkflowContext = ensureSnapshotWorkflowContext(context);
const { snapshot, sendEvent } = snapshotWorkflowContext;
// send a request event to save the missing step (`humanResponseEvent`) to the snapshot
sendEvent(request(responseEvent));
// get and save snapshot
const [_, snapshotData] = await snapshot();
await saveSnapshot(snapshotId, snapshotData);
};
-28
View File
@@ -1,28 +0,0 @@
import { type Workflow } from "@llamaindex/workflow";
import type { HumanResponseEventData } from "./events";
import {
ensureSnapshotWorkflow,
loadSnapshot,
type SnapshotWorkflowContext,
} from "./snapshot";
// create workflow context from snapshot and start running it from the last missing step
export const resumeWorkflowFromHumanResponses = async (
workflow: Workflow, // the workflow to resume
humanResponses: Array<HumanResponseEventData>, // human can send multiple responses
snapshotId: string,
): Promise<SnapshotWorkflowContext> => {
// check workflow is snapshotable
const snapshotWorkflow = ensureSnapshotWorkflow(workflow);
const snapshot = await loadSnapshot(snapshotId);
if (!snapshot) {
// if there is no snapshot, we can't resume the workflow
throw new Error("No snapshot found for request id: " + snapshotId);
}
// resume the workflow from the snapshot with human response
const context = snapshotWorkflow.resume(humanResponses, snapshot);
return context;
};
@@ -1,78 +0,0 @@
import {
withSnapshot,
type Workflow,
type WorkflowContext,
} from "@llamaindex/workflow";
import { promises as fs } from "fs";
import path from "path";
// @llama-flow doesn't export snapshot types, we need to infer them from the functions
export type SnapshotWorkflow = ReturnType<typeof withSnapshot<Workflow>>;
export type SnapshotWorkflowContext = ReturnType<
SnapshotWorkflow["createContext"]
>;
export type SnapshotData = Awaited<
ReturnType<SnapshotWorkflowContext["snapshot"]>
>[1];
const SNAPSHOTS_DIR = path.join("output", "snapshots");
// Ensure the checkpoints directory exists
const ensureCheckpointsDir = async () => {
try {
await fs.mkdir(SNAPSHOTS_DIR, { recursive: true });
} catch (error) {
console.error("Failed to create checkpoints directory:", error);
}
};
export const saveSnapshot = async (
requestId: string,
snapshot: SnapshotData,
) => {
try {
await ensureCheckpointsDir();
const filePath = path.join(SNAPSHOTS_DIR, `${requestId}.json`);
await fs.writeFile(filePath, JSON.stringify(snapshot, null, 2), "utf8");
console.log(`Snapshot saved to: ${filePath}`);
} catch (error) {
console.error("Failed to save snapshot:", error);
throw error;
}
};
export const loadSnapshot = async (
requestId: string,
): Promise<SnapshotData | undefined> => {
try {
const filePath = path.join(SNAPSHOTS_DIR, `${requestId}.json`);
const data = await fs.readFile(filePath, "utf8");
return JSON.parse(data);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return undefined; // File doesn't exist
}
console.error("Failed to load snapshot:", error);
throw error;
}
};
export function ensureSnapshotWorkflow(workflow: Workflow): SnapshotWorkflow {
if (!("resume" in workflow)) {
throw new Error(
"Workflow is not a snapshot workflow. Please use withSnapshot() to make it snapshotable.",
);
}
return workflow as SnapshotWorkflow;
}
export function ensureSnapshotWorkflowContext(
context: WorkflowContext,
): SnapshotWorkflowContext {
if (!("snapshot" in context)) {
throw new Error(
"Cannot get snapshot of the workflow. Please use withSnapshot() to make workflow snapshotable.",
);
}
return context as SnapshotWorkflowContext;
}
-1
View File
@@ -1,7 +1,6 @@
export * from "./events";
export * from "./file";
export * from "./gen-ui";
export * from "./hitl";
export * from "./inline";
export * from "./prompts";
export * from "./request";
+1 -41
View File
@@ -1,16 +1,10 @@
import {
agentStreamEvent,
type WorkflowEvent,
type WorkflowEventData,
} from "@llamaindex/workflow";
import { agentStreamEvent, type WorkflowEventData } from "@llamaindex/workflow";
import {
createDataStream,
formatDataStreamPart,
type DataStreamWriter,
type JSONValue,
} from "ai";
import type { ChatResponseChunk } from "llamaindex";
import { humanInputEvent, type HumanResponseEventData } from "./hitl";
/**
* Configuration options and helper callback methods for stream lifecycle events.
@@ -30,11 +24,6 @@ export interface StreamCallbacks {
text: string,
dataStreamWriter: DataStreamWriter,
) => Promise<void> | void;
/** `onPauseForHumanInput`: Called when human input event is emitted. */
onPauseForHumanInput?:
| ((event: WorkflowEvent<HumanResponseEventData>) => Promise<void> | void)
| undefined;
}
/**
@@ -72,14 +61,6 @@ export function toDataStream(
await callbacks.onText(content, dataStreamWriter);
}
}
} else if (humanInputEvent.include(event)) {
const { response, ...rest } = event.data;
dataStreamWriter.writeMessageAnnotation(rest); // show human input in UI
if (callbacks?.onPauseForHumanInput) {
await callbacks.onPauseForHumanInput(response);
return; // stop the stream
}
} else {
dataStreamWriter.writeMessageAnnotation(event.data as JSONValue);
}
@@ -97,24 +78,3 @@ export function toDataStream(
},
});
}
export async function writeResponseToStream(
generator: AsyncIterable<ChatResponseChunk<object>>,
sendEvent: (event: WorkflowEventData<unknown>) => void,
) {
let response = "";
if (generator) {
for await (const chunk of generator) {
response += chunk.delta;
sendEvent(
agentStreamEvent.with({
delta: chunk.delta,
response,
currentAgentName: "LLM",
raw: chunk.raw,
}),
);
}
}
return response;
}
+20 -37
View File
@@ -1,11 +1,12 @@
import {
agentToolCallEvent,
agentToolCallResultEvent,
run,
startAgentEvent,
stopAgentEvent,
WorkflowStream,
type AgentInputData,
type Workflow,
type WorkflowContext,
type WorkflowEventData,
} from "@llamaindex/workflow";
import {
@@ -21,48 +22,30 @@ import {
type SourceEventNode,
} from "./events";
import { downloadFile } from "./file";
import {
resumeWorkflowFromHumanResponses,
type HumanResponseEventData,
} from "./hitl/index";
import { toInlineAnnotationEvent } from "./inline";
export async function runWorkflow({
workflow,
input,
human,
}: {
workflow: Workflow;
input: AgentInputData;
human?: {
snapshotId?: string | undefined; // the snapshot id to restore workflow
responses?: HumanResponseEventData[]; // the data from human to trigger events after restoring
};
}): Promise<WorkflowContext> {
let context: WorkflowContext;
if (human?.responses?.length && human?.snapshotId) {
// resume the workflow if there is human response
context = await resumeWorkflowFromHumanResponses(
workflow,
human.responses,
human.snapshotId,
);
} else {
// otherwise, create a new empty context and run the workflow with startAgentEvent
context = workflow.createContext();
context.sendEvent(
startAgentEvent.with({
userInput: input.userInput,
chatHistory: input.chatHistory,
}),
);
export async function runWorkflow(
workflow: Workflow,
input: AgentInputData,
abortSignal?: AbortSignal,
): Promise<WorkflowStream<WorkflowEventData<unknown>>> {
if (!input.userInput) {
throw new Error("Missing user input to start the workflow");
}
const workflowStream = run(workflow, [
startAgentEvent.with({
userInput: input.userInput,
chatHistory: input.chatHistory,
}),
]);
return context;
// Transform the stream to handle annotations
return processWorkflowStream(workflowStream).until(
(event) => abortSignal?.aborted || stopAgentEvent.include(event),
);
}
export function processWorkflowStream(
function processWorkflowStream(
stream: WorkflowStream<WorkflowEventData<unknown>>,
) {
return stream.pipeThrough(
+23 -273
View File
@@ -187,8 +187,8 @@ importers:
specifier: ~0.1.30
version: 0.1.30
'@llamaindex/workflow':
specifier: ~1.1.8
version: 1.1.8(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(@modelcontextprotocol/sdk@1.12.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)
specifier: ~1.1.3
version: 1.1.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(@modelcontextprotocol/sdk@1.12.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)
'@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)
@@ -342,7 +342,7 @@ importers:
version: 7.20.7
llamaindex:
specifier: ~0.11.0
version: 0.11.1(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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)
version: 0.11.1(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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
@@ -381,7 +381,7 @@ importers:
version: 16.5.0
llamaindex:
specifier: ~0.11.0
version: 0.11.1(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.13)
version: 0.11.1(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.13)
zod:
specifier: ^3.24.2
version: 3.25.13
@@ -393,8 +393,8 @@ importers:
specifier: ^3.1.10
version: 3.1.10
tsx:
specifier: 4.7.2
version: 4.7.2
specifier: ^4.7.2
version: 4.19.3
typescript:
specifier: ^5.3.2
version: 5.8.3
@@ -699,12 +699,6 @@ packages:
'@emnapi/wasi-threads@1.0.2':
resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==}
'@esbuild/aix-ppc64@0.19.12':
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [aix]
'@esbuild/aix-ppc64@0.21.5':
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
@@ -717,12 +711,6 @@ packages:
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.19.12':
resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm64@0.21.5':
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
engines: {node: '>=12'}
@@ -735,12 +723,6 @@ packages:
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.19.12':
resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
'@esbuild/android-arm@0.21.5':
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
engines: {node: '>=12'}
@@ -753,12 +735,6 @@ packages:
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.19.12':
resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
'@esbuild/android-x64@0.21.5':
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
engines: {node: '>=12'}
@@ -771,12 +747,6 @@ packages:
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.19.12':
resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-arm64@0.21.5':
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
engines: {node: '>=12'}
@@ -789,12 +759,6 @@ packages:
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.19.12':
resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
'@esbuild/darwin-x64@0.21.5':
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
engines: {node: '>=12'}
@@ -807,12 +771,6 @@ packages:
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.19.12':
resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-arm64@0.21.5':
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
engines: {node: '>=12'}
@@ -825,12 +783,6 @@ packages:
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.19.12':
resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
'@esbuild/freebsd-x64@0.21.5':
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
engines: {node: '>=12'}
@@ -843,12 +795,6 @@ packages:
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.19.12':
resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm64@0.21.5':
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
engines: {node: '>=12'}
@@ -861,12 +807,6 @@ packages:
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.19.12':
resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
'@esbuild/linux-arm@0.21.5':
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
engines: {node: '>=12'}
@@ -879,12 +819,6 @@ packages:
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.19.12':
resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-ia32@0.21.5':
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
engines: {node: '>=12'}
@@ -897,12 +831,6 @@ packages:
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.19.12':
resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-loong64@0.21.5':
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
engines: {node: '>=12'}
@@ -915,12 +843,6 @@ packages:
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.19.12':
resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-mips64el@0.21.5':
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
engines: {node: '>=12'}
@@ -933,12 +855,6 @@ packages:
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.19.12':
resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-ppc64@0.21.5':
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
engines: {node: '>=12'}
@@ -951,12 +867,6 @@ packages:
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.19.12':
resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-riscv64@0.21.5':
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
engines: {node: '>=12'}
@@ -969,12 +879,6 @@ packages:
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.19.12':
resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-s390x@0.21.5':
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
engines: {node: '>=12'}
@@ -987,12 +891,6 @@ packages:
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.19.12':
resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
'@esbuild/linux-x64@0.21.5':
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
engines: {node: '>=12'}
@@ -1011,12 +909,6 @@ packages:
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.19.12':
resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
'@esbuild/netbsd-x64@0.21.5':
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
engines: {node: '>=12'}
@@ -1035,12 +927,6 @@ packages:
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.19.12':
resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
'@esbuild/openbsd-x64@0.21.5':
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
engines: {node: '>=12'}
@@ -1053,12 +939,6 @@ packages:
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.19.12':
resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
'@esbuild/sunos-x64@0.21.5':
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
engines: {node: '>=12'}
@@ -1071,12 +951,6 @@ packages:
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.19.12':
resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-arm64@0.21.5':
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
engines: {node: '>=12'}
@@ -1089,12 +963,6 @@ packages:
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.19.12':
resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-ia32@0.21.5':
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
engines: {node: '>=12'}
@@ -1107,12 +975,6 @@ packages:
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.19.12':
resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
'@esbuild/win32-x64@0.21.5':
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
engines: {node: '>=12'}
@@ -1471,8 +1333,8 @@ packages:
'@lezer/yaml@1.0.3':
resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==}
'@llama-flow/core@0.4.4':
resolution: {integrity: sha512-hwK1EQ+atUG/E7XcDV3KsTaA8op29pb8gbpVurpsqbLnGFkdTT4F/6V7Hy1cC2o/yOY+DKc/rxoIsH1uJS0cZg==}
'@llama-flow/core@0.4.1':
resolution: {integrity: sha512-xHhJMRmY16C1pYPWIonmLWPkkjTGuj1iVQCTXOM6sXajQ3r0+mEVERQCjPqf48tvX0K+szbdgxjg6wx+KwVqcg==}
peerDependencies:
'@modelcontextprotocol/sdk': ^1.7.0
hono: ^4.7.4
@@ -1563,10 +1425,10 @@ packages:
'@llamaindex/env': 0.1.29
zod: ^3.23.8
'@llamaindex/workflow@1.1.8':
resolution: {integrity: sha512-40uy5dWnqqlojq/fjlrIw8ZQOcFmZ2kSrvTyH65vQtypoTP8JWgBSTt3SFTWBUndwijNsfl9VvLh5S9INb0XBQ==}
'@llamaindex/workflow@1.1.3':
resolution: {integrity: sha512-B9wlN4xMtfbnIQW84wTC0GdyIaLGXYnZ4I0LENV2UM/0wqlXKI7YoBTO3cJvi6pO1sPA+K29Tqw822VsGF+GkA==}
peerDependencies:
'@llamaindex/core': 0.6.9
'@llamaindex/core': 0.6.6
'@llamaindex/env': 0.1.30
zod: ^3.23.8
@@ -3999,11 +3861,6 @@ packages:
resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==}
engines: {node: '>=0.12'}
esbuild@0.19.12:
resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
engines: {node: '>=12'}
hasBin: true
esbuild@0.21.5:
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
engines: {node: '>=12'}
@@ -6858,11 +6715,6 @@ packages:
engines: {node: '>=18.0.0'}
hasBin: true
tsx@4.7.2:
resolution: {integrity: sha512-BCNd4kz6fz12fyrgCTEdZHGJ9fWTGeUzXmQysh0RVocDY3h4frk05ZNCXSy4kIenF7y/QnrdiVpTsyNRn6vlAw==}
engines: {node: '>=18.0.0'}
hasBin: true
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
@@ -7878,153 +7730,102 @@ snapshots:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.19.12':
optional: true
'@esbuild/aix-ppc64@0.21.5':
optional: true
'@esbuild/aix-ppc64@0.25.3':
optional: true
'@esbuild/android-arm64@0.19.12':
optional: true
'@esbuild/android-arm64@0.21.5':
optional: true
'@esbuild/android-arm64@0.25.3':
optional: true
'@esbuild/android-arm@0.19.12':
optional: true
'@esbuild/android-arm@0.21.5':
optional: true
'@esbuild/android-arm@0.25.3':
optional: true
'@esbuild/android-x64@0.19.12':
optional: true
'@esbuild/android-x64@0.21.5':
optional: true
'@esbuild/android-x64@0.25.3':
optional: true
'@esbuild/darwin-arm64@0.19.12':
optional: true
'@esbuild/darwin-arm64@0.21.5':
optional: true
'@esbuild/darwin-arm64@0.25.3':
optional: true
'@esbuild/darwin-x64@0.19.12':
optional: true
'@esbuild/darwin-x64@0.21.5':
optional: true
'@esbuild/darwin-x64@0.25.3':
optional: true
'@esbuild/freebsd-arm64@0.19.12':
optional: true
'@esbuild/freebsd-arm64@0.21.5':
optional: true
'@esbuild/freebsd-arm64@0.25.3':
optional: true
'@esbuild/freebsd-x64@0.19.12':
optional: true
'@esbuild/freebsd-x64@0.21.5':
optional: true
'@esbuild/freebsd-x64@0.25.3':
optional: true
'@esbuild/linux-arm64@0.19.12':
optional: true
'@esbuild/linux-arm64@0.21.5':
optional: true
'@esbuild/linux-arm64@0.25.3':
optional: true
'@esbuild/linux-arm@0.19.12':
optional: true
'@esbuild/linux-arm@0.21.5':
optional: true
'@esbuild/linux-arm@0.25.3':
optional: true
'@esbuild/linux-ia32@0.19.12':
optional: true
'@esbuild/linux-ia32@0.21.5':
optional: true
'@esbuild/linux-ia32@0.25.3':
optional: true
'@esbuild/linux-loong64@0.19.12':
optional: true
'@esbuild/linux-loong64@0.21.5':
optional: true
'@esbuild/linux-loong64@0.25.3':
optional: true
'@esbuild/linux-mips64el@0.19.12':
optional: true
'@esbuild/linux-mips64el@0.21.5':
optional: true
'@esbuild/linux-mips64el@0.25.3':
optional: true
'@esbuild/linux-ppc64@0.19.12':
optional: true
'@esbuild/linux-ppc64@0.21.5':
optional: true
'@esbuild/linux-ppc64@0.25.3':
optional: true
'@esbuild/linux-riscv64@0.19.12':
optional: true
'@esbuild/linux-riscv64@0.21.5':
optional: true
'@esbuild/linux-riscv64@0.25.3':
optional: true
'@esbuild/linux-s390x@0.19.12':
optional: true
'@esbuild/linux-s390x@0.21.5':
optional: true
'@esbuild/linux-s390x@0.25.3':
optional: true
'@esbuild/linux-x64@0.19.12':
optional: true
'@esbuild/linux-x64@0.21.5':
optional: true
@@ -8034,9 +7835,6 @@ snapshots:
'@esbuild/netbsd-arm64@0.25.3':
optional: true
'@esbuild/netbsd-x64@0.19.12':
optional: true
'@esbuild/netbsd-x64@0.21.5':
optional: true
@@ -8046,45 +7844,30 @@ snapshots:
'@esbuild/openbsd-arm64@0.25.3':
optional: true
'@esbuild/openbsd-x64@0.19.12':
optional: true
'@esbuild/openbsd-x64@0.21.5':
optional: true
'@esbuild/openbsd-x64@0.25.3':
optional: true
'@esbuild/sunos-x64@0.19.12':
optional: true
'@esbuild/sunos-x64@0.21.5':
optional: true
'@esbuild/sunos-x64@0.25.3':
optional: true
'@esbuild/win32-arm64@0.19.12':
optional: true
'@esbuild/win32-arm64@0.21.5':
optional: true
'@esbuild/win32-arm64@0.25.3':
optional: true
'@esbuild/win32-ia32@0.19.12':
optional: true
'@esbuild/win32-ia32@0.21.5':
optional: true
'@esbuild/win32-ia32@0.25.3':
optional: true
'@esbuild/win32-x64@0.19.12':
optional: true
'@esbuild/win32-x64@0.21.5':
optional: true
@@ -8524,14 +8307,14 @@ snapshots:
'@lezer/highlight': 1.2.1
'@lezer/lr': 1.4.2
'@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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)':
'@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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:
'@modelcontextprotocol/sdk': 1.12.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
'@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13)':
'@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13)':
optionalDependencies:
'@modelcontextprotocol/sdk': 1.12.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)
@@ -8590,17 +8373,17 @@ snapshots:
- supports-color
- yjs
'@llamaindex/cloud@4.0.9(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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/cloud@4.0.9(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.4(@modelcontextprotocol/sdk@1.12.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)
'@llama-flow/core': 0.4.1(@modelcontextprotocol/sdk@1.12.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/cloud@4.0.9(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13))(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)':
'@llamaindex/cloud@4.0.9(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13))(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)':
dependencies:
'@llama-flow/core': 0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13)
'@llama-flow/core': 0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13)
'@llamaindex/core': 0.6.6
'@llamaindex/env': 0.1.30
p-retry: 6.2.1
@@ -8700,9 +8483,9 @@ snapshots:
'@llamaindex/env': 0.1.30
zod: 3.25.13
'@llamaindex/workflow@1.1.8(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(@modelcontextprotocol/sdk@1.12.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/workflow@1.1.3(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)(@modelcontextprotocol/sdk@1.12.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)':
dependencies:
'@llama-flow/core': 0.4.4(@modelcontextprotocol/sdk@1.12.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)
'@llama-flow/core': 0.4.1(@modelcontextprotocol/sdk@1.12.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
@@ -11287,32 +11070,6 @@ snapshots:
d: 1.0.2
ext: 1.7.0
esbuild@0.19.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.19.12
'@esbuild/android-arm': 0.19.12
'@esbuild/android-arm64': 0.19.12
'@esbuild/android-x64': 0.19.12
'@esbuild/darwin-arm64': 0.19.12
'@esbuild/darwin-x64': 0.19.12
'@esbuild/freebsd-arm64': 0.19.12
'@esbuild/freebsd-x64': 0.19.12
'@esbuild/linux-arm': 0.19.12
'@esbuild/linux-arm64': 0.19.12
'@esbuild/linux-ia32': 0.19.12
'@esbuild/linux-loong64': 0.19.12
'@esbuild/linux-mips64el': 0.19.12
'@esbuild/linux-ppc64': 0.19.12
'@esbuild/linux-riscv64': 0.19.12
'@esbuild/linux-s390x': 0.19.12
'@esbuild/linux-x64': 0.19.12
'@esbuild/netbsd-x64': 0.19.12
'@esbuild/openbsd-x64': 0.19.12
'@esbuild/sunos-x64': 0.19.12
'@esbuild/win32-arm64': 0.19.12
'@esbuild/win32-ia32': 0.19.12
'@esbuild/win32-x64': 0.19.12
esbuild@0.21.5:
optionalDependencies:
'@esbuild/aix-ppc64': 0.21.5
@@ -12553,9 +12310,9 @@ snapshots:
rfdc: 1.4.1
wrap-ansi: 9.0.0
llamaindex@0.11.1(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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):
llamaindex@0.11.1(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.4(@modelcontextprotocol/sdk@1.12.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/cloud': 4.0.9(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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)
@@ -12573,9 +12330,9 @@ snapshots:
- web-tree-sitter
- zod
llamaindex@0.11.1(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.13):
llamaindex@0.11.1(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13))(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.25.13):
dependencies:
'@llamaindex/cloud': 4.0.9(@llama-flow/core@0.4.4(@modelcontextprotocol/sdk@1.12.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.25.13))(@llamaindex/core@0.6.6)(@llamaindex/env@0.1.30)
'@llamaindex/cloud': 4.0.9(@llama-flow/core@0.4.1(@modelcontextprotocol/sdk@1.12.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.25.13))(@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)
@@ -14890,13 +14647,6 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
tsx@4.7.2:
dependencies:
esbuild: 0.19.12
get-tsconfig: 4.10.0
optionalDependencies:
fsevents: 2.3.3
tunnel-agent@0.6.0:
dependencies:
safe-buffer: 5.2.1
-20
View File
@@ -1,25 +1,5 @@
# @create-llama/llama-index-server
## 0.1.22
### Patch Changes
- 66b81e5: fix cannot catch the error raised from the workflow
- Updated dependencies [e2486eb]
- @llamaindex/server@0.2.8
## 0.1.21
### Patch Changes
- 1ff6eaf: Add support for upload file
- af9ad3c: feat: show document artifact after generating report
- a543a27: feat: bump chat-ui with inline artifact
- Updated dependencies [af9ad3c]
- Updated dependencies [a543a27]
- Updated dependencies [1ff6eaf]
- @llamaindex/server@0.2.7
## 0.1.20
### Patch Changes
@@ -25,10 +25,6 @@ class StreamHandler:
"""Cancel the workflow handler."""
await self.workflow_handler.cancel_run()
async def wait_for_completion(self) -> Any:
"""Wait for the workflow to finish."""
await self.workflow_handler
async def stream_events(self) -> AsyncGenerator[Any, None]:
"""Stream events through the processor chain."""
try:
@@ -195,7 +195,6 @@ async def _stream_content(
if not isinstance(event, (AgentInput, AgentSetup)):
yield VercelStreamResponse.convert_data(event.model_dump())
await handler.wait_for_completion()
except asyncio.CancelledError:
logger.warning("Client cancelled the request!")
await handler.cancel_run()
@@ -4,7 +4,6 @@ from llama_index.server.models.artifacts import (
ArtifactType,
CodeArtifactData,
DocumentArtifactData,
DocumentArtifactSource,
)
from llama_index.server.models.chat import ChatAPIMessage, ChatRequest
from llama_index.server.models.hitl import HumanInputEvent, HumanResponseEvent
@@ -21,7 +20,6 @@ __all__ = [
"ArtifactEvent",
"ArtifactType",
"DocumentArtifactData",
"DocumentArtifactSource",
"CodeArtifactData",
"ChatAPIMessage",
"ChatRequest",
@@ -1,6 +1,6 @@
import logging
from enum import Enum
from typing import List, Literal, Optional, Union
from typing import Literal, Optional, Union
from llama_index.core.workflow.events import Event
from llama_index.server.models.chat import ChatAPIMessage
@@ -21,16 +21,10 @@ class CodeArtifactData(BaseModel):
language: str
class DocumentArtifactSource(BaseModel):
id: str
# we can add more fields here
class DocumentArtifactData(BaseModel):
title: str
content: str
type: Literal["markdown", "html"]
sources: Optional[List[DocumentArtifactSource]] = None
class Artifact(BaseModel):
@@ -1,45 +0,0 @@
from typing import AsyncGenerator, Union
from llama_index.core.base.llms.types import (
CompletionResponse,
CompletionResponseAsyncGen,
)
from llama_index.core.workflow import Context
from llama_index.core.agent.workflow.workflow_events import AgentStream
async def write_response_to_stream(
res: Union[CompletionResponse, CompletionResponseAsyncGen],
ctx: Context,
current_agent_name: str = "assistant",
) -> str:
"""
Handle both streaming and non-streaming LLM responses.
Args:
res: The LLM response (either streaming or non-streaming)
ctx: The workflow context for writing events to stream
current_agent_name: The name of the current agent (default: "assistant")
Returns:
The final response text as a string
"""
final_response = ""
if isinstance(res, AsyncGenerator):
# Handle streaming response (CompletionResponseAsyncGen)
async for chunk in res:
ctx.write_event_to_stream(
AgentStream(
delta=chunk.delta or "",
response=final_response,
current_agent_name=current_agent_name,
tool_calls=[],
raw=chunk.raw or "",
)
)
final_response = chunk.text
else:
# Handle non-streaming response (CompletionResponse)
final_response = res.text
return final_response
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@create-llama/llama-index-server",
"private": true,
"version": "0.1.22",
"version": "0.1.20",
"type": "module",
"scripts": {
"prebuild": "uv run -- scripts/frontend.py --mode copy",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "llama-index-server"
version = "0.1.22"
version = "0.1.20"
description = "llama-index fastapi server"
readme = "README.md"
license = "MIT"
@@ -31,7 +31,6 @@ def chat_request() -> ChatRequest:
def mock_workflow_handler() -> AsyncMock:
handler = AsyncMock(spec=WorkflowHandler)
handler.accumulate_text = MagicMock()
handler.wait_for_completion = AsyncMock()
return handler
+1 -1
View File
@@ -1937,7 +1937,7 @@ wheels = [
[[package]]
name = "llama-index-server"
version = "0.1.21"
version = "0.1.20"
source = { editable = "." }
dependencies = [
{ name = "cachetools" },