mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-16 07:14:29 -04:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fb093ac578 | |||
| 4a8c29746d | |||
| 15bbddf451 | |||
| 9a788f35ee | |||
| cc3fe92a22 | |||
| 63ab0dba4e | |||
| 2225ffd1d4 | |||
| bc5334249b | |||
| 41953a3ef9 |
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@llamaindex/workflow": patch
|
||||
---
|
||||
|
||||
Bump llama-flow@0.4.1
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@llamaindex/cloud": patch
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
feat: bump llama cloud sdk
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@llamaindex/workflow": minor
|
||||
---
|
||||
|
||||
Update workflows to llama-flow syntax
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@llamaindex/resolution-tests": patch
|
||||
"llamaindex": patch
|
||||
---
|
||||
|
||||
fix: node10 module resolution fail in sub llamaindex packages
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [18.x, 20.x, 22.x, 23.x]
|
||||
node-version: [20.x, 22.x, 23.x]
|
||||
name: E2E on Node.js ${{ matrix.node-version }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [18.x, 20.x, 22.x, 23.x]
|
||||
node-version: [20.x, 22.x, 23.x]
|
||||
name: Test on Node.js ${{ matrix.node-version }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"dependencies": {
|
||||
"@huggingface/transformers": "^3.5.0",
|
||||
"@icons-pack/react-simple-icons": "^10.1.0",
|
||||
"@llama-flow/docs": "0.0.5",
|
||||
"@llama-flow/docs": "0.0.8",
|
||||
"@llamaindex/chat-ui": "0.2.0",
|
||||
"@llamaindex/cloud": "workspace:*",
|
||||
"@llamaindex/core": "workspace:*",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { tool } from "@llamaindex/core/tools";
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import fs from "fs";
|
||||
import {
|
||||
agent,
|
||||
AgentToolCall,
|
||||
AgentToolCallResult,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
multiAgent,
|
||||
tool,
|
||||
} from "llamaindex";
|
||||
} from "@llamaindex/workflow";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -56,19 +56,19 @@ async function main() {
|
||||
rootAgent: researchAgent,
|
||||
});
|
||||
|
||||
const context = workflow.run("Write a blog post about history of LLM");
|
||||
const events = workflow.runStream("Write a blog post about history of LLM");
|
||||
|
||||
let finalResult;
|
||||
for await (const event of context) {
|
||||
if (event instanceof AgentToolCall) {
|
||||
for await (const event of events) {
|
||||
if (agentToolCallEvent.include(event)) {
|
||||
console.log(
|
||||
`[Agent ${event.displayName}] executing tool ${event.data.toolName} with parameters ${JSON.stringify(
|
||||
`[Agent ${event.data.agentName}] executing tool ${event.data.toolName} with parameters ${JSON.stringify(
|
||||
event.data.toolKwargs,
|
||||
)}`,
|
||||
);
|
||||
} else if (event instanceof AgentToolCallResult) {
|
||||
} else if (agentToolCallResultEvent.include(event)) {
|
||||
console.log(
|
||||
`[Agent ${event.displayName}] executed tool ${event.data.toolName} with result ${event.data.toolOutput.result}`,
|
||||
`[Tool ${event.data.toolName}] executed with result ${event.data.toolOutput.result}`,
|
||||
);
|
||||
}
|
||||
finalResult = event;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { FunctionTool, agent } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const csvData =
|
||||
@@ -11,24 +12,22 @@ const userQuestion = "which are the best comedies after 2010?";
|
||||
// The agent will succeed if we increase `maxTokens` to 1024
|
||||
const llm = new OpenAI({ model: "gpt-4-turbo", maxTokens: 1024 });
|
||||
|
||||
const interpreterTool = FunctionTool.from(
|
||||
({ code }) => {
|
||||
const interpreterTool = tool({
|
||||
name: "interpreter",
|
||||
description:
|
||||
"Execute python code in a Jupyter notebook cell and return any result, stdout, stderr, display_data, and error.",
|
||||
parameters: z.object({
|
||||
code: z.string({
|
||||
description: "The python code to execute in a single cell.",
|
||||
}),
|
||||
}),
|
||||
execute: ({ code }) => {
|
||||
console.log(
|
||||
`To answer the user's question, call the following code:\n${code}`,
|
||||
);
|
||||
return code;
|
||||
},
|
||||
{
|
||||
name: "interpreter",
|
||||
description:
|
||||
"Execute python code in a Jupyter notebook cell and return any result, stdout, stderr, display_data, and error.",
|
||||
parameters: z.object({
|
||||
code: z.string({
|
||||
description: "The python code to execute in a single cell.",
|
||||
}),
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const systemPrompt =
|
||||
"You are a Python interpreter.\n - You are given tasks to complete and you run python code to solve them.\n - The python code runs in a Jupyter notebook. Every time you call $(interpreter) tool, the python code is executed in a separate cell. It's okay to make multiple calls to $(interpreter).\n - Display visualizations using matplotlib or any other visualization library directly in the notebook. Shouldn't save the visualizations to a file, just return the base64 encoded data.\n - You can install any pip package (if it exists) if you need to but the usual packages for data analysis are already preinstalled.\n - You can run any python code you want in a secure environment.";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import { mcp } from "@llamaindex/tools";
|
||||
import { agent } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
|
||||
async function main() {
|
||||
// Create an MCP server for filesystem tools
|
||||
+17
-17
@@ -6,15 +6,15 @@
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import {
|
||||
agent,
|
||||
AgentInput,
|
||||
AgentOutput,
|
||||
AgentStream,
|
||||
AgentToolCall,
|
||||
AgentToolCallResult,
|
||||
agentInputEvent,
|
||||
agentOutputEvent,
|
||||
agentStreamEvent,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
multiAgent,
|
||||
StopEvent,
|
||||
tool,
|
||||
} from "llamaindex";
|
||||
stopAgentEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const llm = openai({
|
||||
@@ -79,21 +79,21 @@ async function multiWeatherAgent() {
|
||||
});
|
||||
|
||||
// Ask the agent to get the weather in a city
|
||||
const context = workflow.run(
|
||||
const events = workflow.runStream(
|
||||
"What is the weather in San Francisco in Celsius?",
|
||||
);
|
||||
// Stream the events
|
||||
for await (const event of context) {
|
||||
// These events might be useful for UI
|
||||
for await (const event of events) {
|
||||
// These events are useful for reporting the current state to the user in the UI
|
||||
if (
|
||||
event instanceof AgentToolCall ||
|
||||
event instanceof AgentToolCallResult ||
|
||||
event instanceof AgentOutput ||
|
||||
event instanceof AgentInput ||
|
||||
event instanceof StopEvent
|
||||
agentToolCallEvent.include(event) ||
|
||||
agentToolCallResultEvent.include(event) ||
|
||||
agentOutputEvent.include(event) ||
|
||||
agentInputEvent.include(event) ||
|
||||
stopAgentEvent.include(event)
|
||||
) {
|
||||
console.log(event);
|
||||
} else if (event instanceof AgentStream) {
|
||||
} else if (agentStreamEvent.include(event)) {
|
||||
for (const chunk of event.data.delta) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import { agent, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const sumNumbers = tool({
|
||||
@@ -1,11 +1,9 @@
|
||||
import {
|
||||
AgentStream,
|
||||
AgentToolCallResult,
|
||||
Document,
|
||||
VectorStoreIndex,
|
||||
agent,
|
||||
openai,
|
||||
} from "llamaindex";
|
||||
agentStreamEvent,
|
||||
agentToolCallResultEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import { Document, VectorStoreIndex, openai } from "llamaindex";
|
||||
|
||||
async function main() {
|
||||
const index = await VectorStoreIndex.fromDocuments([
|
||||
@@ -30,15 +28,15 @@ async function main() {
|
||||
],
|
||||
});
|
||||
|
||||
const context = myAgent.run("The fact about cats");
|
||||
const events = myAgent.runStream("The fact about cats");
|
||||
|
||||
for await (const event of context) {
|
||||
if (event instanceof AgentToolCallResult) {
|
||||
for await (const event of events) {
|
||||
if (agentToolCallResultEvent.include(event)) {
|
||||
console.log(
|
||||
"Using these retrieved information to answer the question:\n",
|
||||
event.data.toolOutput.result,
|
||||
);
|
||||
} else if (event instanceof AgentStream) {
|
||||
} else if (agentStreamEvent.include(event)) {
|
||||
for (const chunk of event.data.delta) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
* This example shows how to use a single agent with a tool
|
||||
*/
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import { agent } from "llamaindex";
|
||||
import { getWeatherTool } from "../agent/utils/tools";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { getWeatherTool } from "../deprecated/utils/tools";
|
||||
|
||||
async function main() {
|
||||
const weatherAgent = agent({
|
||||
@@ -14,14 +14,14 @@ async function main() {
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
// Run the agent and keep the context
|
||||
const context = weatherAgent.run("What's the weather like in San Francisco?");
|
||||
const result = await context;
|
||||
const result = await weatherAgent.run(
|
||||
"What's the weather like in San Francisco?",
|
||||
);
|
||||
console.log(`${JSON.stringify(result, null, 2)}`);
|
||||
|
||||
// Reuse the context from the previous run
|
||||
// Reuse the state from the previous run
|
||||
const caResult = await weatherAgent.run("Compare it with California?", {
|
||||
context: context.data,
|
||||
state: result.data.state,
|
||||
});
|
||||
console.log(`${JSON.stringify(caResult, null, 2)}`);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { wiki } from "@llamaindex/tools";
|
||||
import { AgentStream, agent } from "llamaindex";
|
||||
import { agent, agentStreamEvent } from "@llamaindex/workflow";
|
||||
|
||||
async function main() {
|
||||
const llm = new OpenAI({ model: "gpt-4-turbo" });
|
||||
@@ -12,10 +12,10 @@ async function main() {
|
||||
});
|
||||
|
||||
// Chat with the agent
|
||||
const context = workflow.run("Who was Goethe?");
|
||||
const events = workflow.runStream("Who was Goethe?");
|
||||
|
||||
for await (const event of context) {
|
||||
if (event instanceof AgentStream) {
|
||||
for await (const event of events) {
|
||||
if (agentStreamEvent.include(event)) {
|
||||
for (const chunk of event.data.delta) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
+11
-11
@@ -1,11 +1,11 @@
|
||||
import fs from "fs";
|
||||
import {
|
||||
agent,
|
||||
AgentToolCall,
|
||||
AgentToolCallResult,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
multiAgent,
|
||||
tool,
|
||||
} from "llamaindex";
|
||||
} from "@llamaindex/workflow";
|
||||
import fs from "fs";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
import { anthropic } from "@llamaindex/anthropic";
|
||||
@@ -81,21 +81,21 @@ async function main() {
|
||||
rootAgent: researchAgent,
|
||||
});
|
||||
|
||||
const context = workflow.run(
|
||||
const events = workflow.runStream(
|
||||
"Write a report about New York weather and inflation",
|
||||
);
|
||||
|
||||
let finalResult;
|
||||
for await (const event of context) {
|
||||
if (event instanceof AgentToolCall) {
|
||||
for await (const event of events) {
|
||||
if (agentToolCallEvent.include(event)) {
|
||||
console.log(
|
||||
`[Agent ${event.displayName}] executing tool ${event.data.toolName} with parameters ${JSON.stringify(
|
||||
`[Agent ${event.data.agentName}] executing tool ${event.data.toolName} with parameters ${JSON.stringify(
|
||||
event.data.toolKwargs,
|
||||
)}`,
|
||||
);
|
||||
} else if (event instanceof AgentToolCallResult) {
|
||||
} else if (agentToolCallResultEvent.include(event)) {
|
||||
console.log(
|
||||
`[Agent ${event.displayName}] executed tool ${event.data.toolName} with result ${event.data.toolOutput.result}`,
|
||||
`[Agent executed tool ${event.data.toolName} with result ${event.data.toolOutput.result}`,
|
||||
);
|
||||
}
|
||||
finalResult = event;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ollama } from "@llamaindex/ollama";
|
||||
import { agent } from "llamaindex";
|
||||
import { getWeatherTool } from "../agent/utils/tools";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { getWeatherTool } from "../deprecated/utils/tools";
|
||||
|
||||
async function main() {
|
||||
const myAgent = agent({
|
||||
@@ -0,0 +1,94 @@
|
||||
import { openai } from "@llamaindex/openai";
|
||||
import {
|
||||
createStatefulMiddleware,
|
||||
createWorkflow,
|
||||
workflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
|
||||
// Create LLM instance
|
||||
const llm = openai({ model: "gpt-4.1-mini" });
|
||||
|
||||
// Define our workflow events
|
||||
const startEvent = workflowEvent<string>(); // Input topic for joke
|
||||
const jokeEvent = workflowEvent<{ joke: string }>(); // Intermediate joke
|
||||
const critiqueEvent = workflowEvent<{ joke: string; critique: string }>(); // Intermediate critique
|
||||
const resultEvent = workflowEvent<{ joke: string; critique: string }>(); // Final joke + critique
|
||||
|
||||
// Create our workflow
|
||||
const { withState, getContext } = createStatefulMiddleware(() => ({
|
||||
numIterations: 0,
|
||||
maxIterations: 3,
|
||||
}));
|
||||
const jokeFlow = withState(createWorkflow());
|
||||
|
||||
// Define handlers for each step
|
||||
jokeFlow.handle([startEvent], async (event) => {
|
||||
// Prompt the LLM to write a joke
|
||||
const prompt = `Write your best joke about ${event.data}. Write the joke between <joke> and </joke> tags.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke =
|
||||
response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ??
|
||||
response.text;
|
||||
return jokeEvent.with({ joke: joke });
|
||||
});
|
||||
|
||||
jokeFlow.handle([jokeEvent], async (event) => {
|
||||
// Prompt the LLM to critique the joke
|
||||
const prompt = `Give a thorough critique of the following joke. If the joke needs improvement, put "IMPROVE" somewhere in the critique: ${event.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// If the critique includes "IMPROVE", keep iterating, else, return the result
|
||||
if (response.text.includes("IMPROVE")) {
|
||||
return critiqueEvent.with({
|
||||
joke: event.data.joke,
|
||||
critique: response.text,
|
||||
});
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: event.data.joke, critique: response.text });
|
||||
});
|
||||
|
||||
jokeFlow.handle([critiqueEvent], async (event) => {
|
||||
// Keep track of the number of iterations
|
||||
const state = getContext().state;
|
||||
state.numIterations++;
|
||||
|
||||
// Write a new joke based on the previous joke and critique
|
||||
const prompt = `Write a new joke based on the following critique and the original joke. Write the joke between <joke> and </joke> tags.\n\nJoke: ${event.data.joke}\n\nCritique: ${event.data.critique}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke =
|
||||
response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ??
|
||||
response.text;
|
||||
|
||||
// If we've done less than the max number of iterations, keep iterating
|
||||
// else, return the result
|
||||
if (state.numIterations < state.maxIterations) {
|
||||
return jokeEvent.with({ joke: joke });
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: joke, critique: event.data.critique });
|
||||
});
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const { stream, sendEvent } = jokeFlow.createContext();
|
||||
sendEvent(startEvent.with("pirates"));
|
||||
|
||||
let result: { joke: string; critique: string } | undefined;
|
||||
|
||||
for await (const event of stream) {
|
||||
// console.log(event.data); optionally log the event data
|
||||
if (resultEvent.include(event)) {
|
||||
result = event.data;
|
||||
break; // Stop when we get the final result
|
||||
}
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,6 +1,7 @@
|
||||
import { anthropic } from "@llamaindex/anthropic";
|
||||
import { wiki } from "@llamaindex/tools";
|
||||
import { agent, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const workflow = agent({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { gemini, GEMINI_MODEL } from "@llamaindex/google";
|
||||
import { agent, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const sumNumbers = tool({
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Run LlamaIndex Server with simple steps
|
||||
|
||||
1. Setup environment variables
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=<your-openai-api-key>
|
||||
```
|
||||
|
||||
2. Run the server
|
||||
|
||||
```bash
|
||||
npx tsx llamaindex-server/simple-workflow/index.ts
|
||||
```
|
||||
|
||||
3. Open the app at `http://localhost:4000` and start chatting with the agent
|
||||
|
||||

|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 118 KiB |
@@ -1,19 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { LlamaIndexServer } from "@llamaindex/server";
|
||||
import { weather } from "@llamaindex/tools";
|
||||
import "dotenv/config";
|
||||
import { agent } from "llamaindex";
|
||||
|
||||
const weatherAgent = agent({
|
||||
tools: [weather()],
|
||||
llm: new OpenAI({ model: "gpt-4o-mini" }),
|
||||
});
|
||||
|
||||
new LlamaIndexServer({
|
||||
workflow: () => weatherAgent,
|
||||
uiConfig: {
|
||||
appTitle: "Weather Agent",
|
||||
starterQuestions: ["Ho Chi Minh city weather", "New York weather"],
|
||||
},
|
||||
port: 4000,
|
||||
}).start();
|
||||
@@ -1,6 +1,8 @@
|
||||
import { mistral } from "@llamaindex/mistral";
|
||||
import { wiki } from "@llamaindex/tools";
|
||||
import { agent, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
const workflow = agent({
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { StartEvent, StopEvent, Workflow } from "llamaindex";
|
||||
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
};
|
||||
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
},
|
||||
async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.data.counter++;
|
||||
return new StopEvent(`Hello, ${input}!`);
|
||||
},
|
||||
);
|
||||
|
||||
{
|
||||
const ret = await workflow.run("Alex", contextData);
|
||||
console.log(ret.data); // Hello, Alex!
|
||||
}
|
||||
|
||||
{
|
||||
const ret = await workflow.run("World", contextData);
|
||||
console.log(ret.data); // Hello, World!
|
||||
}
|
||||
|
||||
console.log(contextData.counter); // 2
|
||||
@@ -1,6 +1,7 @@
|
||||
import { openaiResponses } from "@llamaindex/openai";
|
||||
import { wiki } from "@llamaindex/tools";
|
||||
import { agent, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { tool } from "llamaindex";
|
||||
import { z } from "zod";
|
||||
|
||||
const workflow = agent({
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
"@llamaindex/together": "^0.0.13",
|
||||
"@llamaindex/jinaai": "^0.0.13",
|
||||
"@llamaindex/perplexity": "^0.0.10",
|
||||
"@llamaindex/server": "^0.1.6",
|
||||
"@llamaindex/supabase": "^0.1.2",
|
||||
"@llamaindex/tools": "^0.0.8",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
> LlamaCloud is a new generation of managed parsing, ingestion, and retrieval services, designed to bring production-grade context-augmentation to your LLM and RAG applications.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { OpenAPI } from "@llamaindex/cloud/api";
|
||||
OpenAPI.TOKEN = "YOUR_API_KEY";
|
||||
OpenAPI.BASE = "https://api.cloud.llamaindex.ai/";
|
||||
// ...
|
||||
```
|
||||
|
||||
For more information, see the [API documentation](https://docs.cloud.llamaindex.ai/).
|
||||
|
||||
## License
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import { defineConfig } from "@hey-api/openapi-ts";
|
||||
import { defaultPlugins, defineConfig } from "@hey-api/openapi-ts";
|
||||
|
||||
export default defineConfig({
|
||||
// you can download this file to get the latest version of the OpenAPI document
|
||||
// @link https://api.cloud.llamaindex.ai/api/openapi.json
|
||||
input: "./openapi.json",
|
||||
client: "@hey-api/client-fetch",
|
||||
output: {
|
||||
path: "./src/client",
|
||||
format: "prettier",
|
||||
lint: "eslint",
|
||||
},
|
||||
plugins: [
|
||||
"@hey-api/schemas",
|
||||
"@hey-api/sdk",
|
||||
...defaultPlugins,
|
||||
"@hey-api/client-fetch",
|
||||
{
|
||||
enums: "javascript",
|
||||
identifierCase: "preserve",
|
||||
name: "@hey-api/typescript",
|
||||
name: "@hey-api/sdk",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
+2975
-4384
File diff suppressed because it is too large
Load Diff
@@ -55,8 +55,8 @@
|
||||
"directory": "packages/cloud"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hey-api/client-fetch": "^0.6.0",
|
||||
"@hey-api/openapi-ts": "^0.61.0",
|
||||
"@hey-api/client-fetch": "^0.10.0",
|
||||
"@hey-api/openapi-ts": "^0.66.7",
|
||||
"@llamaindex/core": "workspace:*",
|
||||
"@llamaindex/env": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
import { client } from "./client/client.gen";
|
||||
|
||||
client.setConfig({
|
||||
baseUrl: "https://api.cloud.llamaindex.ai/",
|
||||
headers: {
|
||||
"X-SDK-Name": "llamaindex-ts",
|
||||
},
|
||||
});
|
||||
|
||||
export * from "./client";
|
||||
export { client };
|
||||
|
||||
@@ -4,7 +4,8 @@ import { Document, FileReader } from "@llamaindex/core/schema";
|
||||
import { fs, getEnv, path } from "@llamaindex/env";
|
||||
import pRetry from "p-retry";
|
||||
import {
|
||||
type Body_upload_file_api_v1_parsing_upload_post,
|
||||
type BodyUploadFileApiParsingUploadPost,
|
||||
type FailPageMode,
|
||||
type ParserLanguages,
|
||||
type ParsingMode,
|
||||
getJobApiV1ParsingJobJobIdGet,
|
||||
@@ -162,6 +163,15 @@ export class LlamaParseReader extends FileReader {
|
||||
content_guideline_instruction?: string | undefined;
|
||||
adaptive_long_table?: boolean | undefined;
|
||||
model?: string | undefined;
|
||||
auto_mode_configuration_json?: string | undefined;
|
||||
compact_markdown_table?: boolean | undefined;
|
||||
markdown_table_multiline_header_separator?: string | undefined;
|
||||
page_error_tolerance?: number | undefined;
|
||||
replace_failed_page_mode?: FailPageMode | undefined;
|
||||
replace_failed_page_with_error_message_prefix?: string | undefined;
|
||||
replace_failed_page_with_error_message_suffix?: string | undefined;
|
||||
save_images?: boolean | undefined;
|
||||
preset?: string | undefined;
|
||||
|
||||
constructor(
|
||||
params: Partial<Omit<LlamaParseReader, "language" | "apiKey">> & {
|
||||
@@ -331,11 +341,23 @@ export class LlamaParseReader extends FileReader {
|
||||
content_guideline_instruction: this.content_guideline_instruction,
|
||||
adaptive_long_table: this.adaptive_long_table,
|
||||
model: this.model,
|
||||
auto_mode_configuration_json: this.auto_mode_configuration_json,
|
||||
compact_markdown_table: this.compact_markdown_table,
|
||||
markdown_table_multiline_header_separator:
|
||||
this.markdown_table_multiline_header_separator,
|
||||
page_error_tolerance: this.page_error_tolerance,
|
||||
replace_failed_page_mode: this.replace_failed_page_mode,
|
||||
replace_failed_page_with_error_message_prefix:
|
||||
this.replace_failed_page_with_error_message_prefix,
|
||||
replace_failed_page_with_error_message_suffix:
|
||||
this.replace_failed_page_with_error_message_suffix,
|
||||
save_images: this.save_images,
|
||||
preset: this.preset,
|
||||
} satisfies {
|
||||
[Key in keyof Body_upload_file_api_v1_parsing_upload_post]-?:
|
||||
| Body_upload_file_api_v1_parsing_upload_post[Key]
|
||||
[Key in keyof BodyUploadFileApiParsingUploadPost]-?:
|
||||
| BodyUploadFileApiParsingUploadPost[Key]
|
||||
| undefined;
|
||||
} as unknown as Body_upload_file_api_v1_parsing_upload_post;
|
||||
} as unknown as BodyUploadFileApiParsingUploadPost;
|
||||
|
||||
const response = await uploadFileApiV1ParsingUploadPost({
|
||||
client: this.#client,
|
||||
@@ -382,10 +404,6 @@ export class LlamaParseReader extends FileReader {
|
||||
client: this.#client,
|
||||
throwOnError: true,
|
||||
path: { job_id: jobId },
|
||||
query: {
|
||||
project_id: this.project_id ?? null,
|
||||
organization_id: this.organization_id ?? null,
|
||||
},
|
||||
signal: AbortSignal.timeout(this.maxTimeout * 1000),
|
||||
}),
|
||||
{
|
||||
@@ -431,7 +449,6 @@ export class LlamaParseReader extends FileReader {
|
||||
throwOnError: true,
|
||||
path: { job_id: jobId },
|
||||
query: {
|
||||
project_id: this.project_id ?? null,
|
||||
organization_id: this.organization_id ?? null,
|
||||
},
|
||||
signal: AbortSignal.timeout(this.maxTimeout * 1000),
|
||||
@@ -445,7 +462,6 @@ export class LlamaParseReader extends FileReader {
|
||||
throwOnError: true,
|
||||
path: { job_id: jobId },
|
||||
query: {
|
||||
project_id: this.project_id ?? null,
|
||||
organization_id: this.organization_id ?? null,
|
||||
},
|
||||
signal: AbortSignal.timeout(this.maxTimeout * 1000),
|
||||
@@ -459,7 +475,6 @@ export class LlamaParseReader extends FileReader {
|
||||
throwOnError: true,
|
||||
path: { job_id: jobId },
|
||||
query: {
|
||||
project_id: this.project_id ?? null,
|
||||
organization_id: this.organization_id ?? null,
|
||||
},
|
||||
signal: AbortSignal.timeout(this.maxTimeout * 1000),
|
||||
@@ -689,10 +704,6 @@ export class LlamaParseReader extends FileReader {
|
||||
job_id: jobId,
|
||||
name: imageName,
|
||||
},
|
||||
query: {
|
||||
project_id: this.project_id ?? null,
|
||||
organization_id: this.organization_id ?? null,
|
||||
},
|
||||
});
|
||||
if (response.error) {
|
||||
throw new Error(`Failed to download image: ${response.error.detail}`);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
addFilesToPipelineApiV1PipelinesPipelineIdFilesPut,
|
||||
addFilesToPipelineApiApiV1PipelinesPipelineIdFilesPut,
|
||||
getPipelineFileStatusApiV1PipelinesPipelineIdFilesFileIdStatusGet,
|
||||
listPipelineFilesApiV1PipelinesPipelineIdFilesGet,
|
||||
listProjectsApiV1ProjectsGet,
|
||||
@@ -56,7 +56,7 @@ export class LLamaCloudFileService {
|
||||
custom_metadata: { file_id: file.id, ...customMetadata },
|
||||
},
|
||||
];
|
||||
await addFilesToPipelineApiV1PipelinesPipelineIdFilesPut({
|
||||
await addFilesToPipelineApiApiV1PipelinesPipelineIdFilesPut({
|
||||
path: {
|
||||
pipeline_id: pipelineId,
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
deletePipelineDocumentApiV1PipelinesPipelineIdDocumentsDocumentIdDelete,
|
||||
getPipelineDocumentStatusApiV1PipelinesPipelineIdDocumentsDocumentIdStatusGet,
|
||||
getPipelineStatusApiV1PipelinesPipelineIdStatusGet,
|
||||
type PipelineCreate,
|
||||
type PipelineCreateReadable,
|
||||
searchPipelinesApiV1PipelinesGet,
|
||||
upsertBatchPipelineDocumentsApiV1PipelinesPipelineIdDocumentsPut,
|
||||
upsertPipelineApiV1PipelinesPut,
|
||||
@@ -182,8 +182,8 @@ export class LlamaCloudIndex {
|
||||
verbose?: boolean;
|
||||
} & CloudConstructorParams,
|
||||
config?: {
|
||||
embedding: PipelineCreate["embedding_config"];
|
||||
transform: PipelineCreate["transform_config"];
|
||||
embedding: PipelineCreateReadable["embedding_config"];
|
||||
transform: PipelineCreateReadable["transform_config"];
|
||||
},
|
||||
): Promise<LlamaCloudIndex> {
|
||||
const index = new LlamaCloudIndex({ ...params });
|
||||
@@ -348,8 +348,8 @@ export class LlamaCloudIndex {
|
||||
}
|
||||
|
||||
public async ensureIndex(config?: {
|
||||
embedding?: PipelineCreate["embedding_config"];
|
||||
transform?: PipelineCreate["transform_config"];
|
||||
embedding?: PipelineCreateReadable["embedding_config"];
|
||||
transform?: PipelineCreateReadable["transform_config"];
|
||||
verbose?: boolean;
|
||||
}): Promise<void> {
|
||||
const projectId = await this.getProjectId();
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": "./dist/index.js",
|
||||
"private": true
|
||||
}
|
||||
@@ -48,6 +48,6 @@
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@llama-flow/llamaindex": "^0.0.12"
|
||||
"@llama-flow/core": "^0.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
import {
|
||||
StartEvent,
|
||||
type StepContext,
|
||||
StopEvent,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "@llama-flow/llamaindex";
|
||||
import type { ChatMessage } from "@llamaindex/core/llms";
|
||||
createWorkflow,
|
||||
getContext,
|
||||
workflowEvent,
|
||||
type WorkflowEventData,
|
||||
} from "@llama-flow/core";
|
||||
import { createStatefulMiddleware } from "@llama-flow/core/middleware/state";
|
||||
import { collect } from "@llama-flow/core/stream/consumer";
|
||||
import { until } from "@llama-flow/core/stream/until";
|
||||
import { Settings } from "@llamaindex/core/global";
|
||||
import type { ChatMessage, MessageContent } from "@llamaindex/core/llms";
|
||||
import { ChatMemoryBuffer } from "@llamaindex/core/memory";
|
||||
import { PromptTemplate } from "@llamaindex/core/prompts";
|
||||
import { FunctionTool } from "@llamaindex/core/tools";
|
||||
import { stringifyJSONToMessageContent } from "@llamaindex/core/utils";
|
||||
import { z } from "zod";
|
||||
import type { AgentWorkflowContext, BaseWorkflowAgent } from "./base";
|
||||
import type { AgentWorkflowState, BaseWorkflowAgent } from "./base";
|
||||
import {
|
||||
AgentInput,
|
||||
AgentOutput,
|
||||
AgentSetup,
|
||||
AgentToolCall,
|
||||
AgentToolCallResult,
|
||||
agentInputEvent,
|
||||
agentOutputEvent,
|
||||
agentSetupEvent,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
type AgentInput,
|
||||
type AgentSetup,
|
||||
type AgentToolCall,
|
||||
type AgentToolCallResult,
|
||||
} from "./events";
|
||||
import { FunctionAgent, type FunctionAgentParams } from "./function-agent";
|
||||
|
||||
@@ -38,23 +45,42 @@ export type AgentInputData = {
|
||||
userInput?: string | undefined;
|
||||
chatHistory?: ChatMessage[] | undefined;
|
||||
};
|
||||
export const startAgentEvent = workflowEvent<
|
||||
AgentInputData,
|
||||
"llamaindex-start"
|
||||
>({
|
||||
debugLabel: "llamaindex-start",
|
||||
});
|
||||
|
||||
export type AgentResultData = {
|
||||
result: MessageContent;
|
||||
state?: AgentWorkflowState | undefined;
|
||||
};
|
||||
export const stopAgentEvent = workflowEvent<AgentResultData, "llamaindex-stop">(
|
||||
{
|
||||
debugLabel: "llamaindex-stop",
|
||||
},
|
||||
);
|
||||
|
||||
// Wrapper events for multiple tool calls and results
|
||||
export class ToolCallsEvent extends WorkflowEvent<{
|
||||
export type ToolCalls = {
|
||||
agentName: string;
|
||||
toolCalls: AgentToolCall[];
|
||||
}> {}
|
||||
};
|
||||
export const toolCallsEvent = workflowEvent<ToolCalls>();
|
||||
|
||||
export class ToolResultsEvent extends WorkflowEvent<{
|
||||
export type ToolResults = {
|
||||
agentName: string;
|
||||
results: AgentToolCallResult[];
|
||||
}> {}
|
||||
};
|
||||
export const toolResultsEvent = workflowEvent<ToolResults>();
|
||||
|
||||
export class AgentStepEvent extends WorkflowEvent<{
|
||||
export type AgentStep = {
|
||||
agentName: string;
|
||||
response: ChatMessage;
|
||||
toolCalls: AgentToolCall[];
|
||||
}> {}
|
||||
};
|
||||
export const agentStepEvent = workflowEvent<AgentStep>();
|
||||
|
||||
export type SingleAgentParams = FunctionAgentParams & {
|
||||
/**
|
||||
@@ -113,13 +139,15 @@ export const agent = (params: SingleAgentParams): AgentWorkflow => {
|
||||
* with multiple tools.
|
||||
*/
|
||||
export class AgentWorkflow {
|
||||
private workflow: Workflow<AgentWorkflowContext, AgentInputData, string>;
|
||||
private stateful = createStatefulMiddleware(
|
||||
(state: AgentWorkflowState) => state,
|
||||
);
|
||||
private workflow = this.stateful.withState(createWorkflow());
|
||||
private agents: Map<string, BaseWorkflowAgent> = new Map();
|
||||
private verbose: boolean;
|
||||
private rootAgentName: string;
|
||||
|
||||
constructor({ agents, rootAgent, verbose, timeout }: AgentWorkflowParams) {
|
||||
this.workflow = new Workflow();
|
||||
constructor({ agents, rootAgent, verbose }: AgentWorkflowParams) {
|
||||
this.verbose = verbose ?? false;
|
||||
|
||||
// Handle AgentWorkflow cases for agents
|
||||
@@ -162,6 +190,7 @@ export class AgentWorkflow {
|
||||
}
|
||||
|
||||
this.addAgents(processedAgents);
|
||||
this.setupWorkflowSteps();
|
||||
}
|
||||
|
||||
private addAgents(agents: BaseWorkflowAgent[]): void {
|
||||
@@ -255,13 +284,13 @@ export class AgentWorkflow {
|
||||
}
|
||||
|
||||
private handleInputStep = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: StartEvent<AgentInputData>,
|
||||
): Promise<AgentInput> => {
|
||||
event: WorkflowEventData<AgentInputData>,
|
||||
) => {
|
||||
const { state } = this.stateful.getContext();
|
||||
const { userInput, chatHistory } = event.data;
|
||||
const memory = ctx.data.memory;
|
||||
const memory = state.memory;
|
||||
if (chatHistory) {
|
||||
chatHistory.forEach((message) => {
|
||||
chatHistory.forEach((message: ChatMessage) => {
|
||||
memory.put(message);
|
||||
});
|
||||
}
|
||||
@@ -279,21 +308,18 @@ export class AgentWorkflow {
|
||||
"Either provide a user message or a chat history with a user message as the last message",
|
||||
);
|
||||
}
|
||||
ctx.data.userInput = lastMessage.content as string;
|
||||
state.userInput = lastMessage.content as string;
|
||||
} else {
|
||||
throw new Error("No user message or chat history provided");
|
||||
}
|
||||
|
||||
return new AgentInput({
|
||||
return agentInputEvent.with({
|
||||
input: await memory.getMessages(),
|
||||
currentAgentName: this.rootAgentName,
|
||||
});
|
||||
};
|
||||
|
||||
private setupAgent = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: AgentInput,
|
||||
): Promise<AgentSetup> => {
|
||||
private setupAgent = async (event: WorkflowEventData<AgentInput>) => {
|
||||
const currentAgentName = event.data.currentAgentName;
|
||||
const agent = this.agents.get(currentAgentName);
|
||||
if (!agent) {
|
||||
@@ -308,16 +334,14 @@ export class AgentWorkflow {
|
||||
});
|
||||
}
|
||||
|
||||
return new AgentSetup({
|
||||
return agentSetupEvent.with({
|
||||
input: llmInput,
|
||||
currentAgentName: currentAgentName,
|
||||
});
|
||||
};
|
||||
|
||||
private runAgentStep = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: AgentSetup,
|
||||
) => {
|
||||
private runAgentStep = async (event: WorkflowEventData<AgentSetup>) => {
|
||||
const { sendEvent } = this.stateful.getContext();
|
||||
const agent = this.agents.get(event.data.currentAgentName);
|
||||
if (!agent) {
|
||||
throw new Error("No valid agent found");
|
||||
@@ -329,24 +353,32 @@ export class AgentWorkflow {
|
||||
);
|
||||
}
|
||||
|
||||
const output = await agent.takeStep(ctx, event.data.input, agent.tools);
|
||||
const output = await agent.takeStep(
|
||||
this.stateful.getContext(),
|
||||
this.stateful.getContext().state,
|
||||
event.data.input,
|
||||
agent.tools,
|
||||
);
|
||||
|
||||
ctx.sendEvent(
|
||||
new AgentStepEvent({
|
||||
sendEvent(
|
||||
agentStepEvent.with({
|
||||
agentName: agent.name,
|
||||
response: output.data.response,
|
||||
toolCalls: output.data.toolCalls,
|
||||
response: output.response,
|
||||
toolCalls: output.toolCalls,
|
||||
}),
|
||||
);
|
||||
|
||||
ctx.sendEvent(output);
|
||||
sendEvent(agentOutputEvent.with(output));
|
||||
};
|
||||
|
||||
private parseAgentOutput = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: AgentStepEvent,
|
||||
): Promise<ToolCallsEvent | StopEvent<{ result: string }>> => {
|
||||
private parseAgentOutput = async (event: WorkflowEventData<AgentStep>) => {
|
||||
const { agentName, response, toolCalls } = event.data;
|
||||
const agent = this.agents.get(agentName);
|
||||
if (!agent) {
|
||||
throw new Error(
|
||||
`parseAgentOutput failed: agent ${agentName} does not exist`,
|
||||
);
|
||||
}
|
||||
|
||||
// If no tool calls, return final response
|
||||
if (!toolCalls || toolCalls.length === 0) {
|
||||
@@ -355,31 +387,31 @@ export class AgentWorkflow {
|
||||
`[Agent ${agentName}]: No tool calls to process, returning final response`,
|
||||
);
|
||||
}
|
||||
const agentOutput = new AgentOutput({
|
||||
const agentOutput = {
|
||||
response,
|
||||
toolCalls: [],
|
||||
raw: response,
|
||||
currentAgentName: agentName,
|
||||
});
|
||||
const content = await this.agents
|
||||
.get(agentName)
|
||||
?.finalize(ctx, agentOutput, ctx.data.memory);
|
||||
};
|
||||
const content = await agent.finalize(
|
||||
this.stateful.getContext().state,
|
||||
agentOutput,
|
||||
);
|
||||
|
||||
return new StopEvent({
|
||||
result: content?.data.response.content as string,
|
||||
return stopAgentEvent.with({
|
||||
result: content.response.content,
|
||||
state: this.stateful.getContext().state,
|
||||
});
|
||||
}
|
||||
|
||||
return new ToolCallsEvent({
|
||||
return toolCallsEvent.with({
|
||||
agentName,
|
||||
toolCalls,
|
||||
});
|
||||
};
|
||||
|
||||
private executeToolCalls = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: ToolCallsEvent,
|
||||
): Promise<ToolResultsEvent | StopEvent<{ result: string }>> => {
|
||||
private executeToolCalls = async (event: WorkflowEventData<ToolCalls>) => {
|
||||
const { sendEvent } = getContext();
|
||||
const { agentName, toolCalls } = event.data;
|
||||
const agent = this.agents.get(agentName);
|
||||
if (!agent) {
|
||||
@@ -391,44 +423,42 @@ export class AgentWorkflow {
|
||||
// Execute each tool call
|
||||
for (const toolCall of toolCalls) {
|
||||
// Send single tool call event, useful for UI
|
||||
ctx.sendEvent(toolCall);
|
||||
const toolResult = new AgentToolCallResult({
|
||||
toolName: toolCall.data.toolName,
|
||||
toolKwargs: toolCall.data.toolKwargs,
|
||||
toolId: toolCall.data.toolId,
|
||||
sendEvent(agentToolCallEvent.with(toolCall));
|
||||
const toolResult = {
|
||||
toolName: toolCall.toolName,
|
||||
toolKwargs: toolCall.toolKwargs,
|
||||
toolId: toolCall.toolId,
|
||||
toolOutput: {
|
||||
id: toolCall.data.toolId,
|
||||
id: toolCall.toolId,
|
||||
result: "",
|
||||
isError: false,
|
||||
},
|
||||
returnDirect: false,
|
||||
raw: {},
|
||||
});
|
||||
};
|
||||
try {
|
||||
const output = await this.callTool(toolCall, ctx);
|
||||
toolResult.data.raw = output;
|
||||
toolResult.data.toolOutput.result =
|
||||
stringifyJSONToMessageContent(output);
|
||||
toolResult.data.returnDirect = toolCall.data.toolName === "handOff";
|
||||
const output = await this.callTool(toolCall);
|
||||
toolResult.raw = output;
|
||||
toolResult.toolOutput.result = stringifyJSONToMessageContent(output);
|
||||
toolResult.returnDirect = toolCall.toolName === "handOff";
|
||||
} catch (error) {
|
||||
toolResult.data.toolOutput.isError = true;
|
||||
toolResult.data.toolOutput.result = `Error: ${error}`;
|
||||
toolResult.toolOutput.isError = true;
|
||||
toolResult.toolOutput.result = `Error: ${error}`;
|
||||
}
|
||||
results.push(toolResult);
|
||||
// Send single tool result event, useful for UI
|
||||
ctx.sendEvent(toolResult);
|
||||
sendEvent(agentToolCallResultEvent.with(toolResult));
|
||||
}
|
||||
|
||||
return new ToolResultsEvent({
|
||||
return toolResultsEvent.with({
|
||||
agentName,
|
||||
results,
|
||||
});
|
||||
};
|
||||
|
||||
private processToolResults = async (
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
event: ToolResultsEvent,
|
||||
): Promise<AgentInput | StopEvent<{ result: string }>> => {
|
||||
event: WorkflowEventData<ToolResults>,
|
||||
) => {
|
||||
const { agentName, results } = event.data;
|
||||
|
||||
// Get agent
|
||||
@@ -437,18 +467,23 @@ export class AgentWorkflow {
|
||||
throw new Error(`Agent ${agentName} not found`);
|
||||
}
|
||||
|
||||
await agent.handleToolCallResults(ctx, results);
|
||||
await agent.handleToolCallResults(
|
||||
this.stateful.getContext().state,
|
||||
results,
|
||||
);
|
||||
|
||||
const directResult = results.find((r) => r.data.returnDirect);
|
||||
const directResult = results.find(
|
||||
(r: AgentToolCallResult) => r.returnDirect,
|
||||
);
|
||||
if (directResult) {
|
||||
const isHandoff = directResult.data.toolName === "handOff";
|
||||
const isHandoff = directResult.toolName === "handOff";
|
||||
|
||||
const output =
|
||||
typeof directResult.data.toolOutput.result === "string"
|
||||
? directResult.data.toolOutput.result
|
||||
: JSON.stringify(directResult.data.toolOutput.result);
|
||||
typeof directResult.toolOutput.result === "string"
|
||||
? directResult.toolOutput.result
|
||||
: JSON.stringify(directResult.toolOutput.result);
|
||||
|
||||
const agentOutput = new AgentOutput({
|
||||
const agentOutput = {
|
||||
response: {
|
||||
role: "assistant" as const,
|
||||
content: output,
|
||||
@@ -456,131 +491,120 @@ export class AgentWorkflow {
|
||||
toolCalls: [],
|
||||
raw: output,
|
||||
currentAgentName: agent.name,
|
||||
});
|
||||
};
|
||||
|
||||
await agent.finalize(ctx, agentOutput, ctx.data.memory);
|
||||
await agent.finalize(this.stateful.getContext().state, agentOutput);
|
||||
|
||||
if (isHandoff) {
|
||||
const nextAgentName = ctx.data.nextAgentName;
|
||||
const nextAgentName = this.stateful.getContext().state.nextAgentName;
|
||||
console.log(
|
||||
`[Agent ${agentName}]: Handoff to ${nextAgentName}: ${directResult.data.toolOutput.result}`,
|
||||
`[Agent ${agentName}]: Handoff to ${nextAgentName}: ${directResult.toolOutput.result}`,
|
||||
);
|
||||
if (nextAgentName) {
|
||||
ctx.data.currentAgentName = nextAgentName;
|
||||
ctx.data.nextAgentName = null;
|
||||
this.stateful.getContext().state.currentAgentName = nextAgentName;
|
||||
this.stateful.getContext().state.nextAgentName = null;
|
||||
|
||||
const messages = await ctx.data.memory.getMessages();
|
||||
return new AgentInput({
|
||||
const messages = await this.stateful
|
||||
.getContext()
|
||||
.state.memory.getMessages();
|
||||
return agentInputEvent.with({
|
||||
input: messages,
|
||||
currentAgentName: nextAgentName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new StopEvent({
|
||||
return stopAgentEvent.with({
|
||||
result: output,
|
||||
state: this.stateful.getContext().state,
|
||||
});
|
||||
}
|
||||
|
||||
// Continue with another agent step
|
||||
const messages = await ctx.data.memory.getMessages();
|
||||
return new AgentInput({
|
||||
const messages = await this.stateful
|
||||
.getContext()
|
||||
.state.memory.getMessages();
|
||||
return agentInputEvent.with({
|
||||
input: messages,
|
||||
currentAgentName: agent.name,
|
||||
});
|
||||
};
|
||||
|
||||
private setupWorkflowSteps() {
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<AgentInputData>],
|
||||
},
|
||||
this.handleInputStep,
|
||||
);
|
||||
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [AgentInput],
|
||||
},
|
||||
this.setupAgent,
|
||||
);
|
||||
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [AgentSetup],
|
||||
},
|
||||
this.runAgentStep,
|
||||
);
|
||||
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [AgentStepEvent],
|
||||
},
|
||||
this.parseAgentOutput,
|
||||
);
|
||||
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [ToolCallsEvent],
|
||||
},
|
||||
this.executeToolCalls,
|
||||
);
|
||||
|
||||
this.workflow.addStep(
|
||||
{
|
||||
inputs: [ToolResultsEvent],
|
||||
},
|
||||
this.processToolResults,
|
||||
);
|
||||
|
||||
return this;
|
||||
this.workflow.handle([startAgentEvent], this.handleInputStep);
|
||||
this.workflow.handle([agentInputEvent], this.setupAgent);
|
||||
this.workflow.handle([agentSetupEvent], this.runAgentStep);
|
||||
this.workflow.handle([agentStepEvent], this.parseAgentOutput);
|
||||
this.workflow.handle([toolCallsEvent], this.executeToolCalls);
|
||||
this.workflow.handle([toolResultsEvent], this.processToolResults);
|
||||
}
|
||||
|
||||
private callTool(
|
||||
toolCall: AgentToolCall,
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
) {
|
||||
private callTool(toolCall: AgentToolCall) {
|
||||
const tool = this.agents
|
||||
.get(toolCall.data.agentName)
|
||||
?.tools.find((t) => t.metadata.name === toolCall.data.toolName);
|
||||
.get(toolCall.agentName)
|
||||
?.tools.find((t) => t.metadata.name === toolCall.toolName);
|
||||
if (!tool) {
|
||||
throw new Error(`Tool ${toolCall.data.toolName} not found`);
|
||||
throw new Error(`Tool ${toolCall.toolName} not found`);
|
||||
}
|
||||
if (tool.metadata.requireContext) {
|
||||
const input = { context: ctx.data, ...toolCall.data.toolKwargs };
|
||||
const input = {
|
||||
context: this.stateful.getContext().state,
|
||||
...toolCall.toolKwargs,
|
||||
};
|
||||
return tool.call(input);
|
||||
} else {
|
||||
return tool.call(toolCall.data.toolKwargs);
|
||||
return tool.call(toolCall.toolKwargs);
|
||||
}
|
||||
}
|
||||
|
||||
run(
|
||||
runStream(
|
||||
userInput: string,
|
||||
params?: {
|
||||
chatHistory?: ChatMessage[];
|
||||
context?: AgentWorkflowContext;
|
||||
state?: AgentWorkflowState;
|
||||
},
|
||||
) {
|
||||
if (this.agents.size === 0) {
|
||||
throw new Error("No agents added to workflow");
|
||||
}
|
||||
this.setupWorkflowSteps();
|
||||
const contextData: AgentWorkflowContext = params?.context ?? {
|
||||
const state: AgentWorkflowState = {
|
||||
...(params?.state ?? {
|
||||
memory: new ChatMemoryBuffer({
|
||||
llm: this.agents.get(this.rootAgentName)?.llm ?? Settings.llm,
|
||||
}),
|
||||
scratchpad: [],
|
||||
currentAgentName: this.rootAgentName,
|
||||
agents: Array.from(this.agents.keys()),
|
||||
nextAgentName: null,
|
||||
}),
|
||||
userInput: userInput,
|
||||
memory: new ChatMemoryBuffer(),
|
||||
scratchpad: [],
|
||||
currentAgentName: this.rootAgentName,
|
||||
agents: Array.from(this.agents.keys()),
|
||||
nextAgentName: null,
|
||||
};
|
||||
|
||||
return this.workflow.run(
|
||||
{
|
||||
const { sendEvent, stream } = this.workflow.createContext(state);
|
||||
sendEvent(
|
||||
startAgentEvent.with({
|
||||
userInput: userInput,
|
||||
chatHistory: params?.chatHistory,
|
||||
},
|
||||
contextData,
|
||||
}),
|
||||
);
|
||||
return until(stream, stopAgentEvent);
|
||||
}
|
||||
|
||||
async run(
|
||||
userInput: string,
|
||||
params?: {
|
||||
chatHistory?: ChatMessage[];
|
||||
state?: AgentWorkflowState;
|
||||
},
|
||||
): Promise<WorkflowEventData<AgentResultData>> {
|
||||
const allEvents = await collect(this.runStream(userInput, params));
|
||||
const finalEvent = allEvents[allEvents.length - 1];
|
||||
if (!stopAgentEvent.include(finalEvent)) {
|
||||
throw new Error(
|
||||
`Agent stopped with unexpected ${finalEvent?.toString() ?? "unknown"} event.`,
|
||||
);
|
||||
}
|
||||
return finalEvent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +622,7 @@ const createHandoffTool = (agents: Map<string, BaseWorkflowAgent>) => {
|
||||
toAgent,
|
||||
reason,
|
||||
}: {
|
||||
context?: AgentWorkflowContext;
|
||||
context?: AgentWorkflowState;
|
||||
toAgent: string;
|
||||
reason: string;
|
||||
}) => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { StepContext } from "@llama-flow/llamaindex";
|
||||
import type { WorkflowContext } from "@llama-flow/core";
|
||||
import type { BaseToolWithCall, ChatMessage, LLM } from "@llamaindex/core/llms";
|
||||
import { BaseMemory } from "@llamaindex/core/memory";
|
||||
import type { AgentOutput, AgentToolCallResult } from "./events";
|
||||
|
||||
export type AgentWorkflowContext = {
|
||||
export type AgentWorkflowState = {
|
||||
userInput: string;
|
||||
memory: BaseMemory;
|
||||
scratchpad: ChatMessage[];
|
||||
@@ -28,7 +28,8 @@ export interface BaseWorkflowAgent {
|
||||
* Using memory directly to get messages instead of requiring them to be passed in
|
||||
*/
|
||||
takeStep(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
ctx: WorkflowContext,
|
||||
state: AgentWorkflowState,
|
||||
llmInput: ChatMessage[],
|
||||
tools: BaseToolWithCall[],
|
||||
): Promise<AgentOutput>;
|
||||
@@ -37,7 +38,7 @@ export interface BaseWorkflowAgent {
|
||||
* Handle results from tool calls
|
||||
*/
|
||||
handleToolCallResults(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
state: AgentWorkflowState,
|
||||
results: AgentToolCallResult[],
|
||||
): Promise<void>;
|
||||
|
||||
@@ -45,8 +46,7 @@ export interface BaseWorkflowAgent {
|
||||
* Finalize the agent's output
|
||||
*/
|
||||
finalize(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
state: AgentWorkflowState,
|
||||
output: AgentOutput,
|
||||
memory: BaseMemory,
|
||||
): Promise<AgentOutput>;
|
||||
}
|
||||
|
||||
@@ -1,43 +1,48 @@
|
||||
import { WorkflowEvent } from "@llama-flow/llamaindex";
|
||||
import { workflowEvent } from "@llama-flow/core";
|
||||
import type { JSONValue } from "@llamaindex/core/global";
|
||||
import type { ChatMessage, ToolResult } from "@llamaindex/core/llms";
|
||||
|
||||
export class AgentToolCall extends WorkflowEvent<{
|
||||
export type AgentToolCall = {
|
||||
agentName: string;
|
||||
toolName: string;
|
||||
toolKwargs: Record<string, JSONValue>;
|
||||
toolId: string;
|
||||
}> {}
|
||||
};
|
||||
export const agentToolCallEvent = workflowEvent<AgentToolCall>();
|
||||
|
||||
export class AgentToolCallResult extends WorkflowEvent<{
|
||||
export type AgentToolCallResult = {
|
||||
toolName: string;
|
||||
toolKwargs: Record<string, JSONValue>;
|
||||
toolId: string;
|
||||
toolOutput: ToolResult;
|
||||
returnDirect: boolean;
|
||||
raw: JSONValue;
|
||||
}> {}
|
||||
};
|
||||
export const agentToolCallResultEvent = workflowEvent<AgentToolCallResult>();
|
||||
|
||||
export class AgentInput extends WorkflowEvent<{
|
||||
export type AgentInput = {
|
||||
input: ChatMessage[];
|
||||
currentAgentName: string;
|
||||
}> {}
|
||||
};
|
||||
export const agentInputEvent = workflowEvent<AgentInput>();
|
||||
|
||||
export class AgentSetup extends WorkflowEvent<{
|
||||
export type AgentSetup = {
|
||||
input: ChatMessage[];
|
||||
currentAgentName: string;
|
||||
}> {}
|
||||
};
|
||||
export const agentSetupEvent = workflowEvent<AgentSetup>();
|
||||
|
||||
export class AgentStream extends WorkflowEvent<{
|
||||
export const agentStreamEvent = workflowEvent<{
|
||||
delta: string;
|
||||
response: string;
|
||||
currentAgentName: string;
|
||||
raw: unknown;
|
||||
}> {}
|
||||
}>();
|
||||
|
||||
export class AgentOutput extends WorkflowEvent<{
|
||||
export type AgentOutput = {
|
||||
response: ChatMessage;
|
||||
toolCalls: AgentToolCall[];
|
||||
raw: unknown;
|
||||
currentAgentName: string;
|
||||
}> {}
|
||||
};
|
||||
export const agentOutputEvent = workflowEvent<AgentOutput>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StepContext } from "@llama-flow/llamaindex";
|
||||
import type { WorkflowContext } from "@llama-flow/core";
|
||||
import type { JSONObject } from "@llamaindex/core/global";
|
||||
import { Settings } from "@llamaindex/core/global";
|
||||
import {
|
||||
@@ -7,14 +7,13 @@ import {
|
||||
type ChatMessage,
|
||||
type ChatResponseChunk,
|
||||
} from "@llamaindex/core/llms";
|
||||
import { BaseMemory } from "@llamaindex/core/memory";
|
||||
import { AgentWorkflow } from "./agent-workflow";
|
||||
import { type AgentWorkflowContext, type BaseWorkflowAgent } from "./base";
|
||||
import { type AgentWorkflowState, type BaseWorkflowAgent } from "./base";
|
||||
import {
|
||||
AgentOutput,
|
||||
AgentStream,
|
||||
AgentToolCall,
|
||||
AgentToolCallResult,
|
||||
agentStreamEvent,
|
||||
type AgentOutput,
|
||||
type AgentToolCall,
|
||||
type AgentToolCallResult,
|
||||
} from "./events";
|
||||
|
||||
const DEFAULT_SYSTEM_PROMPT =
|
||||
@@ -110,12 +109,13 @@ export class FunctionAgent implements BaseWorkflowAgent {
|
||||
}
|
||||
|
||||
async takeStep(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
ctx: WorkflowContext,
|
||||
state: AgentWorkflowState,
|
||||
llmInput: ChatMessage[],
|
||||
tools: BaseToolWithCall[],
|
||||
): Promise<AgentOutput> {
|
||||
// Get scratchpad from context or initialize if not present
|
||||
const scratchpad: ChatMessage[] = ctx.data.scratchpad;
|
||||
const scratchpad: ChatMessage[] = state.scratchpad;
|
||||
const currentLLMInput = [...llmInput, ...scratchpad];
|
||||
|
||||
const responseStream = await this.llm.chat({
|
||||
@@ -129,7 +129,7 @@ export class FunctionAgent implements BaseWorkflowAgent {
|
||||
for await (const chunk of responseStream) {
|
||||
response += chunk.delta;
|
||||
ctx.sendEvent(
|
||||
new AgentStream({
|
||||
agentStreamEvent.with({
|
||||
delta: chunk.delta,
|
||||
response: response,
|
||||
currentAgentName: this.name,
|
||||
@@ -140,7 +140,7 @@ export class FunctionAgent implements BaseWorkflowAgent {
|
||||
if (toolCallsInChunk.length > 0) {
|
||||
// Just upsert the tool calls with the latest one if they exist
|
||||
toolCallsInChunk.forEach((toolCall) => {
|
||||
toolCalls.set(toolCall.data.toolId, toolCall);
|
||||
toolCalls.set(toolCall.toolId, toolCall);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -153,62 +153,61 @@ export class FunctionAgent implements BaseWorkflowAgent {
|
||||
if (toolCalls.size > 0) {
|
||||
message.options = {
|
||||
toolCall: Array.from(toolCalls.values()).map((toolCall) => ({
|
||||
name: toolCall.data.toolName,
|
||||
input: toolCall.data.toolKwargs,
|
||||
id: toolCall.data.toolId,
|
||||
name: toolCall.toolName,
|
||||
input: toolCall.toolKwargs,
|
||||
id: toolCall.toolId,
|
||||
})),
|
||||
};
|
||||
}
|
||||
scratchpad.push(message);
|
||||
ctx.data.scratchpad = scratchpad;
|
||||
return new AgentOutput({
|
||||
state.scratchpad = scratchpad;
|
||||
return {
|
||||
response: message,
|
||||
toolCalls: Array.from(toolCalls.values()),
|
||||
raw: lastChunk?.raw,
|
||||
currentAgentName: this.name,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
async handleToolCallResults(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
state: AgentWorkflowState,
|
||||
results: AgentToolCallResult[],
|
||||
): Promise<void> {
|
||||
const scratchpad: ChatMessage[] = ctx.data.scratchpad;
|
||||
const scratchpad: ChatMessage[] = state.scratchpad;
|
||||
|
||||
for (const result of results) {
|
||||
const content = result.data.toolOutput.result;
|
||||
const content = result.toolOutput.result;
|
||||
|
||||
const rawToolMessage = {
|
||||
role: "user" as const,
|
||||
content,
|
||||
options: {
|
||||
toolResult: {
|
||||
id: result.data.toolId,
|
||||
id: result.toolId,
|
||||
result: content,
|
||||
isError: result.data.toolOutput.isError,
|
||||
isError: result.toolOutput.isError,
|
||||
},
|
||||
},
|
||||
};
|
||||
ctx.data.scratchpad.push(rawToolMessage);
|
||||
state.scratchpad.push(rawToolMessage);
|
||||
}
|
||||
|
||||
ctx.data.scratchpad = scratchpad;
|
||||
state.scratchpad = scratchpad;
|
||||
}
|
||||
|
||||
async finalize(
|
||||
ctx: StepContext<AgentWorkflowContext>,
|
||||
state: AgentWorkflowState,
|
||||
output: AgentOutput,
|
||||
memory: BaseMemory,
|
||||
): Promise<AgentOutput> {
|
||||
// Get scratchpad messages
|
||||
const scratchpad: ChatMessage[] = ctx.data.scratchpad;
|
||||
const scratchpad: ChatMessage[] = state.scratchpad;
|
||||
|
||||
for (const msg of scratchpad) {
|
||||
memory.put(msg);
|
||||
state.memory.put(msg);
|
||||
}
|
||||
|
||||
// Clear scratchpad after finalization
|
||||
ctx.data.scratchpad = [];
|
||||
state.scratchpad = [];
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -233,24 +232,24 @@ export class FunctionAgent implements BaseWorkflowAgent {
|
||||
toolKwargs = call.input as JSONObject;
|
||||
}
|
||||
|
||||
return new AgentToolCall({
|
||||
return {
|
||||
agentName: this.name,
|
||||
toolName: call.name,
|
||||
toolKwargs: toolKwargs,
|
||||
toolId: call.id,
|
||||
});
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const invalidToolCalls = toolCalls.filter(
|
||||
(call) =>
|
||||
!this.tools.some((tool) => tool.metadata.name === call.data.toolName),
|
||||
!this.tools.some((tool) => tool.metadata.name === call.toolName),
|
||||
);
|
||||
|
||||
if (invalidToolCalls.length > 0) {
|
||||
const invalidToolNames = invalidToolCalls
|
||||
.map((call) => call.data.toolName)
|
||||
.map((call) => call.toolName)
|
||||
.join(", ");
|
||||
throw new Error(`Tools not found: ${invalidToolNames}`);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from "@llama-flow/llamaindex";
|
||||
export * from "@llama-flow/core";
|
||||
export * from "@llama-flow/core/middleware/state";
|
||||
export * from "@llama-flow/core/stream/run";
|
||||
export * from "./agent/index.js";
|
||||
|
||||
@@ -3,7 +3,23 @@ import { FunctionTool } from "@llamaindex/core/tools";
|
||||
import { MockLLM } from "@llamaindex/core/utils";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { z } from "zod";
|
||||
import { AgentWorkflow, FunctionAgent, agent, multiAgent } from "../src/agent";
|
||||
import {
|
||||
AgentWorkflow,
|
||||
FunctionAgent,
|
||||
agent,
|
||||
agentInputEvent,
|
||||
agentOutputEvent,
|
||||
agentSetupEvent,
|
||||
agentStepEvent,
|
||||
agentStreamEvent,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
multiAgent,
|
||||
startAgentEvent,
|
||||
stopAgentEvent,
|
||||
toolCallsEvent,
|
||||
toolResultsEvent,
|
||||
} from "../src/agent";
|
||||
import { setupToolCallingMockLLM } from "./mock";
|
||||
|
||||
describe("AgentWorkflow", () => {
|
||||
@@ -116,45 +132,73 @@ describe("AgentWorkflow", () => {
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const result = workflow.run("What is 2 + 2?");
|
||||
const result = workflow.runStream("What is 2 + 2?");
|
||||
|
||||
const events = [];
|
||||
for await (const event of result) {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
// Validate the specific sequence of events emitted by the workflow
|
||||
const expectedEventSequence = [
|
||||
"StartEvent",
|
||||
"AgentInput",
|
||||
"AgentSetup",
|
||||
"AgentStream",
|
||||
"AgentStepEvent",
|
||||
"AgentOutput",
|
||||
"ToolCallsEvent",
|
||||
"AgentToolCall",
|
||||
"AgentToolCallResult",
|
||||
"ToolResultsEvent",
|
||||
"AgentInput",
|
||||
"AgentSetup",
|
||||
"AgentStream",
|
||||
"AgentStepEvent",
|
||||
"AgentOutput",
|
||||
"StopEvent",
|
||||
startAgentEvent,
|
||||
agentInputEvent,
|
||||
agentSetupEvent,
|
||||
agentStreamEvent,
|
||||
agentStepEvent,
|
||||
agentOutputEvent,
|
||||
toolCallsEvent,
|
||||
agentToolCallEvent,
|
||||
agentToolCallResultEvent,
|
||||
toolResultsEvent,
|
||||
agentInputEvent,
|
||||
agentSetupEvent,
|
||||
agentStreamEvent,
|
||||
agentStepEvent,
|
||||
agentOutputEvent,
|
||||
stopAgentEvent,
|
||||
];
|
||||
|
||||
// Check the event sequence - exact types in exact order
|
||||
expect(events.map((e) => e.constructor.name)).toEqual(
|
||||
expectedEventSequence,
|
||||
);
|
||||
let i = 0;
|
||||
for await (const event of result) {
|
||||
expect(expectedEventSequence[i++].include(event));
|
||||
}
|
||||
|
||||
// Check if addTool is called
|
||||
expect(addTool.call).toHaveBeenCalled();
|
||||
|
||||
// Check that we have events
|
||||
expect(events.length).toEqual(expectedEventSequence.length);
|
||||
expect(i).toEqual(expectedEventSequence.length);
|
||||
});
|
||||
|
||||
//
|
||||
test("run method executes workflow correctly", async () => {
|
||||
// Setup mock LLM and tool
|
||||
const mockLLM = setupToolCallingMockLLM("add", { x: 1, y: 2 });
|
||||
Settings.llm = mockLLM;
|
||||
|
||||
const addTool = FunctionTool.from(
|
||||
(params: { x: number; y: number }) => {
|
||||
return params.x + params.y;
|
||||
},
|
||||
{
|
||||
name: "add",
|
||||
description: "Adds two numbers",
|
||||
parameters: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
vi.spyOn(addTool, "call");
|
||||
|
||||
// Create workflow with single agent
|
||||
const workflow = AgentWorkflow.fromTools({
|
||||
tools: [addTool],
|
||||
llm: mockLLM,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
// Run the workflow
|
||||
const result = await workflow.run("What is 1 + 2?");
|
||||
// Verify the result is a stopAgentEvent with the correct data
|
||||
expect(stopAgentEvent.include(result)).toBe(true);
|
||||
expect(result.data.result).toBe("Final response");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChatMessage } from "@llamaindex/core/llms";
|
||||
import { FunctionTool } from "@llamaindex/core/tools";
|
||||
import { tool } from "@llamaindex/core/tools";
|
||||
import { MockLLM } from "@llamaindex/core/utils";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { z } from "zod";
|
||||
@@ -11,17 +11,15 @@ mockLLM.supportToolCall = true;
|
||||
describe("FunctionAgent", () => {
|
||||
test("function agent can parse tool call results", async () => {
|
||||
// Create minimal tools
|
||||
const addTool = FunctionTool.from(
|
||||
(params: { x: number; y: number }) => params.x + params.y,
|
||||
{
|
||||
name: "add",
|
||||
description: "Adds two numbers",
|
||||
parameters: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
const addTool = tool({
|
||||
name: "add",
|
||||
description: "Adds two numbers",
|
||||
parameters: z.object({
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
}),
|
||||
execute: (params: { x: number; y: number }) => params.x + params.y,
|
||||
});
|
||||
|
||||
const calculatorAgent = new FunctionAgent({
|
||||
name: "CalculatorAgent",
|
||||
@@ -30,32 +28,27 @@ describe("FunctionAgent", () => {
|
||||
llm: mockLLM,
|
||||
});
|
||||
|
||||
const dummyResult = {
|
||||
data: {
|
||||
toolName: "add",
|
||||
toolKwargs: { x: 2, y: 2 },
|
||||
toolId: "123",
|
||||
toolOutput: { result: "4", isError: false, id: "123" },
|
||||
returnDirect: false,
|
||||
},
|
||||
displayName: "test",
|
||||
} as unknown as AgentToolCallResult;
|
||||
|
||||
const dummyContext = {
|
||||
data: {
|
||||
scratchpad: [],
|
||||
},
|
||||
const dummyResult: AgentToolCallResult = {
|
||||
toolName: "add",
|
||||
toolKwargs: { x: 2, y: 2 },
|
||||
toolId: "123",
|
||||
toolOutput: { result: "4", isError: false, id: "123" },
|
||||
returnDirect: false,
|
||||
raw: [],
|
||||
};
|
||||
|
||||
await calculatorAgent.handleToolCallResults(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
dummyContext as any,
|
||||
[dummyResult],
|
||||
);
|
||||
const workflowData = {
|
||||
scratchpad: [],
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
await calculatorAgent.handleToolCallResults(workflowData as any, [
|
||||
dummyResult,
|
||||
]);
|
||||
|
||||
// Check if agent's scratchpad has been updated with the correct message
|
||||
expect(dummyContext.data.scratchpad.length).toEqual(1);
|
||||
const message = dummyContext.data.scratchpad[0] as unknown as ChatMessage;
|
||||
expect(workflowData.scratchpad.length).toEqual(1);
|
||||
const message = workflowData.scratchpad[0] as unknown as ChatMessage;
|
||||
expect(message.content).toEqual("4");
|
||||
expect(message.role).toEqual("user");
|
||||
expect(message.options).toEqual({
|
||||
|
||||
Generated
+55
-1477
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.9.0",
|
||||
"llamaindex": "workspace:*",
|
||||
"@llamaindex/workflow": "workspace:*",
|
||||
"zod": "^3.24.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
import { agent, Document, tool } from "llamaindex";
|
||||
import { agent } from "@llamaindex/workflow";
|
||||
import { Document, tool } from "llamaindex";
|
||||
import { ok } from "node:assert";
|
||||
import { test } from "node:test";
|
||||
import { z } from "zod";
|
||||
|
||||
import { VectorStoreIndex } from "llamaindex";
|
||||
import { ReActAgent } from "llamaindex/agent";
|
||||
import { LlamaCloudIndex } from "llamaindex/cloud";
|
||||
import { BaseChatEngine } from "llamaindex/engines";
|
||||
import { CorrectnessEvaluator } from "llamaindex/evaluation";
|
||||
import { BaseExtractor } from "llamaindex/extractors";
|
||||
import { BaseIndex } from "llamaindex/indices";
|
||||
import { IngestionPipeline } from "llamaindex/ingestion";
|
||||
import { NodeParser } from "llamaindex/node-parser";
|
||||
import { ObjectIndex } from "llamaindex/objects";
|
||||
import { SimilarityPostprocessor } from "llamaindex/postprocessors";
|
||||
import { BaseSelector } from "llamaindex/selectors";
|
||||
import { BaseChatStore } from "llamaindex/storage";
|
||||
import { FunctionTool } from "llamaindex/tools";
|
||||
import { FilterCondition } from "llamaindex/vector-store";
|
||||
|
||||
test("LlamaIndex module resolution test", async (t) => {
|
||||
await t.test("works with Document class", () => {
|
||||
const doc = new Document({ text: "This is a test document" });
|
||||
@@ -23,6 +40,7 @@ test("LlamaIndex module resolution test", async (t) => {
|
||||
|
||||
await t.test("works with dynamic imports", async () => {
|
||||
const mod = await import("llamaindex"); // simulate commonjs
|
||||
const agentMod = await import("@llamaindex/workflow"); // simulate commonjs
|
||||
|
||||
const doc = new mod.Document({ text: "This is a test document" });
|
||||
ok(doc.text === "This is a test document");
|
||||
@@ -37,7 +55,29 @@ test("LlamaIndex module resolution test", async (t) => {
|
||||
execute: ({ a, b }) => `${a + b}`,
|
||||
});
|
||||
|
||||
const myAgent = mod.agent({ tools: [sumNumbers] });
|
||||
const myAgent = agentMod.agent({ tools: [sumNumbers] });
|
||||
ok(myAgent !== undefined);
|
||||
});
|
||||
|
||||
await t.test("all imports work", () => {
|
||||
const allImports = [
|
||||
VectorStoreIndex,
|
||||
ReActAgent,
|
||||
LlamaCloudIndex,
|
||||
BaseChatEngine,
|
||||
CorrectnessEvaluator,
|
||||
BaseExtractor,
|
||||
BaseIndex,
|
||||
IngestionPipeline,
|
||||
ObjectIndex,
|
||||
NodeParser,
|
||||
SimilarityPostprocessor,
|
||||
BaseSelector,
|
||||
BaseChatStore,
|
||||
FunctionTool,
|
||||
FilterCondition,
|
||||
];
|
||||
|
||||
ok(allImports.filter(Boolean).length === allImports.length);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user