mirror of
https://github.com/run-llama/create-llama.git
synced 2026-07-15 05:08:15 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ad5912b41f | |||
| 76502d28e7 | |||
| f4ca602da5 |
@@ -1,6 +0,0 @@
|
||||
---
|
||||
"@llamaindex/server": patch
|
||||
"llamaindex-server-examples": patch
|
||||
---
|
||||
|
||||
feat: add examples package for easily testing workflow
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@llamaindex/server": patch
|
||||
---
|
||||
|
||||
fix: use the correct versions of llamaindex and @llamaindex/workflow
|
||||
@@ -1,5 +1,12 @@
|
||||
# create-llama
|
||||
|
||||
## 0.5.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f4ca602: Add artifact use case for Typescript template
|
||||
- f4ca602: Update typescript use cases to use the new workflow engine
|
||||
|
||||
## 0.5.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -31,7 +31,7 @@ const installLlamaIndexServerTemplate = async ({
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await copy("workflow.ts", path.join(root, "src", "app"), {
|
||||
await copy("*.ts", path.join(root, "src", "app"), {
|
||||
parents: true,
|
||||
cwd: path.join(
|
||||
templatesDir,
|
||||
@@ -516,7 +516,7 @@ async function updatePackageJson({
|
||||
if (backend) {
|
||||
packageJson.dependencies = {
|
||||
...packageJson.dependencies,
|
||||
"@llamaindex/readers": "^2.0.0",
|
||||
"@llamaindex/readers": "^3.0.0",
|
||||
};
|
||||
|
||||
if (vectorDb && vectorDb in vectorDbDependencies) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-llama",
|
||||
"version": "0.5.12",
|
||||
"version": "0.5.13",
|
||||
"description": "Create LlamaIndex-powered apps with one command",
|
||||
"keywords": [
|
||||
"rag",
|
||||
|
||||
@@ -62,21 +62,19 @@ export const askSimpleQuestions = async (
|
||||
|
||||
let useLlamaCloud = false;
|
||||
|
||||
if (appType !== "artifacts") {
|
||||
const { language: newLanguage } = await prompts(
|
||||
{
|
||||
type: "select",
|
||||
name: "language",
|
||||
message: "What language do you want to use?",
|
||||
choices: [
|
||||
{ title: "Python (FastAPI)", value: "fastapi" },
|
||||
{ title: "Typescript (NextJS)", value: "nextjs" },
|
||||
],
|
||||
},
|
||||
questionHandlers,
|
||||
);
|
||||
language = newLanguage;
|
||||
}
|
||||
const { language: newLanguage } = await prompts(
|
||||
{
|
||||
type: "select",
|
||||
name: "language",
|
||||
message: "What language do you want to use?",
|
||||
choices: [
|
||||
{ title: "Python (FastAPI)", value: "fastapi" },
|
||||
{ title: "Typescript (NextJS)", value: "nextjs" },
|
||||
],
|
||||
},
|
||||
questionHandlers,
|
||||
);
|
||||
language = newLanguage;
|
||||
|
||||
const { useLlamaCloud: newUseLlamaCloud } = await prompts(
|
||||
{
|
||||
|
||||
+6
-2
@@ -1,5 +1,9 @@
|
||||
import { Document, LLamaCloudFileService, VectorStoreIndex } from "llamaindex";
|
||||
import { LlamaCloudIndex } from "llamaindex/cloud/LlamaCloudIndex";
|
||||
import {
|
||||
Document,
|
||||
LLamaCloudFileService,
|
||||
LlamaCloudIndex,
|
||||
VectorStoreIndex,
|
||||
} from "llamaindex";
|
||||
import { DocumentFile } from "../streaming/annotations";
|
||||
import { parseFile, storeFile } from "./helper";
|
||||
import { runPipeline } from "./pipeline";
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies = [
|
||||
"python-dotenv>=1.0.0",
|
||||
"pydantic<2.10",
|
||||
"llama-index>=0.12.1",
|
||||
"llama-parse>=0.6.21,<0.7.0",
|
||||
"cachetools>=5.3.3",
|
||||
"reflex>=0.6.2.post1",
|
||||
]
|
||||
|
||||
@@ -11,6 +11,7 @@ dependencies = [
|
||||
"python-dotenv>=1.0.0",
|
||||
"pydantic<2.10",
|
||||
"llama-index>=0.12.1",
|
||||
"llama-parse>=0.6.21,<0.7.0",
|
||||
"cachetools>=5.3.3",
|
||||
"reflex>=0.6.2.post1",
|
||||
]
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { LlamaCloudIndex } from "llamaindex/cloud/LlamaCloudIndex";
|
||||
import { LlamaCloudIndex } from "llamaindex";
|
||||
|
||||
type LlamaCloudDataSourceParams = {
|
||||
llamaCloudPipeline?: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LlamaCloudIndex } from "llamaindex/cloud/LlamaCloudIndex";
|
||||
import { LlamaCloudIndex } from "llamaindex";
|
||||
|
||||
type LlamaCloudDataSourceParams = {
|
||||
llamaCloudPipeline?: {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { agent } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { getIndex } from "./data";
|
||||
|
||||
export const workflowFactory = async (reqBody: any) => {
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
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
|
||||
|
||||
We have prepared two artifact workflows:
|
||||
|
||||
- [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.
|
||||
|
||||
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!
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
import { extractLastArtifact } from "@llamaindex/server";
|
||||
import { ChatMemoryBuffer, LLM, Settings } from "llamaindex";
|
||||
|
||||
import {
|
||||
agentStreamEvent,
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
startAgentEvent,
|
||||
stopAgentEvent,
|
||||
workflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
export const RequirementSchema = z.object({
|
||||
next_step: z.enum(["answering", "coding"]),
|
||||
language: z.string().nullable().optional(),
|
||||
file_name: z.string().nullable().optional(),
|
||||
requirement: z.string(),
|
||||
});
|
||||
|
||||
export type Requirement = z.infer<typeof RequirementSchema>;
|
||||
|
||||
export const UIEventSchema = z.object({
|
||||
type: z.literal("ui_event"),
|
||||
data: z.object({
|
||||
state: z
|
||||
.enum(["plan", "generate", "completed"])
|
||||
.describe(
|
||||
"The current state of the workflow: 'plan', 'generate', or 'completed'.",
|
||||
),
|
||||
requirement: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"An optional requirement creating or updating a code, if applicable.",
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
export type UIEvent = z.infer<typeof UIEventSchema>;
|
||||
const planEvent = workflowEvent<{
|
||||
userInput: string;
|
||||
context?: string | undefined;
|
||||
}>();
|
||||
|
||||
const generateArtifactEvent = workflowEvent<{
|
||||
requirement: Requirement;
|
||||
}>();
|
||||
|
||||
const synthesizeAnswerEvent = workflowEvent<object>();
|
||||
|
||||
const uiEvent = workflowEvent<UIEvent>();
|
||||
|
||||
const artifactEvent = workflowEvent<{
|
||||
type: "artifact";
|
||||
data: {
|
||||
type: "code";
|
||||
created_at: number;
|
||||
data: {
|
||||
language: string;
|
||||
file_name: string;
|
||||
code: string;
|
||||
};
|
||||
};
|
||||
}>();
|
||||
|
||||
export function createCodeArtifactWorkflow(reqBody: any, llm?: LLM) {
|
||||
if (!llm) {
|
||||
llm = Settings.llm;
|
||||
}
|
||||
const { withState, getContext } = createStatefulMiddleware(() => {
|
||||
return {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm,
|
||||
chatHistory: reqBody.chatHistory,
|
||||
}),
|
||||
lastArtifact: extractLastArtifact(reqBody),
|
||||
};
|
||||
});
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
workflow.handle([startAgentEvent], async ({ data: { userInput } }) => {
|
||||
// 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,
|
||||
});
|
||||
return planEvent.with({
|
||||
userInput,
|
||||
});
|
||||
});
|
||||
|
||||
workflow.handle([planEvent], async ({ data: planData }) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "plan",
|
||||
},
|
||||
}),
|
||||
);
|
||||
const user_msg = planData.userInput;
|
||||
const context = planData.context
|
||||
? `## The context is: \n${planData.context}\n`
|
||||
: "";
|
||||
const prompt = `
|
||||
You are a product analyst responsible for analyzing the user's request and providing the next step for code or document generation.
|
||||
You are helping user with their code artifact. To update the code, you need to plan a coding step.
|
||||
|
||||
Follow these instructions:
|
||||
1. Carefully analyze the conversation history and the user's request to determine what has been done and what the next step should be.
|
||||
2. The next step must be one of the following two options:
|
||||
- "coding": To make the changes to the current code.
|
||||
- "answering": If you don't need to update the current code or need clarification from the user.
|
||||
Important: Avoid telling the user to update the code themselves, you are the one who will update the code (by planning a coding step).
|
||||
3. If the next step is "coding", you may specify the language ("typescript" or "python") and file_name if known, otherwise set them to null.
|
||||
4. The requirement must be provided clearly what is the user request and what need to be done for the next step in details
|
||||
as precise and specific as possible, don't be stingy with in the requirement.
|
||||
5. If the next step is "answering", set language and file_name to null, and the requirement should describe what to answer or explain to the user.
|
||||
6. Be concise; only return the requirements for the next step.
|
||||
7. The requirements must be in the following format:
|
||||
\`\`\`json
|
||||
{
|
||||
"next_step": "answering" | "coding",
|
||||
"language": "typescript" | "python" | null,
|
||||
"file_name": string | null,
|
||||
"requirement": string
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Example 1:
|
||||
User request: Create a calculator app.
|
||||
You should return:
|
||||
\`\`\`json
|
||||
{
|
||||
"next_step": "coding",
|
||||
"language": "typescript",
|
||||
"file_name": "calculator.tsx",
|
||||
"requirement": "Generate code for a calculator app that has a simple UI with a display and button layout. The display should show the current input and the result. The buttons should include basic operators, numbers, clear, and equals. The calculation should work correctly."
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Example 2:
|
||||
User request: Explain how the game loop works.
|
||||
Context: You have already generated the code for a snake game.
|
||||
You should return:
|
||||
\`\`\`json
|
||||
{
|
||||
"next_step": "answering",
|
||||
"language": null,
|
||||
"file_name": null,
|
||||
"requirement": "The user is asking about the game loop. Explain how the game loop works."
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
${context}
|
||||
|
||||
Now, plan the user's next step for this request:
|
||||
${user_msg}
|
||||
`;
|
||||
|
||||
const response = await llm.complete({
|
||||
prompt,
|
||||
});
|
||||
// parse the response to Requirement
|
||||
// 1. use regex to find the json block
|
||||
const jsonBlock = response.text.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (!jsonBlock) {
|
||||
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}`,
|
||||
});
|
||||
|
||||
if (requirement.next_step === "coding") {
|
||||
return generateArtifactEvent.with({
|
||||
requirement,
|
||||
});
|
||||
} else {
|
||||
return synthesizeAnswerEvent.with({});
|
||||
}
|
||||
});
|
||||
|
||||
workflow.handle([generateArtifactEvent], async ({ data: planData }) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "generate",
|
||||
requirement: planData.requirement.requirement,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const previousArtifact = state.lastArtifact
|
||||
? JSON.stringify(state.lastArtifact)
|
||||
: "There is no previous artifact";
|
||||
const requirementText = planData.requirement.requirement;
|
||||
|
||||
const prompt = `
|
||||
You are a skilled developer who can help user with coding.
|
||||
You are given a task to generate or update a code for a given requirement.
|
||||
|
||||
## Follow these instructions:
|
||||
**1. Carefully read the user's requirements.**
|
||||
If any details are ambiguous or missing, make reasonable assumptions and clearly reflect those in your output.
|
||||
If the previous code is provided:
|
||||
+ Carefully analyze the code with the request to make the right changes.
|
||||
+ Avoid making a lot of changes from the previous code if the request is not to write the code from scratch again.
|
||||
**2. For code requests:**
|
||||
- If the user does not specify a framework or language, default to a React component using the Next.js framework.
|
||||
- For Next.js, use Shadcn UI components, Typescript, @types/node, @types/react, @types/react-dom, PostCSS, and TailwindCSS.
|
||||
The import pattern should be:
|
||||
\`\`\`typescript
|
||||
import { ComponentName } from "@/components/ui/component-name"
|
||||
import { Markdown } from "@llamaindex/chat-ui"
|
||||
import { cn } from "@/lib/utils"
|
||||
\`\`\`
|
||||
- Ensure the code is idiomatic, production-ready, and includes necessary imports.
|
||||
- Only generate code relevant to the user's request—do not add extra boilerplate.
|
||||
**3. Don't be verbose on response**
|
||||
- No other text or comments only return the code which wrapped by \`\`\`language\`\`\` block.
|
||||
- If the user's request is to update the code, only return the updated code.
|
||||
**4. Only the following languages are allowed: "typescript", "python".**
|
||||
**5. If there is no code to update, return the reason without any code block.**
|
||||
|
||||
## Example:
|
||||
\`\`\`typescript
|
||||
import React from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function MyComponent() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<Button>Click me</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
The previous code is:
|
||||
{previousArtifact}
|
||||
|
||||
Now, i have to generate the code for the following requirement:
|
||||
{requirement}
|
||||
`
|
||||
.replace("{previousArtifact}", previousArtifact)
|
||||
.replace("{requirement}", requirementText);
|
||||
|
||||
const response = await llm.complete({
|
||||
prompt,
|
||||
});
|
||||
|
||||
// Extract the code from the response
|
||||
const codeMatch = response.text.match(/```(\w+)([\s\S]*)```/);
|
||||
if (!codeMatch) {
|
||||
return synthesizeAnswerEvent.with({});
|
||||
}
|
||||
|
||||
const code = codeMatch[2].trim();
|
||||
|
||||
// Put the generated code to the memory
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content: `Updated the code: \n${response.text}`,
|
||||
});
|
||||
|
||||
// To show the Canvas panel for the artifact
|
||||
sendEvent(
|
||||
artifactEvent.with({
|
||||
type: "artifact",
|
||||
data: {
|
||||
type: "code",
|
||||
created_at: Date.now(),
|
||||
data: {
|
||||
language: planData.requirement.language || "",
|
||||
file_name: planData.requirement.file_name || "",
|
||||
code,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return synthesizeAnswerEvent.with({});
|
||||
});
|
||||
|
||||
workflow.handle([synthesizeAnswerEvent], async () => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
|
||||
const chatHistory = await state.memory.getMessages();
|
||||
const messages = [
|
||||
...chatHistory,
|
||||
{
|
||||
role: "system" as const,
|
||||
content: `
|
||||
You are a helpful assistant who is responsible for explaining the work to the user.
|
||||
Based on the conversation history, provide an answer to the user's question.
|
||||
The user has access to the code so avoid mentioning the whole code again in your response.
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
const responseStream = await llm.chat({
|
||||
messages,
|
||||
stream: true,
|
||||
});
|
||||
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "completed",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
let response = "";
|
||||
for await (const chunk of responseStream) {
|
||||
response += chunk.delta;
|
||||
sendEvent(
|
||||
agentStreamEvent.with({
|
||||
delta: chunk.delta,
|
||||
response: "",
|
||||
currentAgentName: "assistant",
|
||||
raw: chunk,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return stopAgentEvent.with({
|
||||
result: response,
|
||||
});
|
||||
});
|
||||
|
||||
return workflow;
|
||||
}
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
import { extractLastArtifact } from "@llamaindex/server";
|
||||
import { ChatMemoryBuffer, LLM, Settings } from "llamaindex";
|
||||
|
||||
import {
|
||||
agentStreamEvent,
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
startAgentEvent,
|
||||
stopAgentEvent,
|
||||
workflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
export const DocumentRequirementSchema = z.object({
|
||||
type: z.enum(["markdown", "html"]),
|
||||
title: z.string(),
|
||||
requirement: z.string(),
|
||||
});
|
||||
|
||||
export type DocumentRequirement = z.infer<typeof DocumentRequirementSchema>;
|
||||
|
||||
export const UIEventSchema = z.object({
|
||||
type: z.literal("ui_event"),
|
||||
data: z.object({
|
||||
state: z
|
||||
.enum(["plan", "generate", "completed"])
|
||||
.describe(
|
||||
"The current state of the workflow: 'plan', 'generate', or 'completed'.",
|
||||
),
|
||||
requirement: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
"An optional requirement creating or updating a document, if applicable.",
|
||||
),
|
||||
}),
|
||||
});
|
||||
|
||||
export type UIEvent = z.infer<typeof UIEventSchema>;
|
||||
|
||||
const planEvent = workflowEvent<{
|
||||
userInput: string;
|
||||
context?: string | undefined;
|
||||
}>();
|
||||
|
||||
const generateArtifactEvent = workflowEvent<{
|
||||
requirement: DocumentRequirement;
|
||||
}>();
|
||||
|
||||
const synthesizeAnswerEvent = workflowEvent<{
|
||||
requirement: DocumentRequirement;
|
||||
generatedArtifact: string;
|
||||
}>();
|
||||
|
||||
const uiEvent = workflowEvent<UIEvent>();
|
||||
|
||||
const artifactEvent = workflowEvent<{
|
||||
type: "artifact";
|
||||
data: {
|
||||
type: "document";
|
||||
created_at: number;
|
||||
data: {
|
||||
title: string;
|
||||
content: string;
|
||||
type: "markdown" | "html";
|
||||
};
|
||||
};
|
||||
}>();
|
||||
|
||||
export function createDocumentArtifactWorkflow(reqBody: any, llm?: LLM) {
|
||||
if (!llm) {
|
||||
llm = Settings.llm;
|
||||
}
|
||||
const { withState, getContext } = createStatefulMiddleware(() => {
|
||||
return {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm,
|
||||
chatHistory: reqBody.chatHistory,
|
||||
}),
|
||||
lastArtifact: extractLastArtifact(reqBody),
|
||||
};
|
||||
});
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
workflow.handle([startAgentEvent], async ({ data: { userInput } }) => {
|
||||
// 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,
|
||||
});
|
||||
return planEvent.with({
|
||||
userInput,
|
||||
context: state.lastArtifact
|
||||
? JSON.stringify(state.lastArtifact)
|
||||
: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
workflow.handle([planEvent], async ({ data: planData }) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "plan",
|
||||
},
|
||||
}),
|
||||
);
|
||||
const user_msg = planData.userInput;
|
||||
const context = planData.context
|
||||
? `## The context is: \n${planData.context}\n`
|
||||
: "";
|
||||
const prompt = `
|
||||
You are a documentation analyst responsible for analyzing the user's request and providing requirements for document generation or update.
|
||||
Follow these instructions:
|
||||
1. Carefully analyze the conversation history and the user's request to determine what has been done and what the next step should be.
|
||||
2. From the user's request, provide requirements for the next step of the document generation or update.
|
||||
3. Do not be verbose; only return the requirements for the next step of the document generation or update.
|
||||
4. Only the following document types are allowed: "markdown", "html".
|
||||
5. The requirement should be in the following format:
|
||||
\`\`\`json
|
||||
{
|
||||
"type": "markdown" | "html",
|
||||
"title": string,
|
||||
"requirement": string
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Example:
|
||||
User request: Create a project guideline document.
|
||||
You should return:
|
||||
\`\`\`json
|
||||
{
|
||||
"type": "markdown",
|
||||
"title": "Project Guideline",
|
||||
"requirement": "Generate a Markdown document that outlines the project goals, deliverables, and timeline. Include sections for introduction, objectives, deliverables, and timeline."
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
User request: Add a troubleshooting section to the guideline.
|
||||
You should return:
|
||||
\`\`\`json
|
||||
{
|
||||
"type": "markdown",
|
||||
"title": "Project Guideline",
|
||||
"requirement": "Add a 'Troubleshooting' section at the end of the document with common issues and solutions."
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
${context}
|
||||
|
||||
Now, please plan for the user's request:
|
||||
${user_msg}
|
||||
`;
|
||||
|
||||
const response = await llm.complete({
|
||||
prompt,
|
||||
});
|
||||
// Parse the response to DocumentRequirement
|
||||
const jsonBlock = response.text.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (!jsonBlock) {
|
||||
throw new Error("No JSON block found in the response.");
|
||||
}
|
||||
const requirement = DocumentRequirementSchema.parse(
|
||||
JSON.parse(jsonBlock[1]),
|
||||
);
|
||||
state.memory.put({
|
||||
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,
|
||||
});
|
||||
});
|
||||
|
||||
workflow.handle(
|
||||
[generateArtifactEvent],
|
||||
async ({ data: { requirement } }) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "generate",
|
||||
requirement: requirement.requirement,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const previousArtifact = state.lastArtifact
|
||||
? JSON.stringify(state.lastArtifact)
|
||||
: "";
|
||||
const requirementStr = JSON.stringify(requirement);
|
||||
|
||||
const prompt = `
|
||||
You are a skilled technical writer who can help users with documentation.
|
||||
You are given a task to generate or update a document for a given requirement.
|
||||
|
||||
## Follow these instructions:
|
||||
**1. Carefully read the user's requirements.**
|
||||
If any details are ambiguous or missing, make reasonable assumptions and clearly reflect those in your output.
|
||||
If the previous document is provided:
|
||||
+ Carefully analyze the document with the request to make the right changes.
|
||||
+ Avoid making unnecessary changes from the previous document if the request is not to rewrite it from scratch.
|
||||
**2. For document requests:**
|
||||
- If the user does not specify a type, default to Markdown.
|
||||
- Ensure the document is clear, well-structured, and grammatically correct.
|
||||
- Only generate content relevant to the user's request—do not add extra boilerplate.
|
||||
**3. Do not be verbose in your response.**
|
||||
- No other text or comments; only return the document content wrapped by the appropriate code block (\`\`\`markdown or \`\`\`html).
|
||||
- If the user's request is to update the document, only return the updated document.
|
||||
**4. Only the following types are allowed: "markdown", "html".**
|
||||
**5. If there is no change to the document, return the reason without any code block.**
|
||||
|
||||
## Example:
|
||||
\`\`\`markdown
|
||||
# Project Guideline
|
||||
|
||||
## Introduction
|
||||
...
|
||||
\`\`\`
|
||||
|
||||
The previous content is:
|
||||
${previousArtifact}
|
||||
|
||||
Now, please generate the document for the following requirement:
|
||||
${requirementStr}
|
||||
`;
|
||||
|
||||
const response = await llm.complete({
|
||||
prompt,
|
||||
});
|
||||
|
||||
// Extract the document from the response
|
||||
const docMatch = response.text.match(/```(markdown|html)([\s\S]*)```/);
|
||||
const generatedContent = response.text;
|
||||
|
||||
if (docMatch) {
|
||||
const content = docMatch[2].trim();
|
||||
const docType = docMatch[1] as "markdown" | "html";
|
||||
|
||||
// Put the generated document to the memory
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content: `Generated document: \n${response.text}`,
|
||||
});
|
||||
|
||||
// To show the Canvas panel for the artifact
|
||||
sendEvent(
|
||||
artifactEvent.with({
|
||||
type: "artifact",
|
||||
data: {
|
||||
type: "document",
|
||||
created_at: Date.now(),
|
||||
data: {
|
||||
title: requirement.title,
|
||||
content: content,
|
||||
type: docType,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return synthesizeAnswerEvent.with({
|
||||
requirement,
|
||||
generatedArtifact: generatedContent,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
workflow.handle([synthesizeAnswerEvent], async ({ data }) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { state } = getContext();
|
||||
|
||||
const chatHistory = await state.memory.getMessages();
|
||||
const messages = [
|
||||
...chatHistory,
|
||||
{
|
||||
role: "system" as const,
|
||||
content: `
|
||||
Your responsibility is to explain the work to the user.
|
||||
If there is no document to update, explain the reason.
|
||||
If the document is updated, just summarize what changed. Don't need to include the whole document again in the response.
|
||||
`,
|
||||
},
|
||||
];
|
||||
|
||||
const responseStream = await llm.chat({
|
||||
messages,
|
||||
stream: true,
|
||||
});
|
||||
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
state: "completed",
|
||||
requirement: data.requirement.requirement,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
let response = "";
|
||||
for await (const chunk of responseStream) {
|
||||
response += chunk.delta;
|
||||
sendEvent(
|
||||
agentStreamEvent.with({
|
||||
delta: chunk.delta,
|
||||
response: "",
|
||||
currentAgentName: "assistant",
|
||||
raw: chunk,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return stopAgentEvent.with({
|
||||
result: response,
|
||||
});
|
||||
});
|
||||
|
||||
return workflow;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
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 };
|
||||
+1
-1
@@ -31,7 +31,7 @@ You can configure [LLM model](https://ts.llamaindex.ai/docs/llamaindex/modules/l
|
||||
|
||||
## Custom UI Components
|
||||
|
||||
For Deep Research, we have a custom component located in `components/deep_research_event.jsx`. This is used to display the results of the deep research workflow in a more user-friendly way
|
||||
For Deep Research, we have a custom component located in `components/ui_event.jsx`. This is used to display the results of the deep research workflow in a more user-friendly way
|
||||
|
||||
### Generate a new UI Component from workflow event
|
||||
|
||||
|
||||
+210
-243
@@ -1,22 +1,21 @@
|
||||
import { toSourceEvent, toStreamGenerator } from "@llamaindex/server";
|
||||
import { toSourceEvent } from "@llamaindex/server";
|
||||
import {
|
||||
agentStreamEvent,
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
startAgentEvent,
|
||||
stopAgentEvent,
|
||||
workflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import {
|
||||
AgentInputData,
|
||||
AgentWorkflowContext,
|
||||
ChatMemoryBuffer,
|
||||
ChatResponseChunk,
|
||||
HandlerContext,
|
||||
LlamaCloudIndex,
|
||||
Metadata,
|
||||
MetadataMode,
|
||||
NodeWithScore,
|
||||
PromptTemplate,
|
||||
Settings,
|
||||
StartEvent,
|
||||
StopEvent as StopEventBase,
|
||||
ToolCallLLM,
|
||||
VectorStoreIndex,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "llamaindex";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { z } from "zod";
|
||||
@@ -25,12 +24,11 @@ import { getIndex } from "./data";
|
||||
// workflow factory
|
||||
export const workflowFactory = async (reqBody: any) => {
|
||||
const index = await getIndex(reqBody?.data);
|
||||
return new DeepResearchWorkflow(index);
|
||||
return getWorkflow(index);
|
||||
};
|
||||
|
||||
// workflow configs
|
||||
const MAX_QUESTIONS = 6; // max number of questions to research, research will stop when this number is reached
|
||||
const TIMEOUT = 360; // timeout in seconds
|
||||
const TOP_K = 10; // number of nodes to retrieve from the vector store
|
||||
|
||||
const createPlanResearchPrompt = new PromptTemplate({
|
||||
@@ -114,10 +112,10 @@ Create a well-structured outline for the research report that covers all the ans
|
||||
type ResearchQuestion = { questionId: string; question: string };
|
||||
type ResearchResult = ResearchQuestion & { answer: string };
|
||||
|
||||
class PlanResearchEvent extends WorkflowEvent<{}> {}
|
||||
class ResearchEvent extends WorkflowEvent<ResearchQuestion[]> {}
|
||||
class ReportEvent extends WorkflowEvent<{}> {}
|
||||
class StopEvent extends StopEventBase<AsyncGenerator<ChatResponseChunk>> {}
|
||||
// class PlanResearchEvent extends WorkflowEvent<{}> {}
|
||||
const planResearchEvent = workflowEvent<{}>();
|
||||
const researchEvent = workflowEvent<ResearchQuestion>();
|
||||
const reportEvent = workflowEvent<{}>();
|
||||
|
||||
export const UIEventSchema = z
|
||||
.object({
|
||||
@@ -140,221 +138,180 @@ export const UIEventSchema = z
|
||||
|
||||
type UIEventData = z.infer<typeof UIEventSchema>;
|
||||
|
||||
class UIEvent extends WorkflowEvent<{
|
||||
const uiEvent = workflowEvent<{
|
||||
type: "ui_event";
|
||||
data: UIEventData;
|
||||
}> {}
|
||||
}>();
|
||||
|
||||
// workflow definition
|
||||
class DeepResearchWorkflow extends Workflow<
|
||||
AgentWorkflowContext,
|
||||
AgentInputData,
|
||||
string
|
||||
> {
|
||||
#llm = Settings.llm as ToolCallLLM;
|
||||
#index?: VectorStoreIndex | LlamaCloudIndex;
|
||||
export function getWorkflow(index: VectorStoreIndex | LlamaCloudIndex) {
|
||||
const retriever = index.asRetriever({ similarityTopK: TOP_K });
|
||||
const { withState, getContext } = createStatefulMiddleware(() => {
|
||||
return {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm: Settings.llm,
|
||||
chatHistory: [],
|
||||
}),
|
||||
contextNodes: [] as NodeWithScore<Metadata>[],
|
||||
userRequest: "",
|
||||
totalQuestions: 0,
|
||||
researchResults: [] as ResearchResult[],
|
||||
};
|
||||
});
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
userRequest: string = "";
|
||||
totalQuestions: number = 0;
|
||||
contextNodes: NodeWithScore<Metadata>[] = [];
|
||||
memory: ChatMemoryBuffer = new ChatMemoryBuffer({ llm: Settings.llm });
|
||||
|
||||
constructor(index: VectorStoreIndex | LlamaCloudIndex) {
|
||||
super({ timeout: TIMEOUT });
|
||||
this.#index = index;
|
||||
this.addWorkflowSteps();
|
||||
}
|
||||
|
||||
addWorkflowSteps() {
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [StartEvent<AgentInputData>],
|
||||
outputs: [PlanResearchEvent],
|
||||
},
|
||||
this.handleStartWorkflow,
|
||||
);
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [PlanResearchEvent],
|
||||
outputs: [ResearchEvent, ReportEvent, StopEvent],
|
||||
},
|
||||
this.handlePlanResearch,
|
||||
);
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [ResearchEvent],
|
||||
outputs: [PlanResearchEvent],
|
||||
},
|
||||
this.handleResearch,
|
||||
);
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [ReportEvent],
|
||||
outputs: [StopEvent],
|
||||
},
|
||||
this.handleReport,
|
||||
);
|
||||
}
|
||||
|
||||
async initWorkflow(data: AgentInputData) {
|
||||
workflow.handle([startAgentEvent], async ({ data }) => {
|
||||
const { userInput, chatHistory = [] } = data;
|
||||
const { sendEvent, state } = getContext();
|
||||
if (!userInput) throw new Error("Invalid input");
|
||||
|
||||
this.userRequest = userInput;
|
||||
|
||||
await this.memory.set(chatHistory);
|
||||
await this.memory.put({ role: "user", content: userInput });
|
||||
}
|
||||
|
||||
handleStartWorkflow = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: StartEvent<AgentInputData>,
|
||||
): Promise<PlanResearchEvent> => {
|
||||
await this.initWorkflow(ev.data);
|
||||
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
state.memory.set(chatHistory);
|
||||
state.memory.put({ role: "user", content: userInput });
|
||||
state.userRequest = userInput;
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "retrieve", state: "inprogress" },
|
||||
data: {
|
||||
event: "retrieve",
|
||||
state: "inprogress",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const retrievedNodes = await this.retriever.retrieve(this.userRequest);
|
||||
const retrievedNodes = await retriever.retrieve(userInput);
|
||||
|
||||
ctx.sendEvent(toSourceEvent(retrievedNodes));
|
||||
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
sendEvent(toSourceEvent(retrievedNodes));
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "retrieve", state: "done" },
|
||||
}),
|
||||
);
|
||||
|
||||
this.contextNodes = retrievedNodes;
|
||||
state.contextNodes.push(...retrievedNodes);
|
||||
|
||||
return new PlanResearchEvent({});
|
||||
};
|
||||
return planResearchEvent.with({});
|
||||
});
|
||||
|
||||
handlePlanResearch = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: PlanResearchEvent,
|
||||
): Promise<ResearchEvent | ReportEvent | StopEvent> => {
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
workflow.handle([planResearchEvent], async ({ data }) => {
|
||||
const { sendEvent, state, stream } = getContext();
|
||||
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "analyze", state: "inprogress" },
|
||||
}),
|
||||
);
|
||||
|
||||
const { decision, researchQuestions, cancelReason } =
|
||||
await this.createResearchPlan();
|
||||
await createResearchPlan(
|
||||
state.memory,
|
||||
state.contextNodes
|
||||
.map((node) => node.node.getContent(MetadataMode.NONE))
|
||||
.join("\n"),
|
||||
enhancedPrompt(state.totalQuestions),
|
||||
state.userRequest,
|
||||
);
|
||||
|
||||
// Stop workflow due to decision from LLM
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "analyze", state: "done" },
|
||||
}),
|
||||
);
|
||||
if (decision === "cancel") {
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "analyze", state: "done" },
|
||||
}),
|
||||
);
|
||||
return new StopEvent(
|
||||
toStreamGenerator(
|
||||
cancelReason ?? "Research cancelled without any reason.",
|
||||
),
|
||||
);
|
||||
return agentStreamEvent.with({
|
||||
delta: cancelReason ?? "Research cancelled without any reason.",
|
||||
response: cancelReason ?? "Research cancelled without any reason.",
|
||||
currentAgentName: "",
|
||||
raw: null,
|
||||
});
|
||||
}
|
||||
|
||||
// Trigger research from generated questions
|
||||
if (decision === "research") {
|
||||
this.memory.put({
|
||||
if (decision === "research" && researchQuestions.length > 0) {
|
||||
state.totalQuestions += researchQuestions.length;
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content:
|
||||
"We need to find answers to the following questions:\n" +
|
||||
researchQuestions.join("\n"),
|
||||
});
|
||||
|
||||
researchQuestions.forEach(({ questionId: id, question }) => {
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "answer", state: "pending", id, question },
|
||||
}),
|
||||
);
|
||||
sendEvent(researchEvent.with({ questionId: id, question }));
|
||||
});
|
||||
|
||||
return new ResearchEvent(researchQuestions);
|
||||
const events = await stream
|
||||
.until(() => state.researchResults.length === researchQuestions.length)
|
||||
.toArray();
|
||||
return planResearchEvent.with({});
|
||||
}
|
||||
|
||||
// Resarch done, start writing report
|
||||
this.memory.put({
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content: "No more idea to analyze. We should report the answers.",
|
||||
});
|
||||
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: { event: "analyze", state: "done" },
|
||||
}),
|
||||
);
|
||||
return reportEvent.with({});
|
||||
});
|
||||
|
||||
return new ReportEvent({});
|
||||
};
|
||||
workflow.handle([researchEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
const { questionId, question } = data;
|
||||
|
||||
handleResearch = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: ResearchEvent,
|
||||
): Promise<PlanResearchEvent> => {
|
||||
const researchQuestions = ev.data;
|
||||
|
||||
// Answer questions in parallel
|
||||
const researchResults: ResearchResult[] = await Promise.all(
|
||||
researchQuestions.map(async ({ questionId: id, question }) => {
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
type: "ui_event",
|
||||
data: { event: "answer", state: "inprogress", id, question },
|
||||
}),
|
||||
);
|
||||
|
||||
const answer = await this.answerQuestion(question);
|
||||
|
||||
ctx.sendEvent(
|
||||
new UIEvent({
|
||||
type: "ui_event",
|
||||
data: { event: "answer", state: "done", id, question, answer },
|
||||
}),
|
||||
);
|
||||
|
||||
return { questionId: id, question, answer };
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
event: "answer",
|
||||
state: "inprogress",
|
||||
id: questionId,
|
||||
question,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Save answers to memory
|
||||
researchResults.forEach(({ question, answer }) => {
|
||||
this.memory.put({
|
||||
role: "assistant",
|
||||
content: `<Question>${question}</Question>\n<Answer>${answer}</Answer>`,
|
||||
});
|
||||
});
|
||||
const answer = await answerQuestion(
|
||||
contextStr(state.contextNodes),
|
||||
question,
|
||||
);
|
||||
state.researchResults.push({ questionId, question, answer });
|
||||
|
||||
this.memory.put({
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content:
|
||||
"Researched all the questions. Now, I need to analyze if it's ready to write a report or need to research more.",
|
||||
content: `<Question>${question}</Question>\n<Answer>${answer}</Answer>`,
|
||||
});
|
||||
|
||||
this.totalQuestions += researchResults.length;
|
||||
|
||||
return new PlanResearchEvent({});
|
||||
};
|
||||
|
||||
handleReport = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: ReportEvent,
|
||||
): Promise<StopEvent> => {
|
||||
const chatHistory = await this.memory.getAllMessages();
|
||||
sendEvent(
|
||||
uiEvent.with({
|
||||
type: "ui_event",
|
||||
data: {
|
||||
event: "answer",
|
||||
state: "done",
|
||||
id: questionId,
|
||||
question,
|
||||
answer,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
workflow.handle([reportEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
const chatHistory = await state.memory.getAllMessages();
|
||||
const messages = chatHistory.concat([
|
||||
{
|
||||
role: "system",
|
||||
@@ -367,81 +324,91 @@ class DeepResearchWorkflow extends Workflow<
|
||||
},
|
||||
]);
|
||||
|
||||
const stream = await this.llm.chat({ messages, stream: true });
|
||||
|
||||
return new StopEvent(toStreamGenerator(stream));
|
||||
};
|
||||
|
||||
get llm() {
|
||||
if (!this.#llm.supportToolCall) throw new Error("LLM is not a ToolCallLLM");
|
||||
return this.#llm;
|
||||
}
|
||||
|
||||
get retriever() {
|
||||
if (!this.#index) throw new Error("Index is not initialized");
|
||||
return this.#index.asRetriever({ similarityTopK: TOP_K });
|
||||
}
|
||||
|
||||
get contextStr() {
|
||||
return this.contextNodes
|
||||
.map((node) => {
|
||||
const nodeId = node.node.id_;
|
||||
const nodeContent = node.node.getContent(MetadataMode.NONE);
|
||||
return `<Citation id='${nodeId}'>\n${nodeContent}</Citation id='${nodeId}'>`;
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
get enhancedPrompt() {
|
||||
if (this.totalQuestions === 0) {
|
||||
return "The student has no questions to research. Let start by asking some questions.";
|
||||
const stream = await Settings.llm.chat({ messages, stream: true });
|
||||
let response = "";
|
||||
for await (const chunk of stream) {
|
||||
response += chunk.delta;
|
||||
sendEvent(
|
||||
agentStreamEvent.with({
|
||||
delta: chunk.delta,
|
||||
response,
|
||||
currentAgentName: "",
|
||||
raw: stream,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (this.totalQuestions > MAX_QUESTIONS) {
|
||||
return `The student has researched ${this.totalQuestions} questions. Should cancel the research if the context is not enough to write a report.`;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
async createResearchPlan() {
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
|
||||
const conversationContext = chatHistory
|
||||
.map((message) => `${message.role}: ${message.content}`)
|
||||
.join("\n");
|
||||
|
||||
const prompt = createPlanResearchPrompt.format({
|
||||
context_str: this.contextStr,
|
||||
conversation_context: conversationContext,
|
||||
enhanced_prompt: this.enhancedPrompt,
|
||||
user_request: this.userRequest,
|
||||
return stopAgentEvent.with({
|
||||
result: response,
|
||||
});
|
||||
});
|
||||
|
||||
const responseFormat = z.object({
|
||||
decision: z.enum(["research", "write", "cancel"]),
|
||||
researchQuestions: z.array(z.string()),
|
||||
cancelReason: z.string().optional(),
|
||||
});
|
||||
|
||||
const result = await this.llm.complete({ prompt, responseFormat });
|
||||
const plan = JSON.parse(result.text) as z.infer<typeof responseFormat>;
|
||||
|
||||
return {
|
||||
...plan,
|
||||
researchQuestions: plan.researchQuestions.map((question) => ({
|
||||
questionId: randomUUID(),
|
||||
question,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async answerQuestion(question: string) {
|
||||
const prompt = researchPrompt.format({
|
||||
context_str: this.contextStr,
|
||||
question,
|
||||
});
|
||||
const result = await this.llm.complete({ prompt });
|
||||
return result.text;
|
||||
}
|
||||
return workflow;
|
||||
}
|
||||
|
||||
const createResearchPlan = async (
|
||||
memory: ChatMemoryBuffer,
|
||||
contextStr: string,
|
||||
enhancedPrompt: string,
|
||||
userRequest: string,
|
||||
) => {
|
||||
const chatHistory = await memory.getMessages();
|
||||
|
||||
const conversationContext = chatHistory
|
||||
.map((message) => `${message.role}: ${message.content}`)
|
||||
.join("\n");
|
||||
|
||||
const prompt = createPlanResearchPrompt.format({
|
||||
context_str: contextStr,
|
||||
conversation_context: conversationContext,
|
||||
enhanced_prompt: enhancedPrompt,
|
||||
user_request: userRequest,
|
||||
});
|
||||
|
||||
const responseFormat = z.object({
|
||||
decision: z.enum(["research", "write", "cancel"]),
|
||||
researchQuestions: z.array(z.string()),
|
||||
cancelReason: z.string().optional(),
|
||||
});
|
||||
|
||||
const result = await Settings.llm.complete({ prompt, responseFormat });
|
||||
const plan = JSON.parse(result.text) as z.infer<typeof responseFormat>;
|
||||
|
||||
return {
|
||||
...plan,
|
||||
researchQuestions: plan.researchQuestions.map((question) => ({
|
||||
questionId: randomUUID(),
|
||||
question,
|
||||
})),
|
||||
};
|
||||
};
|
||||
|
||||
const contextStr = (contextNodes: NodeWithScore<Metadata>[]) => {
|
||||
return contextNodes
|
||||
.map((node) => {
|
||||
const nodeId = node.node.id_;
|
||||
const nodeContent = node.node.getContent(MetadataMode.NONE);
|
||||
return `<Citation id='${nodeId}'>\n${nodeContent}</Citation id='${nodeId}'>`;
|
||||
})
|
||||
.join("\n");
|
||||
};
|
||||
|
||||
const enhancedPrompt = (totalQuestions: number) => {
|
||||
if (totalQuestions === 0) {
|
||||
return "The student has no questions to research. Let start by providing some questions for the student to research.";
|
||||
}
|
||||
|
||||
if (totalQuestions >= MAX_QUESTIONS) {
|
||||
return `The student has researched ${totalQuestions} questions. Should proceeding writing report or cancel the research if the answers are not enough to write a report.`;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const answerQuestion = async (contextStr: string, question: string) => {
|
||||
const prompt = researchPrompt.format({
|
||||
context_str: contextStr,
|
||||
question,
|
||||
});
|
||||
const result = await Settings.llm.complete({ prompt });
|
||||
return result.text;
|
||||
};
|
||||
|
||||
+133
-211
@@ -6,27 +6,25 @@ import {
|
||||
interpreter,
|
||||
} from "@llamaindex/tools";
|
||||
import {
|
||||
AgentInputData,
|
||||
AgentWorkflowContext,
|
||||
agentStreamEvent,
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
startAgentEvent,
|
||||
stopAgentEvent,
|
||||
workflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import {
|
||||
BaseToolWithCall,
|
||||
ChatMemoryBuffer,
|
||||
ChatMessage,
|
||||
ChatResponseChunk,
|
||||
HandlerContext,
|
||||
Metadata,
|
||||
NodeWithScore,
|
||||
Settings,
|
||||
StartEvent,
|
||||
StopEvent,
|
||||
ToolCall,
|
||||
ToolCallLLM,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "llamaindex";
|
||||
import { getIndex } from "./data";
|
||||
|
||||
const TIMEOUT = 360 * 1000;
|
||||
|
||||
export async function workflowFactory(reqBody: any) {
|
||||
const index = await getIndex(reqBody?.data);
|
||||
|
||||
@@ -47,28 +45,18 @@ export async function workflowFactory(reqBody: any) {
|
||||
});
|
||||
const documentGeneratorTool = documentGenerator();
|
||||
|
||||
return new FinancialReportWorkflow({
|
||||
return getWorkflow(
|
||||
queryEngineTool,
|
||||
codeInterpreterTool,
|
||||
documentGeneratorTool,
|
||||
timeout: TIMEOUT,
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
// Create a custom event type
|
||||
class InputEvent extends WorkflowEvent<{ input: ChatMessage[] }> {}
|
||||
|
||||
class ResearchEvent extends WorkflowEvent<{
|
||||
toolCalls: ToolCall[];
|
||||
}> {}
|
||||
|
||||
class AnalyzeEvent extends WorkflowEvent<{
|
||||
input: ChatMessage | ToolCall[];
|
||||
}> {}
|
||||
|
||||
class ReportGenerationEvent extends WorkflowEvent<{
|
||||
toolCalls: ToolCall[];
|
||||
}> {}
|
||||
// workflow events
|
||||
const inputEvent = workflowEvent<{ input: ChatMessage[] }>();
|
||||
const researchEvent = workflowEvent<{ toolCalls: ToolCall[] }>();
|
||||
const analyzeEvent = workflowEvent<{ input: ChatMessage | ToolCall[] }>();
|
||||
const reportGenerationEvent = workflowEvent<{ toolCalls: ToolCall[] }>();
|
||||
|
||||
const DEFAULT_SYSTEM_PROMPT = `
|
||||
You are a financial analyst who are given a set of tools to help you.
|
||||
@@ -76,173 +64,103 @@ It's good to using appropriate tools for the user request and always use the inf
|
||||
For the query engine tool, you should break down the user request into a list of queries and call the tool with the queries.
|
||||
`;
|
||||
|
||||
class FinancialReportWorkflow extends Workflow<
|
||||
AgentWorkflowContext,
|
||||
AgentInputData,
|
||||
string
|
||||
> {
|
||||
llm: ToolCallLLM;
|
||||
memory: ChatMemoryBuffer;
|
||||
queryEngineTool: BaseToolWithCall;
|
||||
codeInterpreterTool: BaseToolWithCall;
|
||||
documentGeneratorTool: BaseToolWithCall;
|
||||
systemPrompt?: string;
|
||||
|
||||
constructor(options: {
|
||||
queryEngineTool: BaseToolWithCall;
|
||||
codeInterpreterTool: BaseToolWithCall;
|
||||
documentGeneratorTool: BaseToolWithCall;
|
||||
systemPrompt?: string;
|
||||
verbose?: boolean;
|
||||
timeout?: number;
|
||||
}) {
|
||||
super({
|
||||
verbose: options?.verbose ?? false,
|
||||
timeout: options?.timeout ?? 360,
|
||||
});
|
||||
|
||||
this.llm = Settings.llm as ToolCallLLM;
|
||||
if (!this.llm.supportToolCall) {
|
||||
throw new Error("LLM is not a ToolCallLLM");
|
||||
}
|
||||
this.systemPrompt = options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
||||
this.queryEngineTool = options.queryEngineTool;
|
||||
this.codeInterpreterTool = options.codeInterpreterTool;
|
||||
|
||||
this.documentGeneratorTool = options.documentGeneratorTool;
|
||||
this.memory = new ChatMemoryBuffer({ llm: this.llm, chatHistory: [] });
|
||||
|
||||
// Add steps
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [StartEvent<AgentInputData>],
|
||||
outputs: [InputEvent],
|
||||
},
|
||||
this.prepareChatHistory,
|
||||
);
|
||||
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [InputEvent],
|
||||
outputs: [
|
||||
InputEvent,
|
||||
ResearchEvent,
|
||||
AnalyzeEvent,
|
||||
ReportGenerationEvent,
|
||||
StopEvent,
|
||||
],
|
||||
},
|
||||
this.handleLLMInput,
|
||||
);
|
||||
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [ResearchEvent],
|
||||
outputs: [AnalyzeEvent],
|
||||
},
|
||||
this.handleResearch,
|
||||
);
|
||||
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [AnalyzeEvent],
|
||||
outputs: [InputEvent],
|
||||
},
|
||||
this.handleAnalyze,
|
||||
);
|
||||
|
||||
this.addStep(
|
||||
{
|
||||
inputs: [ReportGenerationEvent],
|
||||
outputs: [InputEvent],
|
||||
},
|
||||
this.handleReportGeneration,
|
||||
);
|
||||
// workflow definition
|
||||
export function getWorkflow(
|
||||
queryEngineTool: BaseToolWithCall,
|
||||
codeInterpreterTool: BaseToolWithCall,
|
||||
documentGeneratorTool: BaseToolWithCall,
|
||||
) {
|
||||
const llm = Settings.llm as ToolCallLLM;
|
||||
if (!llm.supportToolCall) {
|
||||
throw new Error("LLM is not a ToolCallLLM");
|
||||
}
|
||||
const { withState, getContext } = createStatefulMiddleware(() => ({
|
||||
memory: new ChatMemoryBuffer({ llm, chatHistory: [] }),
|
||||
}));
|
||||
|
||||
prepareChatHistory = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: StartEvent<AgentInputData>,
|
||||
): Promise<InputEvent> => {
|
||||
const { userInput, chatHistory = [] } = ev.data;
|
||||
const workflow = withState(createWorkflow());
|
||||
|
||||
// Add steps
|
||||
workflow.handle([startAgentEvent], async ({ data }) => {
|
||||
const { state } = getContext();
|
||||
const { userInput, chatHistory = [] } = data;
|
||||
if (!userInput) throw new Error("Invalid input");
|
||||
|
||||
this.memory.set(chatHistory);
|
||||
state.memory.set(chatHistory);
|
||||
|
||||
if (this.systemPrompt) {
|
||||
this.memory.put({ role: "system", content: this.systemPrompt });
|
||||
}
|
||||
state.memory.put({ role: "system", content: DEFAULT_SYSTEM_PROMPT });
|
||||
|
||||
this.memory.put({ role: "user", content: userInput });
|
||||
state.memory.put({ role: "user", content: userInput });
|
||||
|
||||
const messages = await this.memory.getMessages();
|
||||
return new InputEvent({ input: messages });
|
||||
};
|
||||
const messages = await state.memory.getMessages();
|
||||
return inputEvent.with({ input: messages });
|
||||
});
|
||||
|
||||
handleLLMInput = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: InputEvent,
|
||||
): Promise<
|
||||
| InputEvent
|
||||
| ResearchEvent
|
||||
| AnalyzeEvent
|
||||
| ReportGenerationEvent
|
||||
| StopEvent<AsyncGenerator<ChatResponseChunk, any, any> | undefined>
|
||||
> => {
|
||||
const chatHistory = ev.data.input;
|
||||
workflow.handle([inputEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
const chatHistory = data.input;
|
||||
|
||||
const tools = [
|
||||
this.codeInterpreterTool,
|
||||
this.documentGeneratorTool,
|
||||
this.queryEngineTool,
|
||||
];
|
||||
const tools = [codeInterpreterTool, documentGeneratorTool, queryEngineTool];
|
||||
|
||||
const toolCallResponse = await chatWithTools(this.llm, tools, chatHistory);
|
||||
const toolCallResponse = await chatWithTools(llm, tools, chatHistory);
|
||||
|
||||
if (!toolCallResponse.hasToolCall()) {
|
||||
return new StopEvent(toolCallResponse.responseGenerator);
|
||||
const generator = toolCallResponse.responseGenerator;
|
||||
let response = "";
|
||||
if (generator) {
|
||||
for await (const chunk of generator) {
|
||||
response += chunk.delta;
|
||||
sendEvent(
|
||||
agentStreamEvent.with({
|
||||
delta: chunk.delta,
|
||||
response,
|
||||
currentAgentName: "LLM", // Or derive from context if needed
|
||||
raw: chunk.raw,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
return stopAgentEvent.with({ result: response });
|
||||
}
|
||||
|
||||
if (toolCallResponse.hasMultipleTools()) {
|
||||
this.memory.put({
|
||||
state.memory.put({
|
||||
role: "assistant",
|
||||
content:
|
||||
"Calling different tools is not allowed. Please only use multiple calls of the same tool.",
|
||||
});
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
return new InputEvent({ input: chatHistory });
|
||||
const newChatHistory = await state.memory.getMessages();
|
||||
return inputEvent.with({ input: newChatHistory });
|
||||
}
|
||||
|
||||
// Put the LLM tool call message into the memory
|
||||
// And trigger the next step according to the tool call
|
||||
if (toolCallResponse.toolCallMessage) {
|
||||
this.memory.put(toolCallResponse.toolCallMessage);
|
||||
state.memory.put(toolCallResponse.toolCallMessage);
|
||||
}
|
||||
const toolName = toolCallResponse.getToolNames()[0];
|
||||
switch (toolName) {
|
||||
case this.codeInterpreterTool.metadata.name:
|
||||
return new AnalyzeEvent({
|
||||
case codeInterpreterTool.metadata.name:
|
||||
return analyzeEvent.with({
|
||||
input: toolCallResponse.toolCalls,
|
||||
});
|
||||
case this.documentGeneratorTool.metadata.name:
|
||||
return new ReportGenerationEvent({
|
||||
case documentGeneratorTool.metadata.name:
|
||||
return reportGenerationEvent.with({
|
||||
toolCalls: toolCallResponse.toolCalls,
|
||||
});
|
||||
default:
|
||||
if (this.queryEngineTool.metadata.name === toolName) {
|
||||
return new ResearchEvent({
|
||||
if (queryEngineTool.metadata.name === toolName) {
|
||||
return researchEvent.with({
|
||||
toolCalls: toolCallResponse.toolCalls,
|
||||
});
|
||||
}
|
||||
throw new Error(`Unknown tool: ${toolName}`);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
handleResearch = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: ResearchEvent,
|
||||
): Promise<AnalyzeEvent> => {
|
||||
ctx.sendEvent(
|
||||
workflow.handle([researchEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Researcher",
|
||||
text: "Researching data",
|
||||
@@ -250,13 +168,13 @@ class FinancialReportWorkflow extends Workflow<
|
||||
}),
|
||||
);
|
||||
|
||||
const { toolCalls } = ev.data;
|
||||
const { toolCalls } = data;
|
||||
|
||||
const toolMsgs = await callTools({
|
||||
tools: [this.queryEngineTool],
|
||||
tools: [queryEngineTool],
|
||||
toolCalls,
|
||||
writeEvent: (text, step) => {
|
||||
ctx.sendEvent(
|
||||
sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Researcher",
|
||||
text,
|
||||
@@ -268,7 +186,7 @@ class FinancialReportWorkflow extends Workflow<
|
||||
},
|
||||
});
|
||||
for (const toolMsg of toolMsgs) {
|
||||
this.memory.put(toolMsg);
|
||||
state.memory.put(toolMsg);
|
||||
}
|
||||
|
||||
const sourcesNodes: NodeWithScore<Metadata>[] = toolMsgs
|
||||
@@ -277,26 +195,25 @@ class FinancialReportWorkflow extends Workflow<
|
||||
.filter(Boolean);
|
||||
|
||||
if (sourcesNodes.length > 0) {
|
||||
ctx.sendEvent(toSourceEvent(sourcesNodes));
|
||||
sendEvent(toSourceEvent(sourcesNodes));
|
||||
}
|
||||
|
||||
return new AnalyzeEvent({
|
||||
// Send a message indicating research is done, triggering analysis
|
||||
return analyzeEvent.with({
|
||||
input: {
|
||||
role: "assistant",
|
||||
content:
|
||||
"I have finished researching the data, please analyze the data.",
|
||||
},
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Analyze a research result or a tool call for code interpreter from the LLM
|
||||
*/
|
||||
handleAnalyze = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: AnalyzeEvent,
|
||||
): Promise<InputEvent> => {
|
||||
ctx.sendEvent(
|
||||
workflow.handle([analyzeEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Analyst",
|
||||
text: "Analyzing data",
|
||||
@@ -305,8 +222,8 @@ class FinancialReportWorkflow extends Workflow<
|
||||
);
|
||||
// Request by workflow LLM, input is a list of tool calls
|
||||
let toolCalls: ToolCall[] = [];
|
||||
if (Array.isArray(ev.data.input)) {
|
||||
toolCalls = ev.data.input;
|
||||
if (Array.isArray(data.input)) {
|
||||
toolCalls = data.input;
|
||||
} else {
|
||||
// Requested by Researcher, input is a ChatMessage
|
||||
// We start new LLM chat specifically for analyzing the data
|
||||
@@ -320,63 +237,65 @@ class FinancialReportWorkflow extends Workflow<
|
||||
|
||||
// Clone the current chat history
|
||||
// Add the analysis system prompt and the message from the researcher
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
const currentChatHistory = await state.memory.getMessages();
|
||||
const newChatHistory = [
|
||||
...chatHistory,
|
||||
...currentChatHistory,
|
||||
{ role: "system", content: analysisPrompt },
|
||||
ev.data.input,
|
||||
data.input, // This is the ChatMessage from the research step
|
||||
];
|
||||
const toolCallResponse = await chatWithTools(
|
||||
this.llm,
|
||||
[this.codeInterpreterTool],
|
||||
llm,
|
||||
[codeInterpreterTool],
|
||||
newChatHistory as ChatMessage[],
|
||||
);
|
||||
|
||||
if (!toolCallResponse.hasToolCall()) {
|
||||
this.memory.put(await toolCallResponse.asFullResponse());
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
return new InputEvent({ input: chatHistory });
|
||||
// If no tool call needed for analysis, put the response directly
|
||||
state.memory.put(await toolCallResponse.asFullResponse());
|
||||
const finalChatHistory = await state.memory.getMessages();
|
||||
return inputEvent.with({ input: finalChatHistory });
|
||||
} else {
|
||||
this.memory.put(toolCallResponse.toolCallMessage!);
|
||||
state.memory.put(toolCallResponse.toolCallMessage!);
|
||||
toolCalls = toolCallResponse.toolCalls;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the tools
|
||||
const toolMsgs = await callTools({
|
||||
tools: [this.codeInterpreterTool],
|
||||
toolCalls,
|
||||
writeEvent: (text, step) => {
|
||||
ctx.sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Analyst",
|
||||
text,
|
||||
type: toolCalls.length > 1 ? "progress" : "text",
|
||||
current: step,
|
||||
total: toolCalls.length,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
for (const toolMsg of toolMsgs) {
|
||||
this.memory.put(toolMsg);
|
||||
// Call the code interpreter tools if needed
|
||||
if (toolCalls.length > 0) {
|
||||
const toolMsgs = await callTools({
|
||||
tools: [codeInterpreterTool],
|
||||
toolCalls,
|
||||
writeEvent: (text, step) => {
|
||||
sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Analyst",
|
||||
text,
|
||||
type: toolCalls.length > 1 ? "progress" : "text",
|
||||
current: step,
|
||||
total: toolCalls.length,
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
for (const toolMsg of toolMsgs) {
|
||||
state.memory.put(toolMsg);
|
||||
}
|
||||
}
|
||||
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
return new InputEvent({ input: chatHistory });
|
||||
};
|
||||
const finalChatHistory = await state.memory.getMessages();
|
||||
// After analysis (or tool calls for analysis), trigger the next LLM input cycle
|
||||
return inputEvent.with({ input: finalChatHistory });
|
||||
});
|
||||
|
||||
handleReportGeneration = async (
|
||||
ctx: HandlerContext<AgentWorkflowContext>,
|
||||
ev: ReportGenerationEvent,
|
||||
): Promise<InputEvent> => {
|
||||
const { toolCalls } = ev.data;
|
||||
workflow.handle([reportGenerationEvent], async ({ data }) => {
|
||||
const { sendEvent, state } = getContext();
|
||||
const { toolCalls } = data;
|
||||
|
||||
const toolMsgs = await callTools({
|
||||
tools: [this.documentGeneratorTool],
|
||||
tools: [documentGeneratorTool],
|
||||
toolCalls,
|
||||
writeEvent: (text, step) => {
|
||||
ctx.sendEvent(
|
||||
sendEvent(
|
||||
toAgentRunEvent({
|
||||
agent: "Reporter",
|
||||
text,
|
||||
@@ -388,9 +307,12 @@ class FinancialReportWorkflow extends Workflow<
|
||||
},
|
||||
});
|
||||
for (const toolMsg of toolMsgs) {
|
||||
this.memory.put(toolMsg);
|
||||
state.memory.put(toolMsg);
|
||||
}
|
||||
const chatHistory = await this.memory.getMessages();
|
||||
return new InputEvent({ input: chatHistory });
|
||||
};
|
||||
const chatHistory = await state.memory.getMessages();
|
||||
// After report generation, trigger the next LLM input cycle
|
||||
return inputEvent.with({ input: chatHistory });
|
||||
});
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@llamaindex/openai": "0.2.0",
|
||||
"@llamaindex/readers": "^2.0.0",
|
||||
"@llamaindex/server": "0.1.5",
|
||||
"@llamaindex/server": "0.2.0",
|
||||
"@llamaindex/workflow": "1.1.1",
|
||||
"@llamaindex/tools": "0.0.4",
|
||||
"dotenv": "^16.4.7",
|
||||
"zod": "^3.23.8",
|
||||
"llamaindex": "0.10.2"
|
||||
"llamaindex": "0.10.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.3",
|
||||
|
||||
@@ -9,6 +9,7 @@ readme = "README.md"
|
||||
requires-python = ">=3.11,<3.14"
|
||||
dependencies = [
|
||||
"llama-index>=0.12.1",
|
||||
"llama-parse>=0.6.21,<0.7.0",
|
||||
"fastapi[standard]>=0.109.1",
|
||||
"uvicorn>=0.23.2",
|
||||
"python-dotenv>=1.0.0",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/babel__standalone": "^7.1.9",
|
||||
"@types/babel__traverse": "^7.20.7",
|
||||
"llamaindex": "0.10.5",
|
||||
"llamaindex": "0.10.2",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"tailwindcss": "^4",
|
||||
@@ -104,8 +104,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@llamaindex/env": "^0.1.29",
|
||||
"@llamaindex/workflow": "1.1.1",
|
||||
"llamaindex": "0.10.5",
|
||||
"@llamaindex/workflow": "^1.1.0",
|
||||
"llamaindex": "^0.10.2",
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.23.3"
|
||||
},
|
||||
|
||||
Generated
+8
-162
@@ -187,8 +187,8 @@ importers:
|
||||
specifier: ^0.1.29
|
||||
version: 0.1.29
|
||||
'@llamaindex/workflow':
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1(@llamaindex/core@0.6.4)(@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)
|
||||
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)
|
||||
'@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.5
|
||||
version: 0.10.5(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.24.3)
|
||||
specifier: 0.10.2
|
||||
version: 0.10.2(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))(p-retry@6.2.1)(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))(zod@3.24.3)
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.5.0
|
||||
@@ -1188,21 +1188,12 @@ packages:
|
||||
'@llamaindex/core': 0.6.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
|
||||
'@llamaindex/cloud@4.0.6':
|
||||
resolution: {integrity: sha512-rotXPn7MjSysDqTZn3MN9yNgvmiTuT6lgZYQt1LnMECUKbx4PYZbDRzINl+8DmyG/prPqTqN2rcDbF4//OMSiQ==}
|
||||
peerDependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@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.4':
|
||||
resolution: {integrity: sha512-M6Lwnp3CwqlPyJnn2xPPz8cGg2AFUt44BycOsY7Y/bNXm/tokTSFUhGSfL/etaubWOURvOeUYUm2Fs1lGI6YWw==}
|
||||
|
||||
'@llamaindex/env@0.1.29':
|
||||
resolution: {integrity: sha512-GoITt+QLDNIhu2i1sGsPH8tHH13Gxp4i4ofMFlefrHagytvF761MG7nvTAQVIqP9kxBYk4dnSQ3lQnVPFAfMZQ==}
|
||||
peerDependencies:
|
||||
@@ -1214,17 +1205,6 @@ 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:
|
||||
@@ -1233,23 +1213,12 @@ packages:
|
||||
tree-sitter: ^0.22.0
|
||||
web-tree-sitter: ^0.24.3
|
||||
|
||||
'@llamaindex/node-parser@2.0.4':
|
||||
resolution: {integrity: sha512-fwWE6c4T/ZcZOmXjEI0bHNZ+sEZjv5C3QqYdNsTNy8MglEitIP6hrMVe6L36sZqfhFAW4BJMBc4iwg7KHFFEBg==}
|
||||
peerDependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@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==}
|
||||
|
||||
'@llamaindex/openai@0.3.4':
|
||||
resolution: {integrity: sha512-LelkRYljVfasllJY1fNNOqud141ggqlEhHci+3O5kY92MLcSUMlJ5477kkpRdxA80su6sKfocu1dcwSARijWpA==}
|
||||
|
||||
'@llamaindex/openai@0.3.6':
|
||||
resolution: {integrity: sha512-Od7OdYswIDV8BPPS/L3QkzxSnaWuUp6rX0BB3ypdMD7aHn6gy7cJHrB0otw/bDKDLnHknagouS6psOZxs5QfeQ==}
|
||||
|
||||
'@llamaindex/pdf-viewer@1.3.0':
|
||||
resolution: {integrity: sha512-HJtjzmxn+erb3Vq89W5atPq0q6uyZMMCgzOnmstxudzaHW/Yj1dp1ojCuBh/wlP1tUnIRoe9RmvC0ahmqSwRUA==}
|
||||
peerDependencies:
|
||||
@@ -1283,13 +1252,6 @@ packages:
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: ^3.23.8
|
||||
|
||||
'@llamaindex/workflow@1.1.1':
|
||||
resolution: {integrity: sha512-4V6nvosIeRYIy8GjAsOsOxnkyBhjl9TOm+fPJQoaWKG+ctNwdwH/C8YrpMyftC2zYrQWjCynyyGDnJUrQkrRFA==}
|
||||
peerDependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@llamaindex/env': 0.1.30
|
||||
zod: ^3.23.8
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
|
||||
|
||||
@@ -2519,9 +2481,6 @@ 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==}
|
||||
|
||||
@@ -4158,10 +4117,6 @@ 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'}
|
||||
@@ -4432,10 +4387,6 @@ packages:
|
||||
resolution: {integrity: sha512-tUbb+HYaQ+WP1f4NrYnEOIgPPRUePh3sPnOf/wbxTYAyZTnREpSiwCfLUGgYOiIYKK5PS7Oo/eTZCP7jpi7gSA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
llamaindex@0.10.5:
|
||||
resolution: {integrity: sha512-ZxaP5B+n9hab6ELnG8fglvocfjDykC+VredoKexXN+KGB12Byw03vVJkQ3kwFsqN2oLMoO/vWh9WMYrkbg0WLg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
locate-path@5.0.0:
|
||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5011,10 +4962,6 @@ 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'}
|
||||
@@ -5556,10 +5503,6 @@ 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'}
|
||||
@@ -7165,10 +7108,9 @@ 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))(p-retry@6.2.1)(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))(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)':
|
||||
@@ -7231,12 +7173,6 @@ snapshots:
|
||||
'@llamaindex/core': 0.6.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
|
||||
'@llamaindex/cloud@4.0.6(@llamaindex/core@0.6.4)(@llamaindex/env@0.1.30)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@llamaindex/env': 0.1.30
|
||||
p-retry: 6.2.1
|
||||
|
||||
'@llamaindex/core@0.6.1':
|
||||
dependencies:
|
||||
'@llamaindex/env': 0.1.29
|
||||
@@ -7259,29 +7195,12 @@ snapshots:
|
||||
- '@huggingface/transformers'
|
||||
- gpt-tokenizer
|
||||
|
||||
'@llamaindex/core@0.6.4':
|
||||
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
|
||||
@@ -7290,14 +7209,6 @@ snapshots:
|
||||
tree-sitter: 0.22.4
|
||||
web-tree-sitter: 0.24.7
|
||||
|
||||
'@llamaindex/node-parser@2.0.4(@llamaindex/core@0.6.4)(@llamaindex/env@0.1.30)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@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
|
||||
@@ -7322,18 +7233,6 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@llamaindex/openai@0.3.6(zod@3.24.3)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@llamaindex/env': 0.1.30
|
||||
openai: 4.96.0(zod@3.24.3)
|
||||
transitivePeerDependencies:
|
||||
- '@huggingface/transformers'
|
||||
- encoding
|
||||
- gpt-tokenizer
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@llamaindex/pdf-viewer@1.3.0(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@wojtekmaj/react-hooks': 1.17.2(react@19.1.0)
|
||||
@@ -7387,15 +7286,9 @@ snapshots:
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: 3.24.3
|
||||
|
||||
'@llamaindex/workflow@1.0.3(@llamaindex/core@0.6.4)(@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))(zod@3.24.3)':
|
||||
dependencies:
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@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)
|
||||
'@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.2
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: 3.24.3
|
||||
@@ -7406,19 +7299,6 @@ snapshots:
|
||||
- p-retry
|
||||
- rxjs
|
||||
|
||||
'@llamaindex/workflow@1.1.1(@llamaindex/core@0.6.4)(@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.4
|
||||
'@llamaindex/env': 0.1.29
|
||||
zod: 3.24.3
|
||||
transitivePeerDependencies:
|
||||
- '@modelcontextprotocol/sdk'
|
||||
- hono
|
||||
- next
|
||||
- p-retry
|
||||
- rxjs
|
||||
|
||||
'@manypkg/find-root@1.1.0':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.0
|
||||
@@ -8615,8 +8495,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 20.17.30
|
||||
|
||||
'@types/retry@0.12.2': {}
|
||||
|
||||
'@types/retry@0.12.5': {}
|
||||
|
||||
'@types/tar@6.1.5':
|
||||
@@ -10550,8 +10428,6 @@ snapshots:
|
||||
|
||||
is-module@1.0.0: {}
|
||||
|
||||
is-network-error@1.1.0: {}
|
||||
|
||||
is-number-object@1.1.1:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@@ -10830,28 +10706,6 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
llamaindex@0.10.5(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(zod@3.24.3):
|
||||
dependencies:
|
||||
'@llamaindex/cloud': 4.0.6(@llamaindex/core@0.6.4)(@llamaindex/env@0.1.30)
|
||||
'@llamaindex/core': 0.6.4
|
||||
'@llamaindex/env': 0.1.30
|
||||
'@llamaindex/node-parser': 2.0.4(@llamaindex/core@0.6.4)(@llamaindex/env@0.1.30)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)
|
||||
'@llamaindex/openai': 0.3.6(zod@3.24.3)
|
||||
'@llamaindex/workflow': 1.0.3(@llamaindex/core@0.6.4)(@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'
|
||||
- encoding
|
||||
- gpt-tokenizer
|
||||
- tree-sitter
|
||||
- web-tree-sitter
|
||||
- ws
|
||||
- zod
|
||||
|
||||
locate-path@5.0.0:
|
||||
dependencies:
|
||||
p-locate: 4.1.0
|
||||
@@ -11576,12 +11430,6 @@ 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
|
||||
@@ -12144,8 +11992,6 @@ snapshots:
|
||||
|
||||
retry@0.12.0: {}
|
||||
|
||||
retry@0.13.1: {}
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
Reference in New Issue
Block a user