mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-15 06:52:45 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 588cd0f0b9 | |||
| 7ca9ddff86 | |||
| 3310eaae29 | |||
| 96dac4ddfd | |||
| f9ee683593 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@llamaindex/openai": patch
|
||||
---
|
||||
|
||||
Update o4-mini to accept reasoning parameters and exclude temperature
|
||||
@@ -1,5 +1,13 @@
|
||||
# @llamaindex/doc
|
||||
|
||||
## 0.2.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.2.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/doc",
|
||||
"version": "0.2.12",
|
||||
"version": "0.2.13",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "fumadocs-mdx",
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { baseOptions } from "@/app/layout.config";
|
||||
import { AITrigger } from "@/components/ai-chat";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { source } from "@/lib/source";
|
||||
import { cn } from "@/lib/utils";
|
||||
import "fumadocs-twoslash/twoslash.css";
|
||||
import { DocsLayout } from "fumadocs-ui/layouts/docs";
|
||||
import { MessageCircle } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
@@ -15,21 +11,6 @@ export default function Layout({ children }: { children: ReactNode }) {
|
||||
{...baseOptions}
|
||||
nav={{
|
||||
...baseOptions.nav,
|
||||
children: (
|
||||
<AITrigger
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
variant: "secondary",
|
||||
size: "xs",
|
||||
className:
|
||||
"text-fd-muted-foreground ms-2 gap-1.5 rounded-full px-2 md:flex-1",
|
||||
}),
|
||||
)}
|
||||
>
|
||||
<MessageCircle className="size-3" />
|
||||
Ask LlamaCloud
|
||||
</AITrigger>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -22,7 +22,7 @@ npm i @llamaindex/server
|
||||
|
||||
## Quick Start
|
||||
|
||||
Create index.ts file and add the following code:
|
||||
Create an `index.ts` file and add the following code:
|
||||
|
||||
```ts
|
||||
import { LlamaIndexServer } from "@llamaindex/server";
|
||||
@@ -43,20 +43,20 @@ new LlamaIndexServer({
|
||||
|
||||
In the same directory as `index.ts`, run the following command to start the server:
|
||||
|
||||
```bash
|
||||
tsx index.ts
|
||||
```
|
||||
```bash
|
||||
tsx index.ts
|
||||
```
|
||||
The server will start at `http://localhost:3000`
|
||||
|
||||
You can also make a request to the server:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/api/chat" -H "Content-Type: application/json" -d '{"message": "Who is the first president of the United States?"}'
|
||||
```
|
||||
```bash
|
||||
curl -X POST "http://localhost:3000/api/chat" -H "Content-Type: application/json" -d '{"message": "Who is the first president of the United States?"}'
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The LlamaIndexServer accepts the following configuration
|
||||
The `LlamaIndexServer` accepts the following configuration options:
|
||||
|
||||
- `workflow`: A callable function that creates a workflow instance for each request
|
||||
- `uiConfig`: An object to configure the chat UI containing the following properties:
|
||||
@@ -68,6 +68,72 @@ The LlamaIndexServer accepts the following configuration
|
||||
LlamaIndexServer accepts all the configuration options from Nextjs Custom Server such as `port`, `hostname`, `dev`, etc.
|
||||
See all Nextjs Custom Server options [here](https://nextjs.org/docs/app/building-your-application/configuring/custom-server).
|
||||
|
||||
## AI-generated UI Components
|
||||
|
||||
The LlamaIndex server provides support for rendering workflow events using custom UI components, allowing you to extend and customize the chat interface.
|
||||
These components can be auto-generated using an LLM by providing a JSON schema of the workflow event.
|
||||
|
||||
### UI Event Schema
|
||||
|
||||
To display custom UI components, your workflow needs to emit UI events that have an event type for identification and a data object:
|
||||
|
||||
```typescript
|
||||
class UIEvent extends WorkflowEvent<{
|
||||
type: "ui_event";
|
||||
data: UIEventData;
|
||||
}> {}
|
||||
```
|
||||
|
||||
The `data` object can be any JSON object. To enable AI generation of the UI component, you need to provide a schema for that data (here we're using Zod):
|
||||
|
||||
```typescript
|
||||
const MyEventDataSchema = z.object({
|
||||
stage: z.enum(["retrieve", "analyze", "answer"]).describe("The current stage the workflow process is in."),
|
||||
progress: z.number().min(0).max(1).describe("The progress in percent of the current stage"),
|
||||
}).describe("WorkflowStageProgress");
|
||||
|
||||
type UIEventData = z.infer<typeof MyEventDataSchema>;
|
||||
```
|
||||
|
||||
### Generate UI Components
|
||||
|
||||
The `generateEventComponent` function uses an LLM to generate a custom UI component based on the JSON schema of a workflow event. The schema should contain accurate descriptions of each field so that the LLM can generate matching components for your use case. We've done this for you in the example above using the `describe` function from Zod:
|
||||
|
||||
```typescript
|
||||
import { OpenAI } from "llamaindex";
|
||||
import { generateEventComponent } from "@llamaindex/server";
|
||||
import { MyEventDataSchema } from "./your-workflow";
|
||||
|
||||
// Also works well with Claude 3.5 Sonnet and Google Gemini 2.5 Pro
|
||||
const llm = new OpenAI({ model: "gpt-4.1" });
|
||||
const code = generateEventComponent(MyEventDataSchema, llm);
|
||||
```
|
||||
|
||||
After generating the code, we need to save it to a file. The file name must match the event type from your workflow (e.g., `ui_event.jsx` for handling events with `ui_event` type):
|
||||
|
||||
```ts
|
||||
fs.writeFileSync("components/ui_event.jsx", code);
|
||||
```
|
||||
|
||||
Feel free to modify the generated code to match your needs. If you're not satisfied with the generated code, we suggest improving the provided JSON schema first or trying another LLM.
|
||||
|
||||
> Note that `generateEventComponent` is generating JSX code, but you can also provide a TSX file.
|
||||
|
||||
|
||||
### Server Setup
|
||||
|
||||
To use the generated UI components, you need to initialize the LlamaIndex server with the `componentsDir` that contains your custom UI components:
|
||||
|
||||
```ts
|
||||
new LlamaIndexServer({
|
||||
workflow: createWorkflow,
|
||||
uiConfig: {
|
||||
appTitle: "LlamaIndex App",
|
||||
componentsDir: "components",
|
||||
},
|
||||
}).start();
|
||||
```
|
||||
|
||||
## Default Endpoints and Features
|
||||
|
||||
### Chat Endpoint
|
||||
@@ -85,69 +151,19 @@ The server always provides a chat interface at the root path (`/`) with:
|
||||
### Static File Serving
|
||||
|
||||
- The server automatically mounts the `data` and `output` folders at `{server_url}{api_prefix}/files/data` (default: `/api/files/data`) and `{server_url}{api_prefix}/files/output` (default: `/api/files/output`) respectively.
|
||||
- Your workflows can use both folders to store and access files. As a convention, the `data` folder is used for documents that are ingested and the `output` folder is used for documents that are generated by the workflow.
|
||||
- Your workflows can use both folders to store and access files. By convention, the `data` folder is used for documents that are ingested, and the `output` folder is used for documents generated by the workflow.
|
||||
|
||||
|
||||
## Custom UI Components
|
||||
|
||||
The LlamaIndex server provides support for rendering workflow events using custom UI components, allowing you to extend and customize the chat interface.
|
||||
|
||||
### Overview
|
||||
|
||||
Custom UI components are a powerful feature that enables you to:
|
||||
|
||||
- Add custom interface elements to the chat UI using React JSX or TSX files
|
||||
- Extend the default chat interface functionality
|
||||
- Create specialized visualizations or interactions
|
||||
|
||||
### Configuration
|
||||
|
||||
Your workflow must emit events that fit this structure, allowing the LlamaIndex server to display the right UI components based on the event type.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "<event_name>",
|
||||
"data": <data model>
|
||||
}
|
||||
```
|
||||
|
||||
### Server Setup
|
||||
|
||||
1. Initialize the LlamaIndex server with a component directory:
|
||||
|
||||
```ts
|
||||
new LlamaIndexServer({
|
||||
workflow: createWorkflow,
|
||||
uiConfig: {
|
||||
appTitle: "LlamaIndex App",
|
||||
componentsDir: "components",
|
||||
},
|
||||
}).start();
|
||||
```
|
||||
|
||||
2. Add the custom component code to the directory following the naming pattern:
|
||||
|
||||
- File Extension: `.jsx` and `.tsx` for React components
|
||||
- File Name: Should match the event type from your workflow (e.g., `deep_research_event.jsx` for handling `deep_research_event` type that you defined in your workflow). If there are TSX and JSX files with the same name, the TSX file will be used.
|
||||
- Component Name: Export a default React component named `Component` that receives props from the event data
|
||||
|
||||
Example component structure:
|
||||
|
||||
```jsx
|
||||
function Component({ events }) {
|
||||
// Your component logic here
|
||||
return (
|
||||
// Your UI code here
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Always provide a workflow factory that creates fresh workflow instances
|
||||
2. Use environment variables for sensitive configuration
|
||||
3. Use starter questions to guide users in the chat UI
|
||||
1. Always provide a workflow factory that creates a fresh workflow instance for each request.
|
||||
2. Use environment variables for sensitive configuration (e.g., API keys).
|
||||
3. Use starter questions to guide users in the chat UI.
|
||||
|
||||
## Getting Started with a New Project
|
||||
|
||||
Want to start a new project with LlamaIndexServer? Check out our [create-llama](https://github.com/run-llama/create-llama) tool to quickly generate a new project with LlamaIndexServer.
|
||||
Want to start a new project with LlamaIndexServer? Check out our [create-llama](https://github.com/run-llama/create-llama) tool to quickly generate a new project with LlamaIndexServer.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [LlamaIndexServer](/docs/api/classes/LlamaIndexServer)
|
||||
@@ -1,530 +0,0 @@
|
||||
---
|
||||
title: Advanced Event Handling
|
||||
description: Master complex event patterns and middleware with Workflows
|
||||
---
|
||||
|
||||
This guide explores advanced event handling techniques and patterns you can use with Workflows to build more sophisticated patterns.
|
||||
|
||||
## Event Composition
|
||||
|
||||
Workflows allow you to work with different event types and compose them in powerful ways:
|
||||
|
||||
### Multiple Event Types
|
||||
|
||||
You can define multiple event types for different kinds of data flowing through your workflow:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// Define different event types
|
||||
const textEvent = workflowEvent<string>();
|
||||
const numberEvent = workflowEvent<number>();
|
||||
const booleanEvent = workflowEvent<boolean>();
|
||||
const complexEvent = workflowEvent<{ id: string; value: number }>();
|
||||
|
||||
// Create a workflow that can process different event types
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Handle text events
|
||||
workflow.handle([textEvent], (event) => {
|
||||
console.log(`Processing text: ${event.data}`);
|
||||
return numberEvent.with(event.data.length);
|
||||
});
|
||||
|
||||
// Handle number events
|
||||
workflow.handle([numberEvent], (event) => {
|
||||
const isEven = event.data % 2 === 0;
|
||||
console.log(`Number ${event.data} is ${isEven ? 'even' : 'odd'}`);
|
||||
return booleanEvent.with(isEven);
|
||||
});
|
||||
|
||||
// Handle boolean events
|
||||
workflow.handle([booleanEvent], (event) => {
|
||||
return complexEvent.with({
|
||||
id: crypto.randomUUID(),
|
||||
value: event.data ? 100 : 0
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Event Branching and Merging
|
||||
|
||||
You can create complex event flows with branching and merging patterns:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, until, collect } from "llamaindex";
|
||||
|
||||
// Define events for a data processing pipeline
|
||||
const inputEvent = workflowEvent<string>();
|
||||
const validateEvent = workflowEvent<string>();
|
||||
const processEvent = workflowEvent<string>();
|
||||
const errorEvent = workflowEvent<Error>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
const completeEvent = workflowEvent<string[]>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Branch based on input validation
|
||||
workflow.handle([inputEvent], (event) => {
|
||||
if (event.data && event.data.trim().length > 0) {
|
||||
return validateEvent.with(event.data.trim());
|
||||
} else {
|
||||
return errorEvent.with(new Error("Empty input"));
|
||||
}
|
||||
});
|
||||
|
||||
// Process valid inputs
|
||||
workflow.handle([validateEvent], (event) => {
|
||||
return processEvent.with(event.data.toUpperCase());
|
||||
});
|
||||
|
||||
// Handle processing
|
||||
workflow.handle([processEvent], (event) => {
|
||||
return resultEvent.with(`Processed: ${event.data}`);
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
workflow.handle([errorEvent], (event) => {
|
||||
return resultEvent.with(`Error: ${event.data.message}`);
|
||||
});
|
||||
|
||||
// Merge results: collect multiple results into a single completion event
|
||||
workflow.handle([inputEvent], (start) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
|
||||
// Process all inputs
|
||||
const inputs = start.data.split(',').map(s => s.trim());
|
||||
inputs.forEach(input => sendEvent(inputEvent.with(input)));
|
||||
|
||||
// Collect all results
|
||||
const results = await collect(
|
||||
until(stream, result => resultEvent.include(result))
|
||||
.filter(ev => resultEvent.include(ev))
|
||||
.take(inputs.length)
|
||||
);
|
||||
|
||||
return completeEvent.with(results.map(r => r.data));
|
||||
});
|
||||
```
|
||||
|
||||
## Event Filtering and Transformation
|
||||
|
||||
You can filter and transform events to build sophisticated data processing pipelines:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const dataEvent = workflowEvent<number>();
|
||||
const evenEvent = workflowEvent<number>();
|
||||
const oddEvent = workflowEvent<number>();
|
||||
const transformedEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string[]>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Filter even numbers
|
||||
workflow.handle([dataEvent], (event) => {
|
||||
if (event.data % 2 === 0) {
|
||||
return evenEvent.with(event.data);
|
||||
} else {
|
||||
return oddEvent.with(event.data);
|
||||
}
|
||||
});
|
||||
|
||||
// Transform even numbers
|
||||
workflow.handle([evenEvent], (event) => {
|
||||
return transformedEvent.with(`Even: ${event.data}`);
|
||||
});
|
||||
|
||||
// Transform odd numbers
|
||||
workflow.handle([oddEvent], (event) => {
|
||||
return transformedEvent.with(`Odd: ${event.data}`);
|
||||
});
|
||||
|
||||
// Collect and organize results
|
||||
workflow.handle([dataEvent], (start) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
|
||||
// Generate a sequence of numbers
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
sendEvent(dataEvent.with(i));
|
||||
}
|
||||
|
||||
// Collect transformed events
|
||||
const results = await collect(
|
||||
until(stream)
|
||||
.filter(ev => transformedEvent.include(ev))
|
||||
.take(10)
|
||||
);
|
||||
|
||||
return resultEvent.with(results.map(r => r.data));
|
||||
});
|
||||
```
|
||||
|
||||
## Working with `withTraceEvents` Middleware
|
||||
|
||||
The `withTraceEvents` middleware adds powerful tracing capabilities to your workflows:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createWorkflow,
|
||||
workflowEvent,
|
||||
withTraceEvents,
|
||||
runOnce,
|
||||
createHandlerDecorator
|
||||
} from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<string>();
|
||||
const processEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow with tracing
|
||||
const workflow = withTraceEvents(createWorkflow());
|
||||
|
||||
// Create a custom handler decorator that logs execution time
|
||||
const measureTime = createHandlerDecorator({
|
||||
debugLabel: "measureTime",
|
||||
getInitialValue: () => performance.now(),
|
||||
onBeforeHandler: (handler, context, startTime) => {
|
||||
console.log(`Starting handler execution at ${new Date().toISOString()}`);
|
||||
return handler;
|
||||
},
|
||||
onAfterHandler: (result, context, startTime) => {
|
||||
const duration = performance.now() - startTime;
|
||||
console.log(`Handler executed in ${duration.toFixed(2)}ms`);
|
||||
return startTime; // Return the initial value for next execution
|
||||
}
|
||||
});
|
||||
|
||||
// Run a specific handler only once
|
||||
workflow.handle(
|
||||
[startEvent],
|
||||
runOnce((event) => {
|
||||
console.log("This handler will only run once per workflow context");
|
||||
return processEvent.with(event.data);
|
||||
})
|
||||
);
|
||||
|
||||
// Measure the execution time of this handler
|
||||
workflow.handle(
|
||||
[processEvent],
|
||||
measureTime((event) => {
|
||||
// Simulate processing time
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < 100) {
|
||||
// Busy wait for 100ms
|
||||
}
|
||||
return resultEvent.with(`Processed: ${event.data}`);
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Debugging with Substreams
|
||||
|
||||
You can use the `substream` feature to debug specific event flows:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, withTraceEvents } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const queryEvent = workflowEvent<string>();
|
||||
const fetchEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow with tracing
|
||||
const workflow = withTraceEvents(createWorkflow());
|
||||
|
||||
// Query handler
|
||||
workflow.handle([queryEvent], (event) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
|
||||
// Create a specific fetch event for this query
|
||||
const fetchInstance = fetchEvent.with(event.data);
|
||||
sendEvent(fetchInstance);
|
||||
|
||||
// Create a substream to only track events related to this fetch
|
||||
const substream = workflow.substream(fetchInstance, stream);
|
||||
|
||||
// Listen for results in the substream
|
||||
(async () => {
|
||||
for await (const event of substream) {
|
||||
console.log(`Event in substream: ${event.type}`);
|
||||
}
|
||||
})();
|
||||
|
||||
return resultEvent.with(`Querying: ${event.data}`);
|
||||
});
|
||||
|
||||
// Fetch handler
|
||||
workflow.handle([fetchEvent], (event) => {
|
||||
console.log(`Fetching data for: ${event.data}`);
|
||||
// Actual fetch logic would go here
|
||||
return resultEvent.with(`Results for: ${event.data}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Advanced Validation and Type Safety
|
||||
|
||||
The `withValidation` middleware ensures your workflow connections are both type-safe and runtime-safe:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, withValidation } from "llamaindex";
|
||||
|
||||
// Define events with explicit types
|
||||
const inputEvent = workflowEvent<string, "input">();
|
||||
const validateEvent = workflowEvent<string, "validate">();
|
||||
const processEvent = workflowEvent<string, "process">();
|
||||
const resultEvent = workflowEvent<string, "result">();
|
||||
const errorEvent = workflowEvent<Error, "error">({
|
||||
debugLabel: "errorEvent" // Add debug labels for better error messages
|
||||
});
|
||||
|
||||
// Define the allowed event flow paths
|
||||
const workflow = withValidation(
|
||||
createWorkflow(),
|
||||
[
|
||||
[[inputEvent], [validateEvent, errorEvent]], // inputEvent can lead to validateEvent or errorEvent
|
||||
[[validateEvent], [processEvent, errorEvent]], // validateEvent can lead to processEvent or errorEvent
|
||||
[[processEvent], [resultEvent, errorEvent]], // processEvent can lead to resultEvent or errorEvent
|
||||
[[errorEvent], [resultEvent]] // errorEvent can lead to resultEvent
|
||||
]
|
||||
);
|
||||
|
||||
// Now use strictHandle to get compile-time validation
|
||||
workflow.strictHandle([inputEvent], (sendEvent, event) => {
|
||||
try {
|
||||
if (!event.data || event.data.trim().length === 0) {
|
||||
throw new Error("Empty input");
|
||||
}
|
||||
// This is allowed by our validation rules
|
||||
sendEvent(validateEvent.with(event.data.trim()));
|
||||
|
||||
// This would cause a compile-time error:
|
||||
// sendEvent(resultEvent.with("Result")); // ❌ Not allowed by validation rules
|
||||
} catch (err) {
|
||||
// This is allowed by our validation rules
|
||||
sendEvent(errorEvent.with(err instanceof Error ? err : new Error(String(err))));
|
||||
}
|
||||
});
|
||||
|
||||
// The rest of the workflow with strict validation
|
||||
workflow.strictHandle([validateEvent], (sendEvent, event) => {
|
||||
// Validation logic here
|
||||
sendEvent(processEvent.with(event.data));
|
||||
});
|
||||
|
||||
workflow.strictHandle([processEvent], (sendEvent, event) => {
|
||||
// Processing logic here
|
||||
sendEvent(resultEvent.with(`Processed: ${event.data}`));
|
||||
});
|
||||
|
||||
workflow.strictHandle([errorEvent], (sendEvent, event) => {
|
||||
// Error handling logic here
|
||||
sendEvent(resultEvent.with(`Error handled: ${event.data.message}`));
|
||||
});
|
||||
```
|
||||
|
||||
## Creating Custom Middleware
|
||||
|
||||
You can create your own middleware to extend the workflow capabilities:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// Create a logging middleware
|
||||
function withLogging(workflow) {
|
||||
const originalHandle = workflow.handle;
|
||||
|
||||
workflow.handle = function(eventTypes, handler) {
|
||||
return originalHandle.call(workflow, eventTypes, async function(...args) {
|
||||
const eventType = eventTypes.map(e => e.name || 'AnonymousEvent').join(',');
|
||||
console.log(`[${new Date().toISOString()}] Handling ${eventType}`);
|
||||
|
||||
try {
|
||||
const result = await handler(...args);
|
||||
console.log(`[${new Date().toISOString()}] Completed ${eventType}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(`[${new Date().toISOString()}] Error in ${eventType}:`, error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
// Create a retry middleware
|
||||
function withRetry(maxRetries = 3, workflow) {
|
||||
const originalHandle = workflow.handle;
|
||||
|
||||
workflow.handle = function(eventTypes, handler) {
|
||||
return originalHandle.call(workflow, eventTypes, async function(...args) {
|
||||
let lastError;
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return await handler(...args);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
console.warn(`Attempt ${attempt}/${maxRetries} failed:`, error);
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
// Exponential backoff
|
||||
await new Promise(resolve =>
|
||||
setTimeout(resolve, Math.pow(2, attempt - 1) * 100)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
});
|
||||
};
|
||||
|
||||
return workflow;
|
||||
}
|
||||
|
||||
// Use the custom middleware
|
||||
const workflow = withRetry(3, withLogging(createWorkflow()));
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
// Add handlers
|
||||
workflow.handle([startEvent], (event) => {
|
||||
// This handler might fail but will be retried
|
||||
if (Math.random() < 0.7) {
|
||||
throw new Error("Random failure");
|
||||
}
|
||||
return resultEvent.with(`Processed: ${event.data}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Integrating with External Systems
|
||||
|
||||
You can extend your workflows to integrate with external systems:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const fetchEvent = workflowEvent<string>();
|
||||
const successEvent = workflowEvent<any>();
|
||||
const failureEvent = workflowEvent<Error>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Handle external API calls with proper error handling
|
||||
workflow.handle([fetchEvent], async (event) => {
|
||||
const { signal } = getContext();
|
||||
|
||||
try {
|
||||
// Use AbortSignal for cancellation support
|
||||
const response = await fetch(event.data, { signal });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return successEvent.with(data);
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
return failureEvent.with(new Error('Request was aborted'));
|
||||
}
|
||||
return failureEvent.with(error instanceof Error ? error : new Error(String(error)));
|
||||
}
|
||||
});
|
||||
|
||||
// Database integration example
|
||||
const dbQueryEvent = workflowEvent<{ collection: string; query: any }>();
|
||||
const dbResultEvent = workflowEvent<any[]>();
|
||||
|
||||
workflow.handle([dbQueryEvent], async (event) => {
|
||||
// Connect to database (pseudo-code)
|
||||
const db = await connectToDatabase();
|
||||
|
||||
try {
|
||||
const results = await db.collection(event.data.collection)
|
||||
.find(event.data.query)
|
||||
.toArray();
|
||||
|
||||
return dbResultEvent.with(results);
|
||||
} catch (error) {
|
||||
return failureEvent.with(error);
|
||||
} finally {
|
||||
await db.close();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Handling Complex Asynchronous Patterns
|
||||
|
||||
LlamaIndex workflows excel at managing complex asynchronous patterns:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, until, collect } from "llamaindex";
|
||||
|
||||
// Events for an orchestration workflow
|
||||
const orchestrateEvent = workflowEvent<string[]>();
|
||||
const taskEvent = workflowEvent<string>();
|
||||
const progressEvent = workflowEvent<{ task: string; progress: number }>();
|
||||
const taskCompleteEvent = workflowEvent<string>();
|
||||
const aggregateEvent = workflowEvent<any>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Orchestrator: distribute tasks and collect results
|
||||
workflow.handle([orchestrateEvent], async (event) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
const tasks = event.data;
|
||||
|
||||
// Start all tasks
|
||||
tasks.forEach(task => sendEvent(taskEvent.with(task)));
|
||||
|
||||
// Track progress
|
||||
let completed = 0;
|
||||
const results = {};
|
||||
|
||||
// Process task completion and progress events
|
||||
for await (const event of until(stream, () => completed === tasks.length)) {
|
||||
if (progressEvent.include(event)) {
|
||||
console.log(`Task ${event.data.task}: ${event.data.progress}%`);
|
||||
} else if (taskCompleteEvent.include(event)) {
|
||||
completed++;
|
||||
results[event.data] = `Completed ${event.data}`;
|
||||
console.log(`Completed ${completed}/${tasks.length} tasks`);
|
||||
}
|
||||
}
|
||||
|
||||
return aggregateEvent.with(results);
|
||||
});
|
||||
|
||||
// Task processor
|
||||
workflow.handle([taskEvent], async (event) => {
|
||||
const { sendEvent } = getContext();
|
||||
const task = event.data;
|
||||
|
||||
// Simulate task processing with progress updates
|
||||
for (let progress = 0; progress <= 100; progress += 20) {
|
||||
sendEvent(progressEvent.with({ task, progress }));
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
return taskCompleteEvent.with(task);
|
||||
});
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've explored advanced event handling with workflows, you're ready to build sophisticated applications:
|
||||
|
||||
- [Integrating Workflows with other LlamaIndex Features](./llamaindex-integration.mdx)
|
||||
@@ -1,263 +0,0 @@
|
||||
---
|
||||
title: Basic Workflow Patterns
|
||||
description: Learn common patterns and techniques for building effective workflows
|
||||
---
|
||||
|
||||
This guide explores common patterns you can use to build more complex workflows with workflows.
|
||||
|
||||
## Fan-out (Parallelism)
|
||||
|
||||
One of the most powerful features of workflows is the ability to run tasks in parallel:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, until, collect } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<string>();
|
||||
const processItemEvent = workflowEvent<number>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
const completeEvent = workflowEvent<string[]>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Process start event: fan out to multiple processItemEvent events
|
||||
workflow.handle([startEvent], (start) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
|
||||
// Emit multiple events to be processed in parallel
|
||||
for (let i = 0; i < 10; i++) {
|
||||
sendEvent(processItemEvent.with(i));
|
||||
}
|
||||
|
||||
// Collect all resultEvents and emit a final completeEvent
|
||||
let condition = false;
|
||||
const results = collect(
|
||||
until(stream, () => condition)
|
||||
.filter((ev) => resultEvent.includes(ev))
|
||||
);
|
||||
|
||||
return completeEvent.with(results.map(event => event.data));
|
||||
});
|
||||
|
||||
// Process each item
|
||||
workflow.handle([processItemEvent], (event) => {
|
||||
// Process the item
|
||||
const processedValue = `Processed: ${event.data}`;
|
||||
|
||||
// If this is the last item, set the condition to stop collecting
|
||||
if (event.data === 9) {
|
||||
condition = true;
|
||||
}
|
||||
|
||||
return resultEvent.with(processedValue);
|
||||
});
|
||||
```
|
||||
|
||||
This pattern allows you to:
|
||||
1. Emit multiple events to be processed in parallel
|
||||
2. Collect results as they come in
|
||||
3. Complete once all parallel tasks are finished
|
||||
|
||||
## Conditional Branching
|
||||
|
||||
You can implement conditional logic in your workflows:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
const inputEvent = workflowEvent<number>();
|
||||
const evenNumberEvent = workflowEvent<string>();
|
||||
const oddNumberEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Branch based on whether the number is even or odd
|
||||
workflow.handle([inputEvent], (event) => {
|
||||
if (event.data % 2 === 0) {
|
||||
return evenNumberEvent.with(`${event.data} is even`);
|
||||
} else {
|
||||
return oddNumberEvent.with(`${event.data} is odd`);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle even numbers
|
||||
workflow.handle([evenNumberEvent], (event) => {
|
||||
return resultEvent.with(`Even result: ${event.data}`);
|
||||
});
|
||||
|
||||
// Handle odd numbers
|
||||
workflow.handle([oddNumberEvent], (event) => {
|
||||
return resultEvent.with(`Odd result: ${event.data}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Using Middleware
|
||||
|
||||
LlamaIndex workflows provide middleware that can enhance your workflows:
|
||||
|
||||
### `withStore` Middleware
|
||||
|
||||
The `withStore` middleware adds a persistent store to your workflow context:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, withStore } from "llamaindex";
|
||||
|
||||
const startEvent = workflowEvent<void>();
|
||||
const incrementEvent = workflowEvent<number>();
|
||||
const resultEvent = workflowEvent<number>();
|
||||
|
||||
// Create a workflow with store middleware
|
||||
const workflow = withStore(
|
||||
() => ({
|
||||
count: 0,
|
||||
history: [] as number[],
|
||||
}),
|
||||
createWorkflow()
|
||||
);
|
||||
|
||||
// Increment the counter
|
||||
workflow.handle([startEvent], () => {
|
||||
const store = workflow.getStore();
|
||||
store.count += 1;
|
||||
store.history.push(store.count);
|
||||
return incrementEvent.with(store.count);
|
||||
});
|
||||
|
||||
// Return the current count
|
||||
workflow.handle([incrementEvent], (event) => {
|
||||
const store = workflow.getStore();
|
||||
return resultEvent.with(store.count);
|
||||
});
|
||||
```
|
||||
|
||||
### `withValidation` Middleware
|
||||
|
||||
The `withValidation` middleware adds compile-time and runtime validation to your workflows:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, withValidation } from "llamaindex";
|
||||
|
||||
const startEvent = workflowEvent<string, "start">();
|
||||
const processEvent = workflowEvent<number, "process">();
|
||||
const resultEvent = workflowEvent<string, "result">();
|
||||
const disallowedEvent = workflowEvent<void, "disallowed">();
|
||||
|
||||
// Create a workflow with validation middleware
|
||||
// Define allowed event paths
|
||||
const workflow = withValidation(
|
||||
createWorkflow(),
|
||||
[
|
||||
[[startEvent], [processEvent]], // startEvent can only lead to processEvent
|
||||
[[processEvent], [resultEvent]], // processEvent can only lead to resultEvent
|
||||
]
|
||||
);
|
||||
|
||||
// This will pass validation
|
||||
workflow.strictHandle([startEvent], (sendEvent, start) => {
|
||||
sendEvent(processEvent.with(123)); // ✅ This is allowed
|
||||
});
|
||||
|
||||
// This would fail at compile time and runtime
|
||||
workflow.strictHandle([startEvent], (sendEvent, start) => {
|
||||
// sendEvent(disallowedEvent.with()); // ❌ This would cause an error
|
||||
// sendEvent(resultEvent.with("result")); // ❌ This would also cause an error
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
LlamaIndex workflows provide built-in mechanisms for handling errors:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
const startEvent = workflowEvent<string>();
|
||||
const processEvent = workflowEvent<number>();
|
||||
const errorEvent = workflowEvent<Error>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([startEvent], (start) => {
|
||||
try {
|
||||
const num = Number.parseInt(start.data, 10);
|
||||
if (isNaN(num)) {
|
||||
throw new Error("Invalid number");
|
||||
}
|
||||
return processEvent.with(num);
|
||||
} catch (err) {
|
||||
return errorEvent.with(err instanceof Error ? err : new Error(String(err)));
|
||||
}
|
||||
});
|
||||
|
||||
workflow.handle([processEvent], (event) => {
|
||||
return resultEvent.with(`Result: ${event.data * 2}`);
|
||||
});
|
||||
|
||||
workflow.handle([errorEvent], (event) => {
|
||||
return resultEvent.with(`Error: ${event.data.message}`);
|
||||
});
|
||||
```
|
||||
|
||||
You can also use the signal in `getContext()` to handle errors:
|
||||
|
||||
```ts
|
||||
workflow.handle([processEvent], () => {
|
||||
const { signal } = getContext();
|
||||
|
||||
signal.onabort = () => {
|
||||
console.error("Process aborted:", signal.reason);
|
||||
// Clean up resources
|
||||
};
|
||||
|
||||
// Your processing logic here
|
||||
});
|
||||
```
|
||||
|
||||
## Connecting with Server Endpoints
|
||||
|
||||
Workflow can be used as middleware in server frameworks like Express, Hono, or Fastify:
|
||||
|
||||
```ts
|
||||
import { Hono } from "hono";
|
||||
import { serve } from "@hono/node-server";
|
||||
import { createWorkflow, workflowEvent, createHonoHandler } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const queryEvent = workflowEvent<string>();
|
||||
const responseEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([queryEvent], (event) => {
|
||||
const response = `Processed: ${event.data}`;
|
||||
return responseEvent.with(response);
|
||||
});
|
||||
|
||||
// Create Hono app
|
||||
const app = new Hono();
|
||||
|
||||
// Set up workflow endpoint
|
||||
app.post(
|
||||
"/workflow",
|
||||
createHonoHandler(
|
||||
workflow,
|
||||
async (ctx) => queryEvent.with(await ctx.req.text()),
|
||||
responseEvent
|
||||
)
|
||||
);
|
||||
|
||||
// Start server
|
||||
serve(app, ({ port }) => {
|
||||
console.log(`Server started at http://localhost:${port}`);
|
||||
});
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've learned about basic workflow patterns, explore more advanced topics:
|
||||
- [Streaming with Workflows](./streaming.mdx)
|
||||
- [Advanced Event Handling](./advanced-events.mdx)
|
||||
+2
-12
@@ -1,18 +1,8 @@
|
||||
---
|
||||
title: Inputs / Outputs (Outdated)
|
||||
description: This page has been replaced with newer documentation
|
||||
title: Inputs / Outputs
|
||||
description: Learn how to use different inputs and outputs in your workflows.
|
||||
---
|
||||
|
||||
# ⚠️ Outdated Documentation
|
||||
|
||||
This documentation is for an older version of the workflow API. Please refer to the new llama-flow documentation:
|
||||
|
||||
- [Getting Started with llama-flow](./index.mdx)
|
||||
- [Basic Workflow Patterns](./basic-workflow.mdx)
|
||||
- [Advanced Event Handling](./advanced-events.mdx)
|
||||
|
||||
The new API provides a more lightweight and flexible approach to building workflows.
|
||||
|
||||
Inputs and outputs are the way to communicate between steps in a workflow. In the previous example,
|
||||
we used `StartEvent` and `StopEvent` to communicate between steps. However, you can use any type of event to communicate between steps.
|
||||
|
||||
|
||||
@@ -1,111 +1,196 @@
|
||||
---
|
||||
title: Getting Started with Workflows
|
||||
description: Learn how to use LlamaIndex's lightweight workflow engine for TypeScript
|
||||
title: Basic Usage
|
||||
description: Learn how to use the LlamaIndex workflow.
|
||||
---
|
||||
|
||||
Workflows are a simple and lightweight engine for TypeScript. Built with ❤️ by LlamaIndex.
|
||||
A `Workflow` in LlamaIndex.TS is an event-driven abstraction used to chain together several events.
|
||||
Workflows are made up of steps, with each step responsible for handling certain event types and emitting new events.
|
||||
|
||||
- Minimal core API (\<\=2kb)
|
||||
- 100% Type safe
|
||||
- Event-driven, stream oriented programming
|
||||
- Support for multiple JS runtimes/frameworks
|
||||
Workflows are designed for any cases that benefit from event-driven programming, not only for LLM and AI tasks.
|
||||
|
||||
## Installation
|
||||
|
||||
It's directly included with the `llamaindex` package:
|
||||
|
||||
```shell
|
||||
npm i llamaindex
|
||||
```package-install
|
||||
npm i @llamaindex/workflow
|
||||
```
|
||||
|
||||
But can also be installed separately:
|
||||
## Start from scratch
|
||||
|
||||
Let's start from a Hello World workflow.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow } from '@llamaindex/workflow';
|
||||
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
}
|
||||
// ---cut---
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
// ^?
|
||||
|
||||
```shell
|
||||
npm i @llama-flow/core
|
||||
|
||||
# or with yarn
|
||||
yarn add @llama-flow/core
|
||||
|
||||
# or with pnpm
|
||||
pnpm add @llama-flow/core
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
First, we define a workflow with 3 generic types: `ContextData`, `Input`, and `Output`.
|
||||
|
||||
- **Events**: Data carriers that flow through the workflow
|
||||
- **Handlers**: Functions that process events and emit new events
|
||||
- **Workflow**: Connects events and handlers together
|
||||
- **Context**: Runtime environment for a workflow execution
|
||||
In general, `ContextData` is used to store the shared data between steps, `Input` is the type of the input event, and `Output` is the type of the output event.
|
||||
|
||||
## Basic Usage
|
||||
In you code logic, you should **share state between steps via `ContextData`**.
|
||||
|
||||
Let's build a simple workflow that processes a text input:
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
|
||||
### 1. Define events
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
}
|
||||
|
||||
First, we need to define the events that will flow through our workflow:
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
```ts
|
||||
import { workflowEvent } from "llamaindex";
|
||||
|
||||
// Define input and output events
|
||||
const startEvent = workflowEvent<string>(); // Takes a string input
|
||||
const convertEvent = workflowEvent<Number>(); // Intermediate event
|
||||
const stopEvent = workflowEvent<1 | -1>(); // Final output event, returns 1 or -1
|
||||
```
|
||||
|
||||
### 2. Create a workflow and connect events
|
||||
|
||||
Next, we'll create our workflow and define how events are processed:
|
||||
|
||||
```ts
|
||||
import { createWorkflow } from "llamaindex";
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Handle the start event: convert the string to a number
|
||||
workflow.handle([startEvent], (start) => {
|
||||
return convertEvent.with(Number.parseInt(start.data, 10));
|
||||
});
|
||||
|
||||
// Handle the convert event: determine if number is positive or negative
|
||||
workflow.handle([convertEvent], (convert) => {
|
||||
return stopEvent.with(convert.data > 0 ? 1 : -1);
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.data.counter++;
|
||||
return new StopEvent(`Hello, ${input}!`);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Run the workflow
|
||||
In the workflow, we add a step that listens to `StartEvent<string>` and emits `StopEvent<string>`.
|
||||
|
||||
Finally, we can execute our workflow:
|
||||
The step is an async function that takes two arguments: `context` and `event`.
|
||||
|
||||
```ts
|
||||
// Create a workflow context and send the initial event
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with("42"));
|
||||
### `context` type
|
||||
|
||||
// Process the stream to get the result
|
||||
import { pipeline } from "node:stream/promises";
|
||||
<AutoTypeTable path="./src/deps/type.ts" name="HandlerContext" />
|
||||
|
||||
const result = await pipeline(stream, async function (source) {
|
||||
for await (const event of source) {
|
||||
if (stopEvent.include(event)) {
|
||||
return `Result: ${event.data === 1 ? 'positive' : 'negative'}`;
|
||||
}
|
||||
}
|
||||
There are two more properties in `HandlerContext`:
|
||||
|
||||
- `sendEvent`: invoke another event in the workflow, other than `StartEvent`, `StopEvent`, or the current event. (Or there will have circular reference)
|
||||
- `requireEvent`: wait for a specific event to be emitted.
|
||||
|
||||
You can use `sendEvent` and `requireEvent` to build complex workflows.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
}
|
||||
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
|
||||
// ---cut---
|
||||
class AnalysisStartEvent extends WorkflowEvent<string> {}
|
||||
class AnalysisStopEvent extends WorkflowEvent<boolean> {}
|
||||
workflow.addStep({
|
||||
inputs: [AnalysisStartEvent],
|
||||
outputs: [AnalysisStopEvent]
|
||||
}, async (...args) => {
|
||||
// do some analysis
|
||||
return new AnalysisStopEvent(true);
|
||||
})
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.sendEvent(new AnalysisStartEvent('start'));
|
||||
context.data.counter++;
|
||||
const { data } = await context.requireEvent(AnalysisStopEvent);
|
||||
return new StopEvent(`Hello, ${input}! Analysis result: ${data ? 'success' : 'fail'}`);
|
||||
});
|
||||
```
|
||||
|
||||
For example, you can compile `requireEvent` with `waitUntil` in [Vercel Functions](https://vercel.com/docs/functions/functions-api-reference#waituntil) or [Cloudflare Worker](https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil)
|
||||
|
||||
```ts twoslash
|
||||
import { waitUntil } from '@vercel/functions';
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
}
|
||||
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
|
||||
class AnalysisStartEvent extends WorkflowEvent<string> {}
|
||||
class AnalysisStopEvent extends WorkflowEvent<boolean> {}
|
||||
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.sendEvent(new AnalysisStartEvent('start'));
|
||||
context.data.counter++;
|
||||
waitUntil(context.requireEvent(AnalysisStopEvent));
|
||||
// note that `waitUntil` is not a promise, it will extend the lifetime of the workflow
|
||||
// you can wait for some background tasks to finish
|
||||
return new StopEvent(`Hello, ${input}!`);
|
||||
});
|
||||
```
|
||||
|
||||
## Multiple runs
|
||||
|
||||
You can run the same workflow multiple times with different inputs.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
|
||||
type ContextData = {
|
||||
counter: number;
|
||||
}
|
||||
|
||||
const contextData: ContextData = { counter: 0 };
|
||||
|
||||
const workflow = new Workflow<ContextData, string, string>();
|
||||
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.data.counter++;
|
||||
return new StopEvent(`Hello, ${input}!`);
|
||||
});
|
||||
|
||||
console.log(result); // "Result: positive"
|
||||
// ---cut---
|
||||
{
|
||||
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!
|
||||
}
|
||||
```
|
||||
|
||||
Or using the stream utilities:
|
||||
Context is shared between runs, so the counter will be increased.
|
||||
|
||||
```ts
|
||||
import { collect, until } from "llamaindex";
|
||||
Ideally, it should be serializable to make sure it can be recovered from HTTP requests or other storage.
|
||||
|
||||
// Collect all events until we get a stopEvent
|
||||
const allEvents = await collect(until(stream, stopEvent));
|
||||
const finalEvent = allEvents[allEvents.length - 1];
|
||||
console.log(`Result: ${finalEvent.data === 1 ? 'positive' : 'negative'}`);
|
||||
```
|
||||
### Full example
|
||||
|
||||
Ready to learn more? Check out our [detailed examples](./basic-workflow.mdx) to see llama-flow in action!
|
||||
<iframe
|
||||
className="w-full h-[440px]"
|
||||
aria-label="Workflow example"
|
||||
src="https://stackblitz.com/github/run-llama/LlamaIndexTS/tree/main/examples?file=node/workflow/basic.ts"
|
||||
/>
|
||||
|
||||
## `Workflow` type
|
||||
|
||||
<AutoTypeTable path="./src/deps/type.ts" name="Workflow" />
|
||||
|
||||
## `WorkflowContext` type
|
||||
|
||||
<AutoTypeTable path="./src/deps/type.ts" name="WorkflowContext" />
|
||||
|
||||
@@ -1,288 +0,0 @@
|
||||
---
|
||||
title: Integrating with LlamaIndex
|
||||
description: Build AI applications by combining Workflows with other LlamaIndex features
|
||||
---
|
||||
|
||||
This guide demonstrates how to combine the power of the workflow engine with LlamaIndex's retrieval and reasoning capabilities to build sophisticated AI applications.
|
||||
|
||||
## Basic RAG Workflow
|
||||
|
||||
Let's build a simple Retrieval-Augmented Generation (RAG) workflow:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
import { Document, serviceContextFromDefaults, VectorStoreIndex } from "llamaindex";
|
||||
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
import { Settings } from "@llamaindex/core/global"
|
||||
|
||||
// Define events
|
||||
const queryEvent = workflowEvent<string>();
|
||||
const retrieveEvent = workflowEvent<{ query: string; documents: Document[] }>();
|
||||
const generateEvent = workflowEvent<{ query: string; context: string }>();
|
||||
const responseEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Set default global llm
|
||||
Settings.llm = new OpenAI({
|
||||
model: "gpt-4.1-mini",
|
||||
temperature: 0.2
|
||||
});
|
||||
|
||||
// Set the default global embedModel
|
||||
Settings.embedModel = new OpenAIEmbedding({
|
||||
model: "text-embedding-3-small",
|
||||
});
|
||||
|
||||
// Sample documents
|
||||
const documents = [
|
||||
new Document({
|
||||
text: "LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.",
|
||||
}),
|
||||
new Document({
|
||||
text: "LlamaIndex workflows are a lightweight workflow engine for TypeScript, designed to create event-driven processes.",
|
||||
}),
|
||||
];
|
||||
|
||||
// Create vector store index
|
||||
const index = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
// Handle query: Retrieve relevant documents
|
||||
workflow.handle([queryEvent], (event) => {
|
||||
const query = event.data;
|
||||
console.log(`Processing query: ${query}`);
|
||||
|
||||
// Retrieve relevant documents
|
||||
const retriever = index.asRetriever();
|
||||
const nodes = retriever.retrieve(query);
|
||||
|
||||
return retrieveEvent.with({
|
||||
query,
|
||||
documents: nodes.map(node => node.node),
|
||||
});
|
||||
});
|
||||
|
||||
// Handle retrieval results: Generate response
|
||||
workflow.handle([retrieveEvent], async (event) => {
|
||||
const { query, documents } = event.data;
|
||||
|
||||
// Combine document content as context
|
||||
const context = documents.map(doc => doc.text).join('\n\n');
|
||||
|
||||
return generateEvent.with({ query, context });
|
||||
});
|
||||
|
||||
// Handle generation: Produce final response
|
||||
workflow.handle([generateEvent], async (event) => {
|
||||
const { query, context } = event.data;
|
||||
|
||||
// Create a prompt with the context and query
|
||||
const prompt = `
|
||||
Context information:
|
||||
${context}
|
||||
|
||||
Based on the context information and no other knowledge, answer the following query:
|
||||
${query}
|
||||
`;
|
||||
|
||||
// Generate response with LLM
|
||||
const response = await Settings.llm.complete({ prompt });
|
||||
|
||||
return responseEvent.with(response.text);
|
||||
});
|
||||
|
||||
// Execute the workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(queryEvent.with("What is LlamaIndex?"));
|
||||
|
||||
// Process the stream
|
||||
for await (const event of stream) {
|
||||
if (responseEvent.include(event)) {
|
||||
console.log("Final response:", event.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Building a Chat Application
|
||||
|
||||
Let's create a more complex chat application that maintains conversation history:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, withStore } from "llamaindex";
|
||||
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
import { Document, serviceContextFromDefaults, VectorStoreIndex } from "llamaindex";
|
||||
import { Settings } from "@llamaindex/core/global"
|
||||
|
||||
// Set default global llm
|
||||
Settings.llm = new OpenAI({
|
||||
model: "gpt-4.1-mini",
|
||||
temperature: 0.2
|
||||
});
|
||||
|
||||
// Set the default global embedModel
|
||||
Settings.embedModel = new OpenAIEmbedding({
|
||||
model: "text-embedding-3-small",
|
||||
});
|
||||
|
||||
// Define store type
|
||||
type ChatStore = {
|
||||
history: Array<{ role: string; content: string }>;
|
||||
documents: Document[];
|
||||
index: VectorStoreIndex | null;
|
||||
};
|
||||
|
||||
// Define events
|
||||
const initEvent = workflowEvent<Document[]>();
|
||||
const indexCreatedEvent = workflowEvent<VectorStoreIndex>();
|
||||
const userMessageEvent = workflowEvent<string>();
|
||||
const retrievalEvent = workflowEvent<{ query: string; nodes: any[] }>();
|
||||
const responseEvent = workflowEvent<{ message: { content: string } }>();
|
||||
|
||||
// Create workflow with store
|
||||
const workflow = withStore<ChatStore>(
|
||||
() => ({
|
||||
history: [],
|
||||
documents: [],
|
||||
index: null,
|
||||
}),
|
||||
createWorkflow()
|
||||
);
|
||||
|
||||
// Initialize the chat context
|
||||
workflow.handle([initEvent], async (event) => {
|
||||
const store = workflow.getStore();
|
||||
store.documents = event.data;
|
||||
|
||||
// Create index from documents
|
||||
const index = await VectorStoreIndex.fromDocuments(store.documents);
|
||||
|
||||
store.index = index;
|
||||
return indexCreatedEvent.with(index);
|
||||
});
|
||||
|
||||
// Process user message
|
||||
workflow.handle([userMessageEvent], (event) => {
|
||||
const userMessage = event.data;
|
||||
const store = workflow.getStore();
|
||||
|
||||
// Add user message to history
|
||||
store.history.push({
|
||||
role: "user",
|
||||
content: userMessage,
|
||||
});
|
||||
|
||||
if (!store.index) {
|
||||
throw new Error("Index not initialized yet");
|
||||
}
|
||||
|
||||
// Retrieve relevant context
|
||||
const retriever = store.index.asRetriever();
|
||||
const nodes = retriever.retrieve(userMessage);
|
||||
|
||||
return retrievalEvent.with({
|
||||
query: userMessage,
|
||||
nodes,
|
||||
});
|
||||
});
|
||||
|
||||
// Generate response from retrieval results
|
||||
workflow.handle([retrievalEvent], async (event) => {
|
||||
const { query, nodes } = event.data;
|
||||
const store = workflow.getStore();
|
||||
|
||||
// Context from retrieved nodes
|
||||
const context = nodes.map(node => node.node.text).join('\n\n');
|
||||
|
||||
// Create the system message with context
|
||||
const systemMessage = {
|
||||
role: "system",
|
||||
content: `You are a helpful assistant. Use the following information to answer the user's question:
|
||||
|
||||
${context}
|
||||
|
||||
Only use the information provided above to answer. If you don't know, say so.`,
|
||||
};
|
||||
|
||||
// Create full conversation history for the chat
|
||||
const messages = [
|
||||
systemMessage,
|
||||
...store.history,
|
||||
];
|
||||
|
||||
// Generate response
|
||||
const response = await Settings.llm.chat({
|
||||
messages,
|
||||
});
|
||||
|
||||
// Add assistant response to history
|
||||
store.history.push({
|
||||
role: "assistant",
|
||||
content: response.message.content,
|
||||
});
|
||||
|
||||
return responseEvent.with(response);
|
||||
});
|
||||
|
||||
// Example usage
|
||||
async function runChat() {
|
||||
// Sample documents
|
||||
const documents = [
|
||||
new Document({
|
||||
text: "LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.",
|
||||
}),
|
||||
new Document({
|
||||
text: "LlamaIndex Workflows are a lightweight workflow engine for TypeScript, designed to create event-driven processes.",
|
||||
}),
|
||||
];
|
||||
|
||||
// Initialize the chat
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(initEvent.with(documents));
|
||||
|
||||
// Wait for index creation
|
||||
for await (const event of stream) {
|
||||
if (indexCreatedEvent.include(event)) {
|
||||
console.log("Index created successfully");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Start conversation
|
||||
async function sendUserMessage(message: string) {
|
||||
sendEvent(userMessageEvent.with(message));
|
||||
|
||||
for await (const event of stream) {
|
||||
if (responseEvent.include(event)) {
|
||||
console.log("Assistant:", event.data.message.content);
|
||||
return event.data.message.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await sendUserMessage("What is LlamaIndex?");
|
||||
await sendUserMessage("Can you tell me about LlamaIndex workflows?");
|
||||
await sendUserMessage("How might these two technologies work together?");
|
||||
}
|
||||
|
||||
runChat();
|
||||
```
|
||||
|
||||
## Building an Tool Calling Agent
|
||||
|
||||
[TODO]
|
||||
|
||||
## Conclusion
|
||||
|
||||
By combining the lightweight, event-driven workflow engine with LlamaIndex's powerful document indexing and querying capabilities, you can build sophisticated AI applications with clean, maintainable code.
|
||||
|
||||
The event-driven architecture allows you to:
|
||||
|
||||
1. Break complex processes into manageable steps
|
||||
2. Create reusable components for common AI workflows
|
||||
3. Easily debug and monitor each phase of execution
|
||||
4. Scale your applications by isolating resource-intensive steps
|
||||
5. Build more resilient systems with better error handling
|
||||
|
||||
As you build your own applications, consider how the patterns shown here can be adapted to your specific use cases.
|
||||
@@ -1,12 +1,6 @@
|
||||
{
|
||||
"title": "Workflows",
|
||||
"description": "Event-driven workflow engine for TypeScript",
|
||||
"title": "Workflow",
|
||||
"description": "See how to use @llamaindex/workflow",
|
||||
"defaultOpen": false,
|
||||
"pages": [
|
||||
"index",
|
||||
"basic-workflow",
|
||||
"streaming",
|
||||
"advanced-events",
|
||||
"llamaindex-integration"
|
||||
]
|
||||
"pages": ["index", "different-inputs-outputs", "streaming"]
|
||||
}
|
||||
|
||||
@@ -1,371 +1,198 @@
|
||||
---
|
||||
title: Streaming with Workflows
|
||||
description: Learn how to build streaming workflows
|
||||
title: Streaming
|
||||
description: Learn how to use the LlamaIndex workflow with streaming.
|
||||
---
|
||||
|
||||
LlamaIndex workflows are designed from the ground up to work with streaming data. The streaming capabilities make it perfect for:
|
||||
`Workflow` API by default is designed for streaming data. In this guide, we will show you how to use the `Workflow` API with streaming data.
|
||||
|
||||
- Building real-time applications
|
||||
- Handling large datasets incrementally
|
||||
- Creating responsive UIs that update as data becomes available
|
||||
- Implementing long-running tasks with partial results
|
||||
Each `workflow.run` call returns `WorkflowContext`, which implements `AsyncIterable` interface. You can use it to stream data.
|
||||
|
||||
## Basic Streaming
|
||||
```ts twoslash
|
||||
import { Workflow, WorkflowEvent, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
class ComputeEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
class ComputeResultEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
Every workflow context provides a stream of events:
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
const workflow = new Workflow<ContextData, number, number>();
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<number>],
|
||||
outputs: [StopEvent<number>]
|
||||
}, async (context, startEvent) => {
|
||||
const total = startEvent.data;
|
||||
for (let i = 0; i < total; i++) {
|
||||
context.sendEvent(new ComputeEvent(i));
|
||||
}
|
||||
const computeResults = await Promise.all(Array.from({ length: total }).map(() => context.requireEvent(ComputeResultEvent)));
|
||||
// Workflow API allows you to start events in parallel and wait for all of them to finish
|
||||
context.data.sum = computeResults.reduce((acc, curr) => acc + curr.data, 0);
|
||||
return new StopEvent(context.data.sum);
|
||||
});
|
||||
```
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<string>();
|
||||
const intermediateEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
We define a parallel computation workflow that computes the sum of numbers from 0 to `total`.
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
The workflow sends `ComputeEvent` events for each number and waits for `ComputeResultEvent` events. After receiving all `ComputeResultEvent` events, the workflow returns the sum as a `StopEvent`.
|
||||
|
||||
workflow.handle([startEvent], (event) => {
|
||||
const { sendEvent } = getContext();
|
||||
|
||||
// Emit multiple intermediate events
|
||||
for (let i = 0; i < 5; i++) {
|
||||
sendEvent(intermediateEvent.with(`Progress: ${i * 20}%`));
|
||||
}
|
||||
|
||||
return resultEvent.with("Completed");
|
||||
What if we want cutoff if the sum exceeds a certain value?
|
||||
|
||||
## Streaming
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, WorkflowEvent, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
import { StopCircle } from 'lucide-react';
|
||||
class ComputeEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
class ComputeResultEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
const workflow = new Workflow<ContextData, number, number>();
|
||||
// ---cut---
|
||||
const context = workflow.run(1000, {
|
||||
sum: 0
|
||||
});
|
||||
|
||||
// Run the workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with("Start processing"));
|
||||
|
||||
// Process events as they arrive
|
||||
for await (const event of stream) {
|
||||
if (intermediateEvent.include(event)) {
|
||||
console.log(event.data); // Show progress updates
|
||||
} else if (resultEvent.include(event)) {
|
||||
console.log("Final result:", event.data);
|
||||
break; // Exit the loop when done
|
||||
}
|
||||
for await (const event of context) {
|
||||
if (event instanceof ComputeEvent) {
|
||||
if (context.data.sum > 100) {
|
||||
throw new Error('Sum exceeds 100');
|
||||
}
|
||||
}
|
||||
if (event instanceof StopEvent) {
|
||||
console.log('result', event.data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Stream Utilities
|
||||
You can define more custom logic using `AsyncIterable` interface.
|
||||
|
||||
Workflows provide utility functions to make working with streams easier:
|
||||
For example. I just want to stop the workflow if I get a `ComputeResultEvent`
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent, until, collect } from "llamaindex";
|
||||
|
||||
const startEvent = workflowEvent<void>();
|
||||
const progressEvent = workflowEvent<number>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([startEvent], () => {
|
||||
const { sendEvent } = getContext();
|
||||
|
||||
// Emit progress events
|
||||
for (let i = 0; i < 100; i += 10) {
|
||||
sendEvent(progressEvent.with(i));
|
||||
}
|
||||
|
||||
return resultEvent.with("Complete");
|
||||
});
|
||||
|
||||
// Run the workflow and collect events until a condition is met
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with());
|
||||
|
||||
// Collect all events until resultEvent is encountered
|
||||
const events = await collect(until(stream, (event) => resultEvent.include(event)));
|
||||
|
||||
// Filter only progress events
|
||||
const progressEvents = events.filter(event => progressEvent.include(event));
|
||||
console.log(`Received ${progressEvents.length} progress updates`);
|
||||
```
|
||||
|
||||
## Conditional Stream Processing
|
||||
|
||||
You can conditionally process events and even stop the stream early:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
const startEvent = workflowEvent<number>();
|
||||
const dataEvent = workflowEvent<number>();
|
||||
const thresholdEvent = workflowEvent<void>();
|
||||
const resultEvent = workflowEvent<number[]>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([startEvent], (event) => {
|
||||
const { sendEvent } = getContext();
|
||||
const max = event.data;
|
||||
|
||||
for (let i = 0; i < max; i++) {
|
||||
sendEvent(dataEvent.with(i));
|
||||
if (i >= 10) {
|
||||
// Signal that we've hit a threshold
|
||||
sendEvent(thresholdEvent.with());
|
||||
}
|
||||
}
|
||||
|
||||
return resultEvent.with(Array.from({ length: max }, (_, i) => i));
|
||||
});
|
||||
|
||||
// Run the workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with(100)); // Generate 100 numbers
|
||||
|
||||
const results = [];
|
||||
let hitThreshold = false;
|
||||
|
||||
// Process the stream
|
||||
for await (const event of stream) {
|
||||
if (dataEvent.include(event)) {
|
||||
results.push(event.data);
|
||||
} else if (thresholdEvent.include(event)) {
|
||||
hitThreshold = true;
|
||||
break; // Stop processing early
|
||||
}
|
||||
```ts twoslash
|
||||
import { Workflow, WorkflowEvent, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
import { StopCircle } from 'lucide-react';
|
||||
class ComputeEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
class ComputeResultEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Collected ${results.length} items before ${hitThreshold ? 'hitting threshold' : 'completion'}`);
|
||||
```
|
||||
|
||||
## Integration with UI Frameworks
|
||||
|
||||
Workflow streams can be easily integrated with UI frameworks like React to create responsive interfaces:
|
||||
|
||||
```tsx
|
||||
// In a React component
|
||||
import { useEffect, useState } from 'react';
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
function StreamingComponent() {
|
||||
const [updates, setUpdates] = useState([]);
|
||||
const [isComplete, setIsComplete] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Set up workflow
|
||||
const startEvent = workflowEvent<void>();
|
||||
const updateEvent = workflowEvent<string>();
|
||||
const completeEvent = workflowEvent<void>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
workflow.handle([startEvent], () => {
|
||||
const { sendEvent } = getContext();
|
||||
|
||||
// Simulate async updates
|
||||
const intervals = [
|
||||
setTimeout(() => sendEvent(updateEvent.with("First update")), 500),
|
||||
setTimeout(() => sendEvent(updateEvent.with("Second update")), 1000),
|
||||
setTimeout(() => sendEvent(updateEvent.with("Final update")), 1500),
|
||||
setTimeout(() => sendEvent(completeEvent.with()), 2000)
|
||||
];
|
||||
|
||||
// Cleanup function
|
||||
getContext().signal.onabort = () => {
|
||||
intervals.forEach(clearTimeout);
|
||||
};
|
||||
});
|
||||
|
||||
// Run the workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with());
|
||||
|
||||
// Process events
|
||||
const processEvents = async () => {
|
||||
for await (const event of stream) {
|
||||
if (updateEvent.include(event)) {
|
||||
setUpdates(prev => [...prev, event.data]);
|
||||
} else if (completeEvent.include(event)) {
|
||||
setIsComplete(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
processEvents();
|
||||
|
||||
// Cleanup
|
||||
return () => {
|
||||
// The workflow will be aborted when the component unmounts
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Streaming Updates</h2>
|
||||
<ul>
|
||||
{updates.map((update, i) => (
|
||||
<li key={i}>{update}</li>
|
||||
))}
|
||||
</ul>
|
||||
{isComplete && <div>Process complete!</div>}
|
||||
</div>
|
||||
);
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
const workflow = new Workflow<ContextData, number, number>();
|
||||
// ---cut---
|
||||
async function compute() {
|
||||
const context = workflow.run(1000, {
|
||||
sum: 0
|
||||
});
|
||||
for await (const event of context) {
|
||||
if (event instanceof ComputeResultEvent) {
|
||||
return event.data;
|
||||
}
|
||||
}
|
||||
throw new Error('UNREACHABLE');
|
||||
}
|
||||
|
||||
const result = await compute();
|
||||
```
|
||||
|
||||
## Server-Sent Events (SSE)
|
||||
### Streaming with UI
|
||||
|
||||
Workflows are also suitable for implementing Server-Sent Events:
|
||||
You can use the `Workflow` API with UI libraries like React.
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
import express from 'express';
|
||||
```tsx twoslash
|
||||
// @filename: utils.ts
|
||||
export async function runWithoutBlocking(fn: () => Promise<void>) {
|
||||
fn();
|
||||
}
|
||||
// @filename: action.ts
|
||||
// ---cut---
|
||||
'use server';
|
||||
// "use server" is required to enable server side feature in React
|
||||
import { createStreamableUI } from 'ai/rsc';
|
||||
import { runWithoutBlocking } from './utils';
|
||||
// ---cut-start---
|
||||
import { Workflow, WorkflowEvent, StartEvent, StopEvent } from '@llamaindex/workflow';
|
||||
class ComputeEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
class ComputeResultEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<void>();
|
||||
const dataEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
workflow.handle([startEvent], () => {
|
||||
const { sendEvent } = getContext();
|
||||
|
||||
// Send periodic updates
|
||||
const intervals = [
|
||||
setInterval(() => {
|
||||
sendEvent(dataEvent.with(`Update: ${new Date().toISOString()}`));
|
||||
}, 1000)
|
||||
];
|
||||
|
||||
// Cleanup
|
||||
getContext().signal.onabort = () => {
|
||||
intervals.forEach(clearInterval);
|
||||
};
|
||||
});
|
||||
|
||||
// Set up Express server
|
||||
const app = express();
|
||||
|
||||
app.get('/events', (req, res) => {
|
||||
// Set headers for SSE
|
||||
res.setHeader('Content-Type', 'text/event-stream');
|
||||
res.setHeader('Cache-Control', 'no-cache');
|
||||
res.setHeader('Connection', 'keep-alive');
|
||||
|
||||
// Run workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with());
|
||||
|
||||
// Handle client disconnect
|
||||
req.on('close', () => {
|
||||
// This will trigger the abort signal in the workflow
|
||||
});
|
||||
|
||||
// Process and send events
|
||||
(async () => {
|
||||
for await (const event of stream) {
|
||||
if (dataEvent.include(event)) {
|
||||
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
|
||||
}
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
app.listen(3000, () => {
|
||||
console.log('SSE server running on port 3000');
|
||||
});
|
||||
```
|
||||
|
||||
## Advanced Techniques
|
||||
|
||||
### Flow Control
|
||||
|
||||
You can implement flow control with backpressure in your streaming workflows:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// This example shows how to process items with controlled concurrency
|
||||
const processItems = async (items, maxConcurrency = 3) => {
|
||||
const startEvent = workflowEvent<string[]>();
|
||||
const processItemEvent = workflowEvent<string>();
|
||||
const itemProcessedEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string[]>();
|
||||
|
||||
const workflow = createWorkflow();
|
||||
|
||||
// Handler to process individual items
|
||||
workflow.handle([processItemEvent], async (event) => {
|
||||
// Simulate processing time
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
return itemProcessedEvent.with(`Processed: ${event.data}`);
|
||||
});
|
||||
|
||||
// Main workflow handler
|
||||
workflow.handle([startEvent], async (event) => {
|
||||
const { sendEvent, stream } = getContext();
|
||||
const results = [];
|
||||
|
||||
// Process with controlled concurrency
|
||||
const items = event.data;
|
||||
let inProgress = 0;
|
||||
let itemIndex = 0;
|
||||
|
||||
// Start initial batch of items
|
||||
while (inProgress < maxConcurrency && itemIndex < items.length) {
|
||||
sendEvent(processItemEvent.with(items[itemIndex++]));
|
||||
inProgress++;
|
||||
}
|
||||
|
||||
// Process items and collect results
|
||||
for await (const event of stream) {
|
||||
if (itemProcessedEvent.include(event)) {
|
||||
results.push(event.data);
|
||||
inProgress--;
|
||||
|
||||
// Add next item if available
|
||||
if (itemIndex < items.length) {
|
||||
sendEvent(processItemEvent.with(items[itemIndex++]));
|
||||
inProgress++;
|
||||
} else if (inProgress === 0) {
|
||||
// All done
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resultEvent.with(results);
|
||||
});
|
||||
|
||||
// Run the workflow
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with(items));
|
||||
|
||||
// Wait for final result
|
||||
for await (const event of stream) {
|
||||
if (resultEvent.include(event)) {
|
||||
return event.data;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
// Usage
|
||||
const results = await processItems(
|
||||
Array.from({ length: 20 }, (_, i) => `Item ${i}`)
|
||||
const workflow = new Workflow<ContextData, number, number>();
|
||||
const min = 100;
|
||||
const max = 1000;
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [ComputeEvent],
|
||||
outputs: [ComputeResultEvent]
|
||||
},
|
||||
async (context, event) => {
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, Math.floor(Math.random() * (max - min + 1) + min))
|
||||
);
|
||||
return new ComputeResultEvent(event.data);
|
||||
}
|
||||
);
|
||||
console.log(results);
|
||||
// ---cut-end---
|
||||
export async function compute() {
|
||||
'use server';
|
||||
const ui = createStreamableUI();
|
||||
const context = workflow.run(100, {
|
||||
sum: 0
|
||||
});
|
||||
runWithoutBlocking(async () => {
|
||||
for await (const event of context) {
|
||||
if (event instanceof ComputeResultEvent) {
|
||||
// Update UI
|
||||
} else if (event instanceof StopEvent) {
|
||||
// Update UI
|
||||
}
|
||||
// ...
|
||||
}
|
||||
});
|
||||
return ui.value;
|
||||
}
|
||||
```
|
||||
|
||||
This pattern allows you to:
|
||||
1. Process large datasets without overwhelming system resources
|
||||
2. Control the level of concurrency
|
||||
3. Process data as it becomes available
|
||||
4. Create efficient data pipelines
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've learned about streaming with workflows, explore more advanced topics:
|
||||
- [Advanced Event Handling](./advanced-events.mdx)
|
||||
- [Integration Workflows with other LlamaIndex features](./llamaindex-integration.mdx)
|
||||
<WorkflowStreamingDemo />
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/cloudflare-worker-agent-test
|
||||
|
||||
## 0.0.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.0.155
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/cloudflare-worker-agent-test",
|
||||
"version": "0.0.155",
|
||||
"version": "0.0.156",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/next-agent-test
|
||||
|
||||
## 0.1.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.1.155
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/next-agent-test",
|
||||
"version": "0.1.155",
|
||||
"version": "0.1.156",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# test-edge-runtime
|
||||
|
||||
## 0.1.155
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.1.154
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/nextjs-edge-runtime-test",
|
||||
"version": "0.1.154",
|
||||
"version": "0.1.155",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/next-node-runtime
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
- @llamaindex/huggingface@0.1.6
|
||||
|
||||
## 0.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/next-node-runtime-test",
|
||||
"version": "0.1.21",
|
||||
"version": "0.1.22",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# vite-import-llamaindex
|
||||
|
||||
## 0.0.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vite-import-llamaindex",
|
||||
"private": true,
|
||||
"version": "0.0.21",
|
||||
"version": "0.0.22",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/waku-query-engine-test
|
||||
|
||||
## 0.0.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.0.155
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/waku-query-engine-test",
|
||||
"version": "0.0.155",
|
||||
"version": "0.0.156",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# examples
|
||||
|
||||
## 0.3.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [96dac4d]
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/google@0.2.4
|
||||
- @llamaindex/openai@0.3.4
|
||||
- llamaindex@0.10.2
|
||||
- @llamaindex/clip@0.0.52
|
||||
- @llamaindex/deepinfra@0.0.52
|
||||
- @llamaindex/deepseek@0.0.12
|
||||
- @llamaindex/fireworks@0.0.12
|
||||
- @llamaindex/groq@0.0.67
|
||||
- @llamaindex/huggingface@0.1.6
|
||||
- @llamaindex/jinaai@0.0.12
|
||||
- @llamaindex/perplexity@0.0.9
|
||||
- @llamaindex/together@0.0.12
|
||||
- @llamaindex/vllm@0.0.38
|
||||
|
||||
## 0.3.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/examples",
|
||||
"version": "0.3.8",
|
||||
"version": "0.3.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
@@ -15,16 +15,16 @@
|
||||
"@llamaindex/astra": "^0.0.16",
|
||||
"@llamaindex/azure": "^0.1.11",
|
||||
"@llamaindex/chroma": "^0.0.16",
|
||||
"@llamaindex/clip": "^0.0.51",
|
||||
"@llamaindex/clip": "^0.0.52",
|
||||
"@llamaindex/cloud": "^4.0.3",
|
||||
"@llamaindex/cohere": "^0.0.16",
|
||||
"@llamaindex/core": "^0.6.2",
|
||||
"@llamaindex/deepinfra": "^0.0.51",
|
||||
"@llamaindex/deepinfra": "^0.0.52",
|
||||
"@llamaindex/env": "^0.1.29",
|
||||
"@llamaindex/firestore": "^1.0.9",
|
||||
"@llamaindex/google": "^0.2.3",
|
||||
"@llamaindex/groq": "^0.0.66",
|
||||
"@llamaindex/huggingface": "^0.1.5",
|
||||
"@llamaindex/google": "^0.2.4",
|
||||
"@llamaindex/groq": "^0.0.67",
|
||||
"@llamaindex/huggingface": "^0.1.6",
|
||||
"@llamaindex/milvus": "^0.1.11",
|
||||
"@llamaindex/mistral": "^0.1.2",
|
||||
"@llamaindex/mixedbread": "^0.0.16",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@llamaindex/elastic-search": "^0.1.2",
|
||||
"@llamaindex/node-parser": "^2.0.2",
|
||||
"@llamaindex/ollama": "^0.1.2",
|
||||
"@llamaindex/openai": "^0.3.3",
|
||||
"@llamaindex/openai": "^0.3.4",
|
||||
"@llamaindex/pinecone": "^0.1.2",
|
||||
"@llamaindex/portkey-ai": "^0.0.44",
|
||||
"@llamaindex/postgres": "^0.0.45",
|
||||
@@ -41,15 +41,15 @@
|
||||
"@llamaindex/replicate": "^0.0.44",
|
||||
"@llamaindex/upstash": "^0.0.16",
|
||||
"@llamaindex/vercel": "^0.1.2",
|
||||
"@llamaindex/vllm": "^0.0.37",
|
||||
"@llamaindex/vllm": "^0.0.38",
|
||||
"@llamaindex/voyage-ai": "^1.0.8",
|
||||
"@llamaindex/weaviate": "^0.0.16",
|
||||
"@llamaindex/workflow": "^1.0.3",
|
||||
"@llamaindex/deepseek": "^0.0.11",
|
||||
"@llamaindex/fireworks": "^0.0.11",
|
||||
"@llamaindex/together": "^0.0.11",
|
||||
"@llamaindex/jinaai": "^0.0.11",
|
||||
"@llamaindex/perplexity": "^0.0.8",
|
||||
"@llamaindex/deepseek": "^0.0.12",
|
||||
"@llamaindex/fireworks": "^0.0.12",
|
||||
"@llamaindex/together": "^0.0.12",
|
||||
"@llamaindex/jinaai": "^0.0.12",
|
||||
"@llamaindex/perplexity": "^0.0.9",
|
||||
"@llamaindex/supabase": "^0.1.1",
|
||||
"@llamaindex/tools": "^0.0.5",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
@@ -60,7 +60,7 @@
|
||||
"commander": "^12.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"js-tiktoken": "^1.0.14",
|
||||
"llamaindex": "^0.10.1",
|
||||
"llamaindex": "^0.10.2",
|
||||
"mongodb": "6.7.0",
|
||||
"postgres": "^3.4.4",
|
||||
"wikipedia": "^2.1.2",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/autotool
|
||||
|
||||
## 7.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 7.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/autotool-01-node-example
|
||||
|
||||
## 0.0.103
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
- @llamaindex/autotool@7.0.2
|
||||
|
||||
## 0.0.102
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
"scripts": {
|
||||
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
|
||||
},
|
||||
"version": "0.0.102"
|
||||
"version": "0.0.103"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
|
||||
"directory": "packages/autotool"
|
||||
},
|
||||
"version": "7.0.1",
|
||||
"version": "7.0.2",
|
||||
"description": "auto transpile your JS function to LLM Agent compatible",
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/experimental
|
||||
|
||||
## 0.0.172
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.0.171
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/experimental",
|
||||
"description": "Experimental package for LlamaIndexTS",
|
||||
"version": "0.0.171",
|
||||
"version": "0.0.172",
|
||||
"type": "module",
|
||||
"types": "dist/type/index.d.ts",
|
||||
"main": "dist/cjs/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# llamaindex
|
||||
|
||||
## 0.10.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.10.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "llamaindex",
|
||||
"version": "0.10.1",
|
||||
"version": "0.10.2",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/clip
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/clip",
|
||||
"description": "Clip Embedding Adapter for LlamaIndex",
|
||||
"version": "0.0.51",
|
||||
"version": "0.0.52",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/index.cjs",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/deepinfra
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/deepinfra",
|
||||
"description": "Deepinfra Adapter for LlamaIndex",
|
||||
"version": "0.0.51",
|
||||
"version": "0.0.52",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/deepseek
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/deepseek",
|
||||
"description": "DeepSeek Adapter for LlamaIndex",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.12",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/fireworks
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/fireworks",
|
||||
"description": "Fireworks Adapter for LlamaIndex",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.12",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/google
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 96dac4d: Add Gemini 2.5 Flash Preview
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/google",
|
||||
"description": "Google Adapter for LlamaIndex",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -62,6 +62,7 @@ export const GEMINI_MODEL_INFO_MAP: Record<GEMINI_MODEL, GeminiModelInfo> = {
|
||||
[GEMINI_MODEL.GEMINI_2_0_FLASH_THINKING_EXP]: { contextWindow: 32768 },
|
||||
[GEMINI_MODEL.GEMINI_2_0_PRO_EXPERIMENTAL]: { contextWindow: 2 * 10 ** 6 },
|
||||
[GEMINI_MODEL.GEMINI_2_5_PRO_PREVIEW]: { contextWindow: 10 ** 6 },
|
||||
[GEMINI_MODEL.GEMINI_2_5_FLASH_PREVIEW]: { contextWindow: 10 ** 6 },
|
||||
};
|
||||
|
||||
export const SUPPORT_TOOL_CALL_MODELS: GEMINI_MODEL[] = [
|
||||
@@ -79,6 +80,7 @@ export const SUPPORT_TOOL_CALL_MODELS: GEMINI_MODEL[] = [
|
||||
GEMINI_MODEL.GEMINI_2_0_FLASH,
|
||||
GEMINI_MODEL.GEMINI_2_0_PRO_EXPERIMENTAL,
|
||||
GEMINI_MODEL.GEMINI_2_5_PRO_PREVIEW,
|
||||
GEMINI_MODEL.GEMINI_2_5_FLASH_PREVIEW,
|
||||
];
|
||||
|
||||
export const DEFAULT_GEMINI_PARAMS = {
|
||||
|
||||
@@ -74,6 +74,7 @@ export enum GEMINI_MODEL {
|
||||
GEMINI_2_0_FLASH_THINKING_EXP = "gemini-2.0-flash-thinking-exp-01-21",
|
||||
GEMINI_2_0_PRO_EXPERIMENTAL = "gemini-2.0-pro-exp-02-05",
|
||||
GEMINI_2_5_PRO_PREVIEW = "gemini-2.5-pro-preview-03-25",
|
||||
GEMINI_2_5_FLASH_PREVIEW = "gemini-2.5-flash-preview-04-17",
|
||||
}
|
||||
|
||||
export interface GeminiModelInfo {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/groq
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/groq",
|
||||
"description": "Groq Adapter for LlamaIndex",
|
||||
"version": "0.0.66",
|
||||
"version": "0.0.67",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/huggingface
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/huggingface",
|
||||
"description": "Huggingface Adapter for LlamaIndex",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/index.cjs",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/jinaai
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/jinaai",
|
||||
"description": "JinaAI Adapter for LlamaIndex",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.12",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/openai
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e5c3f95: Update o4-mini to accept reasoning parameters and exclude temperature
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/openai",
|
||||
"description": "OpenAI Adapter for LlamaIndex",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/perplexity
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/perplexity",
|
||||
"description": "Perplexity Adapter for LlamaIndex",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.9",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/together
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/together",
|
||||
"description": "Together Adapter for LlamaIndex",
|
||||
"version": "0.0.11",
|
||||
"version": "0.0.12",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/vllm
|
||||
|
||||
## 0.0.38
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
|
||||
## 0.0.37
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/vllm",
|
||||
"description": "vLLM Adapter for LlamaIndex",
|
||||
"version": "0.0.37",
|
||||
"version": "0.0.38",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @llamaindex/server
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 7ca9ddf: Add generate ui workflow to @llamaindex/server
|
||||
- 3310eaa: chore: bump chat-ui
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -4,9 +4,10 @@ import { ChatSection as ChatSectionUI } from "@llamaindex/chat-ui";
|
||||
import "@llamaindex/chat-ui/styles/markdown.css";
|
||||
import "@llamaindex/chat-ui/styles/pdf.css";
|
||||
import { useChat } from "ai/react";
|
||||
import { Sparkles, Star } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import Header from "./header";
|
||||
import { RenderingErrors } from "./rendering-errors";
|
||||
import { Button } from "./ui/button";
|
||||
import CustomChatInput from "./ui/chat/chat-input";
|
||||
import CustomChatMessages from "./ui/chat/chat-messages";
|
||||
import { fetchComponentDefinitions } from "./ui/chat/custom/events/loader";
|
||||
@@ -51,20 +52,66 @@ export default function ChatSection() {
|
||||
experimental_throttle: 100,
|
||||
});
|
||||
return (
|
||||
<div className="flex h-[85vh] w-full flex-col gap-2">
|
||||
<Header />
|
||||
<RenderingErrors
|
||||
uniqueErrors={uniqueErrors}
|
||||
clearErrors={() => setErrors([])}
|
||||
/>
|
||||
<ChatSectionUI handler={handler} className="min-h-0 w-full flex-1">
|
||||
<CustomChatMessages
|
||||
componentDefs={componentDefs}
|
||||
appendError={appendError}
|
||||
/>
|
||||
<CustomChatInput />
|
||||
</ChatSectionUI>
|
||||
<>
|
||||
<div className="grid h-screen w-screen grid-cols-4 gap-4 overflow-hidden">
|
||||
<div className="col-span-1">
|
||||
<div className="flex flex-col gap-8 p-2 pl-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="size-4" />
|
||||
<h1 className="font-semibold">{getConfig("APP_TITLE")}</h1>
|
||||
</div>
|
||||
<RenderingErrors
|
||||
uniqueErrors={uniqueErrors}
|
||||
clearErrors={() => setErrors([])}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-2 h-full min-h-0">
|
||||
<ChatSectionUI handler={handler} className="p-0">
|
||||
<CustomChatMessages
|
||||
componentDefs={componentDefs}
|
||||
appendError={appendError}
|
||||
/>
|
||||
<CustomChatInput />
|
||||
</ChatSectionUI>
|
||||
</div>
|
||||
<div className="col-span-1">
|
||||
<LlamaIndexLinks />
|
||||
</div>
|
||||
</div>
|
||||
<TailwindCDNInjection />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LlamaIndexLinks() {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-4 p-2 pr-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<a
|
||||
href="https://www.llamaindex.ai/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
>
|
||||
Built by LlamaIndex
|
||||
</a>
|
||||
<img
|
||||
className="h-[24px] w-[24px] rounded-sm"
|
||||
src="/llama.png"
|
||||
alt="Llama Logo"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
href="https://github.com/run-llama/LlamaIndexTS"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button variant="outline" size="sm">
|
||||
<Star className="mr-2 size-4" />
|
||||
Star on GitHub
|
||||
</Button>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { getConfig } from "./ui/lib/utils";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm">
|
||||
<div className="flex w-full flex-col items-center pb-2 text-center">
|
||||
<h1 className="mb-2 text-4xl font-bold">{getConfig("APP_TITLE")}</h1>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<a
|
||||
href="https://www.llamaindex.ai/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-gray-600 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-200"
|
||||
>
|
||||
Built by LlamaIndex
|
||||
</a>
|
||||
<img
|
||||
className="h-[24px] w-[24px] rounded-sm"
|
||||
src="/llama.png"
|
||||
alt="Llama Logo"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import { useChatMessage } from "@llamaindex/chat-ui";
|
||||
import { User2 } from "lucide-react";
|
||||
import { ChatMessage } from "@llamaindex/chat-ui";
|
||||
|
||||
export function ChatMessageAvatar() {
|
||||
const { message } = useChatMessage();
|
||||
if (message.role === "user") {
|
||||
return (
|
||||
<div className="bg-background flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm">
|
||||
<User2 className="h-4 w-4" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border bg-black text-white shadow-sm">
|
||||
<ChatMessage.Avatar>
|
||||
<img
|
||||
className="h-[40px] w-[40px] rounded-xl object-contain"
|
||||
className="border-1 rounded-full border-[#e711dd]"
|
||||
src="/llama.png"
|
||||
alt="Llama Logo"
|
||||
/>
|
||||
</div>
|
||||
</ChatMessage.Avatar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,30 +45,24 @@ export default function CustomChatInput() {
|
||||
const annotations = getAnnotations();
|
||||
|
||||
return (
|
||||
<ChatInput
|
||||
className="rounded-xl shadow-xl"
|
||||
resetUploadedFiles={reset}
|
||||
annotations={annotations}
|
||||
>
|
||||
<div>
|
||||
{/* Image preview section */}
|
||||
{imageUrl && (
|
||||
<ImagePreview url={imageUrl} onRemove={() => setImageUrl(null)} />
|
||||
)}
|
||||
{/* Document previews section */}
|
||||
{files.length > 0 && (
|
||||
<div className="flex w-full gap-4 overflow-auto py-2">
|
||||
{files.map((file) => (
|
||||
<DocumentInfo
|
||||
key={file.id}
|
||||
document={{ url: file.url, sources: [] }}
|
||||
className="mb-2 mt-2"
|
||||
onRemove={() => removeDoc(file)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ChatInput resetUploadedFiles={reset} annotations={annotations}>
|
||||
{/* Image preview section */}
|
||||
{imageUrl && (
|
||||
<ImagePreview url={imageUrl} onRemove={() => setImageUrl(null)} />
|
||||
)}
|
||||
{/* Document previews section */}
|
||||
{files.length > 0 && (
|
||||
<div className="flex w-full gap-4 overflow-auto py-2">
|
||||
{files.map((file) => (
|
||||
<DocumentInfo
|
||||
key={file.id}
|
||||
document={{ url: file.url, sources: [] }}
|
||||
className="mb-2 mt-2"
|
||||
onRemove={() => removeDoc(file)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<ChatInput.Form>
|
||||
<ChatInput.Field />
|
||||
{uploadAPI && <ChatInput.Upload onUpload={handleUploadFile} />}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function CustomChatMessages({
|
||||
const { messages } = useChatUI();
|
||||
|
||||
return (
|
||||
<ChatMessages className="rounded-xl shadow-xl">
|
||||
<ChatMessages>
|
||||
<ChatMessages.List>
|
||||
{messages.map((message, index) => (
|
||||
<ChatMessage
|
||||
@@ -32,9 +32,9 @@ export default function CustomChatMessages({
|
||||
<ChatMessage.Actions />
|
||||
</ChatMessage>
|
||||
))}
|
||||
<ChatMessages.Empty />
|
||||
<ChatMessages.Loading />
|
||||
</ChatMessages.List>
|
||||
<ChatMessages.Actions />
|
||||
<ChatStarter />
|
||||
</ChatMessages>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useChatUI } from "@llamaindex/chat-ui";
|
||||
import { StarterQuestions } from "@llamaindex/chat-ui/widgets";
|
||||
import { getConfig } from "../lib/utils";
|
||||
|
||||
export function ChatStarter() {
|
||||
export function ChatStarter({ className }: { className?: string }) {
|
||||
const { append, messages, requestData } = useChatUI();
|
||||
const starterQuestions = getConfig("STARTER_QUESTIONS") ?? [];
|
||||
|
||||
@@ -13,6 +13,7 @@ export function ChatStarter() {
|
||||
<StarterQuestions
|
||||
append={(message) => append(message, { data: requestData })}
|
||||
questions={starterQuestions}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,5 @@ const ChatSection = dynamic(() => import("./components/chat-section"), {
|
||||
});
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="background-gradient flex h-screen w-screen items-center justify-center overflow-hidden">
|
||||
<div className="w-[90%] space-y-2 lg:w-[60rem] lg:space-y-10">
|
||||
<ChatSection />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
return <ChatSection />;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/server",
|
||||
"description": "LlamaIndex Server",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
@@ -51,16 +51,18 @@
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "1.2.5",
|
||||
"tsx": "^4.19.3",
|
||||
"tw-animate-css": "1.2.5",
|
||||
"vitest": "^2.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.0",
|
||||
"@babel/standalone": "^7.27.0",
|
||||
"@babel/traverse": "^7.27.0",
|
||||
"@babel/types": "^7.27.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@llamaindex/chat-ui": "0.3.2",
|
||||
"@llama-flow/core": "^0.3.4",
|
||||
"@llamaindex/chat-ui": "0.4.0",
|
||||
"@llamaindex/env": "workspace:*",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||
@@ -106,10 +108,19 @@
|
||||
"recharts": "^2.15.2",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.2"
|
||||
"vaul": "^1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"llamaindex": "workspace:*"
|
||||
"llamaindex": "workspace:*",
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.23.3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"zod": {
|
||||
"optional": true
|
||||
},
|
||||
"zod-to-json-schema": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./events";
|
||||
export * from "./server";
|
||||
export * from "./types";
|
||||
export { generateEventComponent } from "./utils/gen-ui";
|
||||
export { toStreamGenerator } from "./utils/workflow";
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
import { parse } from "@babel/parser";
|
||||
import type { NodePath } from "@babel/traverse";
|
||||
import traverse from "@babel/traverse";
|
||||
import type {
|
||||
ExportDefaultDeclaration,
|
||||
ImportDeclaration,
|
||||
ImportDefaultSpecifier,
|
||||
ImportNamespaceSpecifier,
|
||||
ImportSpecifier,
|
||||
} from "@babel/types";
|
||||
import { createWorkflow, getContext, workflowEvent } from "@llama-flow/core";
|
||||
import { collect } from "@llama-flow/core/stream/consumer";
|
||||
import { until } from "@llama-flow/core/stream/until";
|
||||
import type { LLM } from "llamaindex";
|
||||
import type { ZodType } from "zod";
|
||||
|
||||
const writeAggregationEvent = workflowEvent<{
|
||||
eventSchema: object;
|
||||
uiDescription: string;
|
||||
}>();
|
||||
|
||||
const writeUiComponentEvent = workflowEvent<{
|
||||
eventSchema: object;
|
||||
uiDescription: string;
|
||||
aggregationFunction: string | undefined;
|
||||
}>();
|
||||
|
||||
const refineGeneratedCodeEvent = workflowEvent<{
|
||||
uiCode: string;
|
||||
aggregationFunction: string;
|
||||
uiDescription: string;
|
||||
}>();
|
||||
|
||||
const startEvent = workflowEvent<{
|
||||
eventSchema: object;
|
||||
}>();
|
||||
const stopEvent = workflowEvent<string | null>();
|
||||
|
||||
const CODE_STRUCTURE = `
|
||||
// export the component
|
||||
// The component accepts an 'events' array prop. Each item in the array conforms to the schema provided during generation.
|
||||
export default function Component({ events }) {
|
||||
// logic for aggregating events (if needed)
|
||||
const aggregatedEvents = // ... aggregation logic based on aggregationFunction description ...
|
||||
|
||||
// Determine which events to render (original or aggregated)
|
||||
const eventsToRender = aggregatedEvents || events;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Render eventsToRender using shadcn/ui, lucide-react, tailwind CSS */}
|
||||
{/* Map over eventsToRender and display each one */}
|
||||
{/* Example: */}
|
||||
{/* {eventsToRender.map((event, index) => (
|
||||
<Card key={index}>
|
||||
<CardHeader>
|
||||
<CardTitle>Event Data</CardTitle> // Adjust title as needed
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<pre>{JSON.stringify(event, null, 2)}</pre>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))} */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`;
|
||||
|
||||
const SOURCE_MAP: Record<string, boolean> = {
|
||||
react: true,
|
||||
"react-dom": true,
|
||||
"@/components/ui/accordion": true,
|
||||
"@/components/ui/alert": true,
|
||||
"@/components/ui/alert-dialog": true,
|
||||
"@/components/ui/aspect-ratio": true,
|
||||
"@/components/ui/avatar": true,
|
||||
"@/components/ui/badge": true,
|
||||
"@/components/ui/breadcrumb": true,
|
||||
"@/components/ui/button": true,
|
||||
"@/components/ui/calendar": true,
|
||||
"@/components/ui/card": true,
|
||||
"@/components/ui/carousel": true,
|
||||
"@/components/ui/chart": true,
|
||||
"@/components/ui/checkbox": true,
|
||||
"@/components/ui/collapsible": true,
|
||||
"@/components/ui/command": true,
|
||||
"@/components/ui/context-menu": true,
|
||||
"@/components/ui/dialog": true,
|
||||
"@/components/ui/drawer": true,
|
||||
"@/components/ui/dropdown-menu": true,
|
||||
"@/components/ui/form": true,
|
||||
"@/components/ui/hover-card": true,
|
||||
"@/components/ui/input": true,
|
||||
"@/components/ui/input-otp": true,
|
||||
"@/components/ui/label": true,
|
||||
"@/components/ui/menubar": true,
|
||||
"@/components/ui/navigation-menu": true,
|
||||
"@/components/ui/pagination": true,
|
||||
"@/components/ui/popover": true,
|
||||
"@/components/ui/progress": true,
|
||||
"@/components/ui/radio-group": true,
|
||||
"@/components/ui/resizable": true,
|
||||
"@/components/ui/scroll-area": true,
|
||||
"@/components/ui/select": true,
|
||||
"@/components/ui/separator": true,
|
||||
"@/components/ui/sheet": true,
|
||||
"@/components/ui/sidebar": true,
|
||||
"@/components/ui/skeleton": true,
|
||||
"@/components/ui/slider": true,
|
||||
"@/components/ui/sonner": true,
|
||||
"@/components/ui/switch": true,
|
||||
"@/components/ui/table": true,
|
||||
"@/components/ui/tabs": true,
|
||||
"@/components/ui/textarea": true,
|
||||
"@/components/ui/toggle": true,
|
||||
"@/components/ui/toggle-group": true,
|
||||
"@/components/ui/tooltip": true,
|
||||
"@/components/lib/utils": true,
|
||||
"@/lib/utils": true,
|
||||
"lucide-react": true,
|
||||
"@llamaindex/chat-ui/widgets": true,
|
||||
};
|
||||
|
||||
function generateSupportedDeps(): string {
|
||||
// Extract all shadcn component names from SOURCE_MAP
|
||||
const shadcnComponents = Object.keys(SOURCE_MAP)
|
||||
.filter((key) => key.startsWith("@/components/ui/"))
|
||||
.map((key) => key.replace("@/components/ui/", ""))
|
||||
.sort()
|
||||
.join(", ");
|
||||
|
||||
return `
|
||||
- React: import { useState } from "react";
|
||||
- shadcn/ui: import { ComponentName } from "@/components/ui/<component_path>";
|
||||
Supported shadcn components:
|
||||
${shadcnComponents}
|
||||
- lucide-react: import { IconName } from "lucide-react";
|
||||
- tailwind css: import { cn } from "@/lib/utils"; // Note: clsx is not supported
|
||||
- LlamaIndex's markdown-ui: import { Markdown } from "@llamaindex/chat-ui/widgets";
|
||||
`;
|
||||
}
|
||||
|
||||
const SUPPORTED_DEPS = generateSupportedDeps();
|
||||
|
||||
function validateComponentCode(code: string): {
|
||||
isValid: boolean;
|
||||
error?: string;
|
||||
componentName?: string;
|
||||
} {
|
||||
try {
|
||||
const imports: Array<{ name: string; source: string }> = [];
|
||||
let componentName: string | null = null;
|
||||
|
||||
// Parse the code into an AST
|
||||
const ast = parse(code, {
|
||||
sourceType: "module",
|
||||
plugins: ["jsx", "typescript"],
|
||||
});
|
||||
|
||||
// Traverse the AST to find import declarations
|
||||
traverse(ast, {
|
||||
// Find import declarations
|
||||
ImportDeclaration(path: NodePath<ImportDeclaration>) {
|
||||
path.node.specifiers.forEach(
|
||||
(
|
||||
specifier:
|
||||
| ImportSpecifier
|
||||
| ImportDefaultSpecifier
|
||||
| ImportNamespaceSpecifier,
|
||||
) => {
|
||||
if (
|
||||
specifier.type === "ImportSpecifier" ||
|
||||
specifier.type === "ImportDefaultSpecifier"
|
||||
) {
|
||||
imports.push({
|
||||
name: specifier.local.name, // e.g., "Button"
|
||||
source: path.node.source.value, // e.g., "@/components/ui/button"
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
// Find export default declaration
|
||||
ExportDefaultDeclaration(path: NodePath<ExportDefaultDeclaration>) {
|
||||
const declaration = path.node.declaration;
|
||||
if (declaration.type === "FunctionDeclaration" && declaration.id) {
|
||||
componentName = declaration.id.name; // e.g., "EventTimeline"
|
||||
} else if (
|
||||
declaration.type === "Identifier" &&
|
||||
path.scope.hasBinding(declaration.name)
|
||||
) {
|
||||
componentName = declaration.name; // e.g., named function assigned to export
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Validate imports
|
||||
for (const { name, source } of imports) {
|
||||
if (!(source in SOURCE_MAP)) {
|
||||
console.error(`Invalid import: ${name} from ${source}`);
|
||||
return {
|
||||
isValid: false,
|
||||
error: `Failed to import ${name} from ${source}. Reason: Module not found.
|
||||
\nHere is the list of supported imports: ${SUPPORTED_DEPS}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Validate component export
|
||||
if (!componentName) {
|
||||
console.warn("Could not identify component name in the generated code.");
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
...(componentName ? { componentName } : {}),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error during code validation:", error);
|
||||
return {
|
||||
isValid: false,
|
||||
error:
|
||||
error instanceof Error ? error.message : "Unknown validation error",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the UI generation workflow with the provided LLM instance.
|
||||
*
|
||||
* @param llm - The LLM instance to use for the workflow.
|
||||
* @returns The configured workflow instance.
|
||||
*/
|
||||
export function createGenUiWorkflow(llm: LLM) {
|
||||
const genUiWorkflow = createWorkflow();
|
||||
|
||||
genUiWorkflow.handle([startEvent], async ({ data: { eventSchema } }) => {
|
||||
const context = getContext();
|
||||
|
||||
const planningPrompt = `
|
||||
# Your role
|
||||
You are an AI assistant helping to plan a React UI component. This component will display *one or more events* in a chat application, all conforming to a single JSON schema.
|
||||
|
||||
# Context
|
||||
Here is the JSON schema for the events the component needs to display:
|
||||
${JSON.stringify(eventSchema, null, 2)}
|
||||
|
||||
# Task
|
||||
1. Analyze the event schema.
|
||||
2. Decide if multiple events of this type should be aggregated before rendering in the UI (e.g., group similar events, summarize sequences). Assume the component will receive an array of these events.
|
||||
3. If aggregation is needed, provide a *brief* description of the JavaScript function logic (no code implementation yet, just the logic description) that would take an array of events and return an aggregated representation.
|
||||
4. Provide a concise description of the desired UI look and feel for displaying these events (e.g., "Display each event in a card with an icon representing the event type.").
|
||||
|
||||
e.g: Assume that the backend produce list of events with animal name, action, and status.
|
||||
\`\`\`
|
||||
A card-based layout displaying animal actions:
|
||||
- Each card shows an animal's image at the top
|
||||
- Below the image: animal name as the card title
|
||||
- Action details in the card body with an icon (eating 🍖, sleeping 😴, playing 🎾)
|
||||
- Status badge in the corner showing if action is ongoing/completed
|
||||
- Expandable section for additional details
|
||||
- Soft color scheme based on action type
|
||||
\`\`\`
|
||||
|
||||
Don't be verbose, just return the description for the UI based on the event schema and data.
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await llm.complete({
|
||||
prompt: planningPrompt,
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const responseText = response.text.trim();
|
||||
console.log("\nUI Description:", responseText);
|
||||
|
||||
context.sendEvent(
|
||||
writeAggregationEvent.with({
|
||||
eventSchema,
|
||||
uiDescription: responseText,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error during UI planning:", error);
|
||||
context.sendEvent(stopEvent.with(null));
|
||||
}
|
||||
});
|
||||
|
||||
genUiWorkflow.handle([writeAggregationEvent], async ({ data: planData }) => {
|
||||
const context = getContext();
|
||||
|
||||
const schemaContext = JSON.stringify(planData.eventSchema, null, 2);
|
||||
const uiDescriptionContext = planData.uiDescription;
|
||||
|
||||
const writingPrompt = `
|
||||
# Your role
|
||||
You are a frontend developer who is developing a React component for given events that are emitted from a backend workflow.
|
||||
Here are the events that you need to work on: ${schemaContext}
|
||||
Here is the description of the UI: ${uiDescriptionContext}
|
||||
|
||||
# Task
|
||||
Based on the description of the UI and the list of events, write the aggregation function that will be used to aggregate the events.
|
||||
Take into account that the list of events grows with time. At the beginning, there is only one event in the list, and events are incrementally added.
|
||||
To render the events in a visually pleasing way, try to aggregate them by their attributes and render the aggregates instead of just rendering a list of all events.
|
||||
Don't add computation to the aggregation function, just group the events by their attributes.
|
||||
Make sure that the aggregation should reflect the description of the UI and the grouped events are not duplicated, make it as simple as possible to avoid unnecessary issues.
|
||||
|
||||
# Answer with the following format:
|
||||
\`\`\`jsx
|
||||
const aggregateEvents = () => {
|
||||
// code for aggregating events here if needed otherwise let the jsx code block empty
|
||||
}
|
||||
\`\`\`
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await llm.complete({
|
||||
prompt: writingPrompt,
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const generatedCode = response.text.trim();
|
||||
context.sendEvent(
|
||||
writeUiComponentEvent.with({
|
||||
eventSchema: planData.eventSchema,
|
||||
uiDescription: planData.uiDescription,
|
||||
aggregationFunction: generatedCode,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error during aggregation function writing:", error);
|
||||
context.sendEvent(stopEvent.with(null));
|
||||
}
|
||||
});
|
||||
|
||||
genUiWorkflow.handle([writeUiComponentEvent], async ({ data: planData }) => {
|
||||
const context = getContext();
|
||||
|
||||
const aggregationFunctionContext = planData.aggregationFunction
|
||||
? `
|
||||
# Here is the aggregation function that aggregates the events:
|
||||
${planData.aggregationFunction}`
|
||||
: "";
|
||||
|
||||
const schemaContext = JSON.stringify(planData.eventSchema, null, 2);
|
||||
const uiDescriptionContext = planData.uiDescription;
|
||||
|
||||
const writingPrompt = `
|
||||
# Your role
|
||||
You are a frontend developer who is developing a React component using shadcn/ui, lucide-react, LlamaIndex's chat-ui, and tailwind css (cn) for the UI.
|
||||
You are given a list of events and other context.
|
||||
Your task is to write a beautiful UI for the events that will be included in a chat UI.
|
||||
|
||||
# Context:
|
||||
Here are the events that you need to work on: ${schemaContext}
|
||||
${aggregationFunctionContext}
|
||||
Here is the description of the UI:
|
||||
\`\`\`
|
||||
${uiDescriptionContext}
|
||||
\`\`\`
|
||||
|
||||
|
||||
# Only use the following dependencies: ${SUPPORTED_DEPS}
|
||||
|
||||
# Requirements:
|
||||
- Write beautiful UI components for the events using the supported dependencies
|
||||
- The component text/label should be specified for each event type.
|
||||
|
||||
|
||||
# Instructions:
|
||||
## Event and schema notice
|
||||
- Based on the provided list of events, determine their types and attributes.
|
||||
- It's normal that the schema is applied to all events, but the events might be completely different where some schema attributes aren't used.
|
||||
- You should make the component visually distinct for each event type.
|
||||
e.g: A simple cat schema
|
||||
\`\`\`{"type": "cat", "action": ["jump", "run", "meow"], "jump": {"height": 10, "distance": 20}, "run": {"distance": 100}}\`\`\`
|
||||
You should display the jump, run and meow actions in different ways. Don't try to render "height" for the "run" and "meow" action.
|
||||
|
||||
## UI notice
|
||||
- Use the supported dependencies for the UI.
|
||||
- Be careful on state handling, make sure the update should be updated in the state and there is no duplicate state.
|
||||
- For a long content, consider to use markdown along with dropdown to show the full content.
|
||||
e.g:
|
||||
\`\`\`jsx
|
||||
import { Markdown } from "@llamaindex/chat-ui/widgets";
|
||||
<Markdown content={content} />
|
||||
\`\`\`
|
||||
- Try to make the component placement not monotonous, consider use row/column/flex/grid layout.
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await llm.complete({
|
||||
prompt: writingPrompt,
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const generatedCode = response.text.trim();
|
||||
|
||||
context.sendEvent(
|
||||
refineGeneratedCodeEvent.with({
|
||||
uiCode: generatedCode,
|
||||
aggregationFunction: planData.aggregationFunction || "",
|
||||
uiDescription: planData.uiDescription,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error during UI component writing:", error);
|
||||
context.sendEvent(stopEvent.with(null));
|
||||
}
|
||||
});
|
||||
|
||||
genUiWorkflow.handle(
|
||||
[refineGeneratedCodeEvent],
|
||||
async ({ data: writeData }) => {
|
||||
const context = getContext();
|
||||
const MAX_VALIDATION_ATTEMPTS = 3;
|
||||
|
||||
let currentCode = writeData.uiCode;
|
||||
let attemptCount = 0;
|
||||
let validationError = null;
|
||||
|
||||
while (attemptCount < MAX_VALIDATION_ATTEMPTS) {
|
||||
attemptCount++;
|
||||
if (attemptCount > 1) {
|
||||
console.log(
|
||||
`Refinement attempt ${attemptCount}/${MAX_VALIDATION_ATTEMPTS}`,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Build refinement prompt - include error info for subsequent attempts
|
||||
const errorSection =
|
||||
attemptCount > 1 && validationError
|
||||
? `\n# Error to fix:\n${validationError}\n\n# Additional requirements:\n1. Only import from supported modules\n2. Ensure the component has an export default statement\n3. Component must accept an 'events' array prop`
|
||||
: "";
|
||||
|
||||
const refiningPrompt = `
|
||||
# Your role
|
||||
You are a senior frontend developer reviewing React code written by a junior developer.
|
||||
|
||||
# Context:
|
||||
- The goal is to create a React component that displays an array of events.
|
||||
- Required Code Structure (Component accepts an \`events\` array prop):
|
||||
${CODE_STRUCTURE}
|
||||
- Aggregation Context (if any): ${writeData.aggregationFunction || "None"}
|
||||
- ${attemptCount > 1 ? "Previous" : "Generated"} Code:
|
||||
${currentCode}${errorSection}
|
||||
|
||||
# Task:
|
||||
Review and refine the provided code. Ensure it strictly follows the "Required Code Structure" (including accepting the \`events\` array prop), implements any described aggregation logic correctly, imports are correct (individual shadcn/ui imports), and there are no obvious bugs or undefined variables.
|
||||
|
||||
# Output Format:
|
||||
Return ONLY the final, refined code, enclosed in a single JSX code block (\`\`\`jsx ... \`\`\`). Do not add any explanations before or after the code block.
|
||||
`;
|
||||
|
||||
const response = await llm.complete({
|
||||
prompt: refiningPrompt,
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const refinedCode = response.text.trim();
|
||||
// Extract code from markdown block if present
|
||||
const codeMatch = refinedCode.match(/```jsx\n?([^]*?)\n?```/);
|
||||
if (codeMatch && codeMatch[1]) {
|
||||
currentCode = codeMatch[1].trim();
|
||||
} else {
|
||||
// Fallback if no block found - attempt cleanup
|
||||
currentCode = refinedCode.replace(/^```jsx|```$/g, "").trim();
|
||||
console.warn(
|
||||
"Could not find standard JSX code block in refinement response, using raw content.",
|
||||
);
|
||||
}
|
||||
|
||||
// Validate the refined code
|
||||
const validation = validateComponentCode(currentCode);
|
||||
|
||||
if (validation.isValid) {
|
||||
console.log(`\n✅ Code validated successfully`);
|
||||
context.sendEvent(stopEvent.with(currentCode));
|
||||
return;
|
||||
} else {
|
||||
validationError = validation.error;
|
||||
console.warn(
|
||||
`Validation failed (attempt ${attemptCount}/${MAX_VALIDATION_ATTEMPTS}): ${validation.error}`,
|
||||
);
|
||||
|
||||
// If this was the last attempt, give up
|
||||
if (attemptCount >= MAX_VALIDATION_ATTEMPTS) {
|
||||
console.error(
|
||||
`Failed to generate valid code after ${MAX_VALIDATION_ATTEMPTS} attempts`,
|
||||
);
|
||||
context.sendEvent(stopEvent.with(null));
|
||||
return;
|
||||
}
|
||||
// Otherwise continue to the next iteration of the loop
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error during refinement attempt ${attemptCount}:`,
|
||||
error,
|
||||
);
|
||||
context.sendEvent(stopEvent.with(null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return genUiWorkflow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a React UI component for displaying event data of a given type.
|
||||
*
|
||||
* @param eventType - A Zod schema representing the event type.
|
||||
* @param llm - The LLM instance to use for the workflow.
|
||||
* We recommend using gpt-4.1, sonnet-3.7, or gemini-2.5-pro
|
||||
* for better results
|
||||
* @returns The generated React component code as a string.
|
||||
*/
|
||||
export async function generateEventComponent(
|
||||
eventType: ZodType | object,
|
||||
llm: LLM,
|
||||
): Promise<string> {
|
||||
let eventSchema: object = eventType;
|
||||
if ("parse" in eventType && "safeParse" in eventType) {
|
||||
// Zod schema given, convert to JSON schema including descriptions
|
||||
const zodToJsonSchema = (await import("zod-to-json-schema")).default;
|
||||
const zodEventSchema = zodToJsonSchema(eventType, {
|
||||
target: "openApi3",
|
||||
});
|
||||
if (!zodEventSchema) {
|
||||
throw new Error("Could not get JSON schema for the event type");
|
||||
}
|
||||
eventSchema = zodEventSchema;
|
||||
}
|
||||
console.log(`🎨 Starting UI generation...
|
||||
`);
|
||||
|
||||
try {
|
||||
const genUiWorkflow = createGenUiWorkflow(llm);
|
||||
|
||||
const { stream, sendEvent } = genUiWorkflow.createContext();
|
||||
sendEvent(startEvent.with({ eventSchema }));
|
||||
|
||||
// Collect all events until the stop event and get the last one
|
||||
const allEvents = await collect(until(stream, stopEvent));
|
||||
const result = allEvents[allEvents.length - 1];
|
||||
if (result?.data === null) {
|
||||
throw new Error("Workflow failed.");
|
||||
} else if (result) {
|
||||
console.log("\nWorkflow finished successfully.");
|
||||
return result.data;
|
||||
} else {
|
||||
throw new Error("Workflow result is undefined.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Workflow execution failed:", error);
|
||||
throw new Error(`UI generation workflow failed: ${error}`);
|
||||
}
|
||||
}
|
||||
Generated
+76
-23
@@ -619,7 +619,7 @@ importers:
|
||||
specifier: ^0.0.16
|
||||
version: link:../packages/providers/storage/chroma
|
||||
'@llamaindex/clip':
|
||||
specifier: ^0.0.51
|
||||
specifier: ^0.0.52
|
||||
version: link:../packages/providers/clip
|
||||
'@llamaindex/cloud':
|
||||
specifier: ^4.0.3
|
||||
@@ -631,10 +631,10 @@ importers:
|
||||
specifier: ^0.6.2
|
||||
version: link:../packages/core
|
||||
'@llamaindex/deepinfra':
|
||||
specifier: ^0.0.51
|
||||
specifier: ^0.0.52
|
||||
version: link:../packages/providers/deepinfra
|
||||
'@llamaindex/deepseek':
|
||||
specifier: ^0.0.11
|
||||
specifier: ^0.0.12
|
||||
version: link:../packages/providers/deepseek
|
||||
'@llamaindex/elastic-search':
|
||||
specifier: ^0.1.2
|
||||
@@ -646,19 +646,19 @@ importers:
|
||||
specifier: ^1.0.9
|
||||
version: link:../packages/providers/storage/firestore
|
||||
'@llamaindex/fireworks':
|
||||
specifier: ^0.0.11
|
||||
specifier: ^0.0.12
|
||||
version: link:../packages/providers/fireworks
|
||||
'@llamaindex/google':
|
||||
specifier: ^0.2.3
|
||||
specifier: ^0.2.4
|
||||
version: link:../packages/providers/google
|
||||
'@llamaindex/groq':
|
||||
specifier: ^0.0.66
|
||||
specifier: ^0.0.67
|
||||
version: link:../packages/providers/groq
|
||||
'@llamaindex/huggingface':
|
||||
specifier: ^0.1.5
|
||||
specifier: ^0.1.6
|
||||
version: link:../packages/providers/huggingface
|
||||
'@llamaindex/jinaai':
|
||||
specifier: ^0.0.11
|
||||
specifier: ^0.0.12
|
||||
version: link:../packages/providers/jinaai
|
||||
'@llamaindex/milvus':
|
||||
specifier: ^0.1.11
|
||||
@@ -679,10 +679,10 @@ importers:
|
||||
specifier: ^0.1.2
|
||||
version: link:../packages/providers/ollama
|
||||
'@llamaindex/openai':
|
||||
specifier: ^0.3.3
|
||||
specifier: ^0.3.4
|
||||
version: link:../packages/providers/openai
|
||||
'@llamaindex/perplexity':
|
||||
specifier: ^0.0.8
|
||||
specifier: ^0.0.9
|
||||
version: link:../packages/providers/perplexity
|
||||
'@llamaindex/pinecone':
|
||||
specifier: ^0.1.2
|
||||
@@ -706,7 +706,7 @@ importers:
|
||||
specifier: ^0.1.1
|
||||
version: link:../packages/providers/storage/supabase
|
||||
'@llamaindex/together':
|
||||
specifier: ^0.0.11
|
||||
specifier: ^0.0.12
|
||||
version: link:../packages/providers/together
|
||||
'@llamaindex/tools':
|
||||
specifier: ^0.0.5
|
||||
@@ -718,7 +718,7 @@ importers:
|
||||
specifier: ^0.1.2
|
||||
version: link:../packages/providers/vercel
|
||||
'@llamaindex/vllm':
|
||||
specifier: ^0.0.37
|
||||
specifier: ^0.0.38
|
||||
version: link:../packages/providers/vllm
|
||||
'@llamaindex/voyage-ai':
|
||||
specifier: ^1.0.8
|
||||
@@ -754,7 +754,7 @@ importers:
|
||||
specifier: ^1.0.14
|
||||
version: 1.0.19
|
||||
llamaindex:
|
||||
specifier: ^0.10.1
|
||||
specifier: ^0.10.2
|
||||
version: link:../packages/llamaindex
|
||||
mongodb:
|
||||
specifier: 6.7.0
|
||||
@@ -1655,12 +1655,18 @@ importers:
|
||||
'@babel/traverse':
|
||||
specifier: ^7.27.0
|
||||
version: 7.27.0
|
||||
'@babel/types':
|
||||
specifier: ^7.27.0
|
||||
version: 7.27.0
|
||||
'@hookform/resolvers':
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1(react-hook-form@7.55.0(react@19.1.0))
|
||||
'@llama-flow/core':
|
||||
specifier: ^0.3.4
|
||||
version: 0.3.4(@modelcontextprotocol/sdk@1.9.0)(hono@4.7.7)(next@15.3.0(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.2)
|
||||
'@llamaindex/chat-ui':
|
||||
specifier: 0.3.2
|
||||
version: 0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
specifier: 0.4.0
|
||||
version: 0.4.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@llamaindex/env':
|
||||
specifier: workspace:*
|
||||
version: link:../env
|
||||
@@ -1802,6 +1808,9 @@ importers:
|
||||
zod:
|
||||
specifier: ^3.24.2
|
||||
version: 3.24.2
|
||||
zod-to-json-schema:
|
||||
specifier: ^3.23.3
|
||||
version: 3.24.5(zod@3.24.2)
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3
|
||||
@@ -3789,6 +3798,26 @@ packages:
|
||||
'@lezer/yaml@1.0.3':
|
||||
resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==}
|
||||
|
||||
'@llama-flow/core@0.3.4':
|
||||
resolution: {integrity: sha512-BOe23pfm7j9hKMH7u0jFS8bPKLsShKgz8KdN/rXeicgnopCwhDMO4ppOg7Cy7tWap8kYZIY8ZliN7Q9SmNfjkg==}
|
||||
peerDependencies:
|
||||
'@modelcontextprotocol/sdk': ^1.7.0
|
||||
hono: ^4.7.4
|
||||
next: ^15.2.2
|
||||
p-retry: ^6.2.1
|
||||
zod: ^3.24.2
|
||||
peerDependenciesMeta:
|
||||
'@modelcontextprotocol/sdk':
|
||||
optional: true
|
||||
hono:
|
||||
optional: true
|
||||
next:
|
||||
optional: true
|
||||
p-retry:
|
||||
optional: true
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@llama-flow/docs@0.0.3':
|
||||
resolution: {integrity: sha512-5BFSbaWY7Ps5djzXilgyy9t7OYElyoojvEmLy/FC1azUnn6poIxvr04Ctsoi0PR44sYyqWb7hkuU5iJap6uLyA==}
|
||||
|
||||
@@ -3797,8 +3826,8 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
'@llamaindex/chat-ui@0.3.2':
|
||||
resolution: {integrity: sha512-YcQOghcxutqHK9KO2CRSws0inDR5bbMZkmpUFJtC2aWcHjWi8wYbzVZjRVl1vrb3VCk+VInKOhFUTW9hEkzydA==}
|
||||
'@llamaindex/chat-ui@0.4.0':
|
||||
resolution: {integrity: sha512-u9jOUuyKPDFnJsorfH8oIE0UVO+zlabbD8lgTFbN37XUdYFFG4rteEYwUWc/n4/h/GjnFcTfxpiyWmiwTKzicw==}
|
||||
peerDependencies:
|
||||
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
@@ -15877,6 +15906,14 @@ snapshots:
|
||||
'@lezer/highlight': 1.2.1
|
||||
'@lezer/lr': 1.4.2
|
||||
|
||||
'@llama-flow/core@0.3.4(@modelcontextprotocol/sdk@1.9.0)(hono@4.7.7)(next@15.3.0(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(p-retry@6.2.1)(zod@3.24.2)':
|
||||
optionalDependencies:
|
||||
'@modelcontextprotocol/sdk': 1.9.0
|
||||
hono: 4.7.7
|
||||
next: 15.3.0(@opentelemetry/api@1.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
p-retry: 6.2.1
|
||||
zod: 3.24.2
|
||||
|
||||
'@llama-flow/docs@0.0.3': {}
|
||||
|
||||
'@llamaindex/chat-ui@0.2.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
@@ -15909,7 +15946,7 @@ snapshots:
|
||||
- react-dom
|
||||
- supports-color
|
||||
|
||||
'@llamaindex/chat-ui@0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
'@llamaindex/chat-ui@0.4.0(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@llamaindex/pdf-viewer': 1.3.0(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-collapsible': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@@ -20724,7 +20761,7 @@ snapshots:
|
||||
'@typescript-eslint/parser': 8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-react: 7.37.2(eslint@9.22.0(jiti@2.4.2))
|
||||
@@ -20754,6 +20791,22 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
debug: 4.4.0
|
||||
enhanced-resolve: 5.18.1
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
fast-glob: 3.3.3
|
||||
get-tsconfig: 4.10.0
|
||||
is-bun-module: 1.3.0
|
||||
is-glob: 4.0.3
|
||||
stable-hash: 0.0.4
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.16.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
@@ -20782,7 +20835,7 @@ snapshots:
|
||||
is-glob: 4.0.3
|
||||
stable-hash: 0.0.4
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -20808,14 +20861,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -20888,7 +20941,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.30.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @llamaindex/unit-test
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e5c3f95]
|
||||
- @llamaindex/openai@0.3.4
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 0.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/unit-test",
|
||||
"private": true,
|
||||
"version": "0.1.21",
|
||||
"version": "0.1.22",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "vitest run"
|
||||
|
||||
Reference in New Issue
Block a user