mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-19 18:43:34 -04:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c1750fe1c | |||
| 2748108bc0 | |||
| a334c77484 | |||
| 325c7c4dd1 | |||
| fd4c7fa750 | |||
| 1232dd824d | |||
| 359698d04b | |||
| b49fb24948 | |||
| 78841495aa | |||
| c81dd21472 | |||
| 52868ea0f9 | |||
| e0a730e44e | |||
| eda486bb52 | |||
| 10d9c708db | |||
| 556027705e |
@@ -8,6 +8,11 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_REMOTE_ONLY: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
name: Publish Preview
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
TURBO_REMOTE_ONLY: true
|
||||
|
||||
jobs:
|
||||
pre_release:
|
||||
name: Pre Release
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"dependencies": {
|
||||
"@huggingface/transformers": "^3.5.0",
|
||||
"@icons-pack/react-simple-icons": "^10.1.0",
|
||||
"@llama-flow/docs": "0.0.3",
|
||||
"@llama-flow/docs": "0.0.5",
|
||||
"@llamaindex/chat-ui": "0.2.0",
|
||||
"@llamaindex/cloud": "workspace:*",
|
||||
"@llamaindex/core": "workspace:*",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { generateFiles as openapiGenerateFiles } from "fumadocs-openapi";
|
||||
import {
|
||||
createGenerator,
|
||||
generateFiles as typescriptGenerateFiles,
|
||||
@@ -18,12 +17,6 @@ rimrafSync(out, {
|
||||
},
|
||||
});
|
||||
|
||||
void openapiGenerateFiles({
|
||||
input: ["../../packages/cloud/openapi.json"],
|
||||
output: "./src/content/docs/cloud/api",
|
||||
groupBy: "tag",
|
||||
});
|
||||
|
||||
void typescriptGenerateFiles(generator, {
|
||||
input: ["./src/content/docs/api/**/*.mdx"],
|
||||
output: (file) => path.resolve(path.dirname(file), path.basename(file)),
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins";
|
||||
import {
|
||||
rehypeCodeDefaultOptions,
|
||||
remarkStructure,
|
||||
} from "fumadocs-core/mdx-plugins";
|
||||
import { fileGenerator, remarkDocGen, remarkInstall } from "fumadocs-docgen";
|
||||
import { defineConfig, defineDocs } from "fumadocs-mdx/config";
|
||||
import { transformerTwoslash } from "fumadocs-twoslash";
|
||||
import { createFileSystemTypesCache } from "fumadocs-twoslash/cache-fs";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkMath from "remark-math";
|
||||
|
||||
@@ -24,11 +26,7 @@ export default defineConfig({
|
||||
},
|
||||
transformers: [
|
||||
...(rehypeCodeDefaultOptions.transformers ?? []),
|
||||
transformerTwoslash({
|
||||
typesCache: createFileSystemTypesCache({
|
||||
dir: ".next/cache/twoslash",
|
||||
}),
|
||||
}),
|
||||
transformerTwoslash(),
|
||||
{
|
||||
name: "transformers:remove-notation-escape",
|
||||
code(hast) {
|
||||
@@ -49,6 +47,7 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
remarkPlugins: [
|
||||
remarkStructure,
|
||||
remarkMath,
|
||||
[remarkInstall, { persist: { id: "package-manager" } }],
|
||||
[remarkDocGen, { generators: [fileGenerator()] }],
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createFromSource } from "fumadocs-core/search/server";
|
||||
|
||||
// TODO: migrate to another search service, I don't think Vercel can handle that many of documents.
|
||||
export const { GET } = createFromSource(source, (page) => ({
|
||||
id: page.url,
|
||||
id: page.file.path,
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
url: page.url,
|
||||
|
||||
@@ -70,10 +70,6 @@ export default async function Page(props: {
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return source.generateParams();
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
}) {
|
||||
|
||||
@@ -9,6 +9,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
{...baseOptions}
|
||||
links={[]}
|
||||
nav={{
|
||||
...baseOptions.nav,
|
||||
}}
|
||||
|
||||
@@ -27,9 +27,19 @@ export const baseOptions: BaseLayoutProps = {
|
||||
githubUrl: "https://github.com/run-llama/LlamaIndexTS",
|
||||
links: [
|
||||
{
|
||||
text: "Docs",
|
||||
text: "TypeScript",
|
||||
url: DOCUMENT_URL,
|
||||
active: "nested-url",
|
||||
},
|
||||
{
|
||||
text: "Python",
|
||||
url: "https://docs.llamaindex.ai",
|
||||
active: "url",
|
||||
},
|
||||
{
|
||||
text: "LlamaCloud",
|
||||
url: "https://docs.cloud.llamaindex.ai/",
|
||||
active: "url",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -13,11 +13,7 @@ import remarkStringify from "remark-stringify";
|
||||
export const revalidate = false;
|
||||
|
||||
export async function GET() {
|
||||
const files = await fg([
|
||||
"./src/content/docs/**/*.mdx",
|
||||
// remove generated openapi files
|
||||
"!./src/content/docs/cloud/api/**/*",
|
||||
]);
|
||||
const files = await fg(["./src/content/docs/**/*.mdx"]);
|
||||
|
||||
const scan = files.map(async (file) => {
|
||||
const fileContent = await fs.readFile(file);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
title: LlamaCloud
|
||||
description: LlamaCloud is a new generation of managed parsing, ingestion, and retrieval services, designed to bring production-grade context-augmentation to your LLM and RAG applications.
|
||||
---
|
||||
|
||||
This is TypeScript binding for LlamaCloud API. It provides a simple way to interact with LlamaCloud API.
|
||||
|
||||
If you are looking for the official documentation, please visit the [Official Document](https://docs.cloud.llamaindex.ai/)
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"title": "LlamaCloud",
|
||||
"description": "The Cloud framework for LLM",
|
||||
"root": true,
|
||||
"pages": ["---Guide---", "index", "..."]
|
||||
}
|
||||
@@ -112,6 +112,3 @@ The returned `imageDocs` have the alt text assigned as text and the image path a
|
||||
|
||||
You can see the full example file [here](https://github.com/run-llama/LlamaIndexTS/blob/main/examples/readers/src/llamaparse-json.ts).
|
||||
|
||||
## API Reference
|
||||
|
||||
- [LlamaParseReader](/docs/api/classes/LlamaParseReader)
|
||||
|
||||
@@ -32,7 +32,7 @@ They can be divided into two groups.
|
||||
#### Advanced params:
|
||||
|
||||
- `resultType` can be set to `markdown`, `text` or `json`. Defaults to `text`. More information about `json` mode on the next pages.
|
||||
- `language` primarily helps with OCR recognition. Defaults to `en`. Click [here](/docs/api/type-aliases/Language) for a list of supported languages.
|
||||
- `language` primarily helps with OCR recognition. Defaults to `en`.
|
||||
- `parsingInstructions?` Optional. Can help with complicated document structures. See this [LlamaIndex Blog Post](https://www.llamaindex.ai/blog/launching-the-first-genai-native-document-parsing-platform) for an example.
|
||||
- `skipDiagonalText?` Optional. Set to true to ignore diagonal text. (Text that is not rotated 0, 90, 180 or 270 degrees)
|
||||
- `invalidateCache?` Optional. Set to true to ignore the LlamaCloud cache. All document are kept in cache for 48hours after the job was completed to avoid processing the same document twice. Can be useful for testing when trying to re-parse the same document with, e.g. different `parsingInstructions`.
|
||||
@@ -61,4 +61,3 @@ Below a full example of `LlamaParse` integrated in `SimpleDirectoryReader` with
|
||||
## API Reference
|
||||
|
||||
- [SimpleDirectoryReader](/docs/api/classes/SimpleDirectoryReader)
|
||||
- [LlamaParseReader](/docs/api/classes/LlamaParseReader)
|
||||
|
||||
@@ -98,5 +98,4 @@ You can assign any other values of the JSON response to the Document as needed.
|
||||
|
||||
## API Reference
|
||||
|
||||
- [LlamaParseReader](/docs/api/classes/LlamaParseReader)
|
||||
- [SimpleDirectoryReader](/docs/api/classes/SimpleDirectoryReader)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"basic_agent",
|
||||
"rag",
|
||||
"agents",
|
||||
"workflow",
|
||||
"../../llamaflow",
|
||||
"local_llm",
|
||||
"chatbot",
|
||||
"structured_data_extraction"
|
||||
|
||||
-224
@@ -1,224 +0,0 @@
|
||||
---
|
||||
title: Inputs / Outputs
|
||||
description: Learn how to use different inputs and outputs in your 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.
|
||||
|
||||
## Multiple inputs
|
||||
|
||||
You can define multiple inputs for a step.
|
||||
|
||||
In the following example, we define a complex workflow with multiple inputs and outputs.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
class AEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class BEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
First, let's define the events that we will use in the workflow.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
class AEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class BEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
const workflow = new Workflow<never, string, string>();
|
||||
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (
|
||||
context,
|
||||
startEvent
|
||||
) => {
|
||||
const input = startEvent.data;
|
||||
const aEvent = await context.requireEvent(AEvent);
|
||||
const bEvent = await context.requireEvent(BEvent);
|
||||
const a = aEvent.data;
|
||||
const b = bEvent.data;
|
||||
return new StopEvent(`Hello, ${input}! A: ${a}, B: ${b}`);
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [AEvent, BEvent],
|
||||
outputs: [ResultEvent]
|
||||
}, async (
|
||||
context,
|
||||
aEvent,
|
||||
bEvent
|
||||
) => {
|
||||
const a = aEvent.data;
|
||||
const b = bEvent.data;
|
||||
return new ResultEvent(`A: ${a}, B: ${b}`);
|
||||
});
|
||||
```
|
||||
|
||||
This step means that it requires two events: `AEvent` and `BEvent`. It will return a `ResultEvent` with the data `A: ${a}, B: ${b}`.
|
||||
|
||||
## A or B input
|
||||
|
||||
If we want to have a step that can accept either `AEvent` or `BEvent`, we can define the step like this:
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
class AEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class BEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
const workflow = new Workflow<never, string, string>();
|
||||
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (
|
||||
context,
|
||||
startEvent
|
||||
) => {
|
||||
const input = startEvent.data;
|
||||
const aEvent = await context.requireEvent(AEvent);
|
||||
const bEvent = await context.requireEvent(BEvent);
|
||||
const a = aEvent.data;
|
||||
const b = bEvent.data;
|
||||
return new StopEvent(`Hello, ${input}! A: ${a}, B: ${b}`);
|
||||
});
|
||||
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [WorkflowEvent.or(AEvent, BEvent)],
|
||||
outputs: [ResultEvent]
|
||||
}, async (
|
||||
context,
|
||||
aOrBEvent
|
||||
) => {
|
||||
if (aOrBEvent instanceof AEvent) {
|
||||
// ^?
|
||||
|
||||
|
||||
const a = aOrBEvent.data;
|
||||
// ^?
|
||||
|
||||
|
||||
return new ResultEvent(`A: ${a}`);
|
||||
} else {
|
||||
const b = aOrBEvent.data;
|
||||
// ^?
|
||||
|
||||
|
||||
return new ResultEvent(`B: ${b}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This step means that it requires either `AEvent` or `BEvent`. It will return a `ResultEvent` with the data `A: ${a}` or `B: ${b}`.
|
||||
|
||||
You can still combine the logic with `context.requireEvent` to get the data from the event.
|
||||
|
||||
<Accordions>
|
||||
<Accordion title="Under the hood">
|
||||
We use JavaScript Inheritance and the prototype chain to implement the `or` logic.
|
||||
The `or` method creates a new class that extends the two classes that you pass to it.
|
||||
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain"
|
||||
>
|
||||
MDN - Inheritance and the prototype chain
|
||||
</a>
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## Multiple outputs
|
||||
|
||||
You can define multiple outputs for a step.
|
||||
|
||||
```ts twoslash
|
||||
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';
|
||||
|
||||
class AEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class BEvent extends WorkflowEvent<number> {
|
||||
constructor(data: number) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
class ResultEvent extends WorkflowEvent<string> {
|
||||
constructor(data: string) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
|
||||
const workflow = new Workflow<never, string, string>();
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [AEvent, BEvent]
|
||||
}, async (
|
||||
context,
|
||||
startEvent
|
||||
) => {
|
||||
const input = startEvent.data;
|
||||
if (Math.random() > 0.5) {
|
||||
return new AEvent(`Hello, ${input}!`);
|
||||
} else {
|
||||
return new BEvent(42);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This step will return either an `AEvent` or a `BEvent` based on a random number.
|
||||
@@ -1,196 +0,0 @@
|
||||
---
|
||||
title: Basic Usage
|
||||
description: Learn how to use the LlamaIndex workflow.
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
Workflows are designed for any cases that benefit from event-driven programming, not only for LLM and AI tasks.
|
||||
|
||||
```package-install
|
||||
npm i @llamaindex/workflow
|
||||
```
|
||||
|
||||
## 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>();
|
||||
// ^?
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
First, we define a workflow with 3 generic types: `ContextData`, `Input`, and `Output`.
|
||||
|
||||
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.
|
||||
|
||||
In you code logic, you should **share state between steps via `ContextData`**.
|
||||
|
||||
```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>();
|
||||
// ---cut---
|
||||
workflow.addStep({
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>]
|
||||
}, async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
context.data.counter++;
|
||||
return new StopEvent(`Hello, ${input}!`);
|
||||
});
|
||||
```
|
||||
|
||||
In the workflow, we add a step that listens to `StartEvent<string>` and emits `StopEvent<string>`.
|
||||
|
||||
The step is an async function that takes two arguments: `context` and `event`.
|
||||
|
||||
### `context` type
|
||||
|
||||
<AutoTypeTable path="./src/deps/type.ts" name="HandlerContext" />
|
||||
|
||||
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}!`);
|
||||
});
|
||||
|
||||
// ---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!
|
||||
}
|
||||
```
|
||||
|
||||
Context is shared between runs, so the counter will be increased.
|
||||
|
||||
Ideally, it should be serializable to make sure it can be recovered from HTTP requests or other storage.
|
||||
|
||||
### Full example
|
||||
|
||||
<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,6 +0,0 @@
|
||||
{
|
||||
"title": "Workflow",
|
||||
"description": "See how to use @llamaindex/workflow",
|
||||
"defaultOpen": false,
|
||||
"pages": ["index", "different-inputs-outputs", "streaming"]
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
---
|
||||
title: Streaming
|
||||
description: Learn how to use the LlamaIndex workflow with streaming.
|
||||
---
|
||||
|
||||
`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.
|
||||
|
||||
Each `workflow.run` call returns `WorkflowContext`, which implements `AsyncIterable` interface. You can use it to stream data.
|
||||
|
||||
```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);
|
||||
}
|
||||
}
|
||||
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
```
|
||||
|
||||
We define a parallel computation workflow that computes the sum of numbers from 0 to `total`.
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can define more custom logic using `AsyncIterable` interface.
|
||||
|
||||
For example. I just want to stop the workflow if I get a `ComputeResultEvent`
|
||||
|
||||
|
||||
```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---
|
||||
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();
|
||||
```
|
||||
|
||||
### Streaming with UI
|
||||
|
||||
You can use the `Workflow` API with UI libraries like React.
|
||||
|
||||
```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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type ContextData = {
|
||||
sum: number;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
// ---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;
|
||||
}
|
||||
```
|
||||
|
||||
<WorkflowStreamingDemo />
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"pages": ["llamaindex", "llamaflow", "cloud", "api"]
|
||||
"pages": ["llamaindex", "api"]
|
||||
}
|
||||
|
||||
@@ -3,11 +3,17 @@
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": [
|
||||
"node_modules/@llama-flow/docs/**",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.mdx",
|
||||
"src/**/*.md"
|
||||
],
|
||||
"outputs": [
|
||||
".next",
|
||||
".source",
|
||||
"next-env.d.ts",
|
||||
"src/content/docs/cloud/api/**",
|
||||
"src/content/docs/api/**"
|
||||
],
|
||||
"env": [
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
"plugin": ["typedoc-plugin-markdown", "typedoc-plugin-merge-modules"],
|
||||
"entryPoints": [
|
||||
"../../packages/{,**/}index.ts",
|
||||
"../../packages/readers/src/*.ts",
|
||||
"../../packages/cloud/src/{reader,utils}.ts"
|
||||
"../../packages/readers/src/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"../../packages/autotool/**/src/index.ts",
|
||||
"../../packages/cloud/src/client/index.ts",
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/test/**",
|
||||
|
||||
+3
-4
@@ -21,12 +21,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.5",
|
||||
"@eslint/js": "^9.25.0",
|
||||
"bunchee": "6.4.0",
|
||||
"eslint": "9.22.0",
|
||||
"eslint-config-next": "^15.1.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-turbo": "^2.3.3",
|
||||
"eslint-plugin-react": "7.37.2",
|
||||
"eslint-plugin-turbo": "^2.5.0",
|
||||
"globals": "^15.12.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.2.11",
|
||||
@@ -39,7 +41,7 @@
|
||||
"typescript-eslint": "^8.18.0",
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@9.12.3",
|
||||
"packageManager": "pnpm@10.8.1",
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
@@ -48,8 +50,5 @@
|
||||
"*.{json,md,yml}": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"p-retry": "^6.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,5 +63,8 @@
|
||||
"peerDependencies": {
|
||||
"@llamaindex/core": "workspace:*",
|
||||
"@llamaindex/env": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-retry": "^6.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+26
-30
@@ -7,14 +7,13 @@ settings:
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
p-retry:
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1
|
||||
devDependencies:
|
||||
'@changesets/cli':
|
||||
specifier: ^2.27.5
|
||||
version: 2.29.1
|
||||
'@eslint/js':
|
||||
specifier: ^9.25.0
|
||||
version: 9.25.0
|
||||
bunchee:
|
||||
specifier: 6.4.0
|
||||
version: 6.4.0(typescript@5.7.3)
|
||||
@@ -33,6 +32,9 @@ importers:
|
||||
eslint-plugin-react:
|
||||
specifier: 7.37.2
|
||||
version: 7.37.2(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-turbo:
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0(eslint@9.22.0(jiti@2.4.2))(turbo@2.5.0)
|
||||
globals:
|
||||
specifier: ^15.12.0
|
||||
version: 15.15.0
|
||||
@@ -76,8 +78,8 @@ importers:
|
||||
specifier: ^10.1.0
|
||||
version: 10.2.0(react@19.1.0)
|
||||
'@llama-flow/docs':
|
||||
specifier: 0.0.3
|
||||
version: 0.0.3
|
||||
specifier: 0.0.5
|
||||
version: 0.0.5
|
||||
'@llamaindex/chat-ui':
|
||||
specifier: 0.2.0
|
||||
version: 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)
|
||||
@@ -875,6 +877,10 @@ importers:
|
||||
version: 4.19.3
|
||||
|
||||
packages/cloud:
|
||||
dependencies:
|
||||
p-retry:
|
||||
specifier: ^6.2.1
|
||||
version: 6.2.1
|
||||
devDependencies:
|
||||
'@hey-api/client-fetch':
|
||||
specifier: ^0.6.0
|
||||
@@ -3296,6 +3302,10 @@ packages:
|
||||
resolution: {integrity: sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.25.0':
|
||||
resolution: {integrity: sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/object-schema@2.1.6':
|
||||
resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
@@ -3818,8 +3828,8 @@ packages:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@llama-flow/docs@0.0.3':
|
||||
resolution: {integrity: sha512-5BFSbaWY7Ps5djzXilgyy9t7OYElyoojvEmLy/FC1azUnn6poIxvr04Ctsoi0PR44sYyqWb7hkuU5iJap6uLyA==}
|
||||
'@llama-flow/docs@0.0.5':
|
||||
resolution: {integrity: sha512-iAEqqWgPnJNxm4syNSxudDE1aHYNi1eTrxHg+FjcPeZhxyksLYRzpmzUikcZv0uhxgLwRinF7UPTnH9ioKlaEw==}
|
||||
|
||||
'@llamaindex/chat-ui@0.2.0':
|
||||
resolution: {integrity: sha512-9U5+9l2UVBaOG8fSuMjnere5R2QSNxCEcixMwBgt4L4b0evo8jU4ZzlSxLPunWfpn1PWFVMUwKLlSSwa1qTTyA==}
|
||||
@@ -15390,6 +15400,8 @@ snapshots:
|
||||
|
||||
'@eslint/js@9.22.0': {}
|
||||
|
||||
'@eslint/js@9.25.0': {}
|
||||
|
||||
'@eslint/object-schema@2.1.6': {}
|
||||
|
||||
'@eslint/plugin-kit@0.2.7':
|
||||
@@ -15914,7 +15926,7 @@ snapshots:
|
||||
p-retry: 6.2.1
|
||||
zod: 3.24.2
|
||||
|
||||
'@llama-flow/docs@0.0.3': {}
|
||||
'@llama-flow/docs@0.0.5': {}
|
||||
|
||||
'@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)':
|
||||
dependencies:
|
||||
@@ -20761,7 +20773,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(@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-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.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.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))
|
||||
@@ -20791,22 +20803,6 @@ 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
|
||||
@@ -20835,7 +20831,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.7.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.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -20861,14 +20857,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-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)):
|
||||
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)):
|
||||
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(@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-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -20941,7 +20937,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-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))
|
||||
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))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
|
||||
Reference in New Issue
Block a user