mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-01 22:14:03 -04:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa66c9ca8e | |||
| 3ee8c83200 | |||
| e919bab568 | |||
| d28b6b7c4f | |||
| 1c7a262ff7 | |||
| 5a1838cc91 | |||
| b9805f4899 | |||
| 109ec63779 | |||
| 82d4b46fe4 | |||
| f8c2d0b8ad | |||
| 6d7bc4ccbb | |||
| 294f502441 | |||
| 056594452c | |||
| 1e59695cef | |||
| f463efd8a5 | |||
| cf95af40d9 | |||
| ddc910dc73 | |||
| f12af27760 | |||
| ffdbc8f5e8 | |||
| ea8817f7e4 | |||
| 359698d04b | |||
| b49fb24948 | |||
| 78841495aa | |||
| c81dd21472 | |||
| 52868ea0f9 | |||
| e0a730e44e | |||
| eda486bb52 | |||
| 10d9c708db | |||
| 556027705e | |||
| 588cd0f0b9 | |||
| 7ca9ddff86 | |||
| 3310eaae29 | |||
| 96dac4ddfd | |||
| f9ee683593 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@llamaindex/openai": patch
|
||||
---
|
||||
|
||||
Update o4-mini to accept reasoning parameters and exclude temperature
|
||||
@@ -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
|
||||
|
||||
@@ -7,3 +7,4 @@ dist/
|
||||
.source/
|
||||
# prttier doesn't support mdx3 we are using
|
||||
*.mdx
|
||||
packages/server/server/
|
||||
@@ -1,5 +1,33 @@
|
||||
# @llamaindex/doc
|
||||
|
||||
## 0.2.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
- llamaindex@0.10.3
|
||||
- @llamaindex/openai@0.3.5
|
||||
- @llamaindex/cloud@4.0.4
|
||||
- @llamaindex/node-parser@2.0.3
|
||||
- @llamaindex/readers@3.1.1
|
||||
- @llamaindex/workflow@1.0.4
|
||||
|
||||
## 0.2.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1e59695]
|
||||
- @llamaindex/readers@3.1.0
|
||||
|
||||
## 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.15",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "fumadocs-mdx",
|
||||
@@ -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:*",
|
||||
@@ -90,9 +90,9 @@
|
||||
"remark-stringify": "^11.0.0",
|
||||
"tailwindcss": "^4.0.9",
|
||||
"tsx": "^4.19.3",
|
||||
"typedoc": "0.28.2",
|
||||
"typedoc": "0.28.3",
|
||||
"typedoc-plugin-markdown": "^4.6.2",
|
||||
"typedoc-plugin-merge-modules": "^7.0.0",
|
||||
"typedoc-plugin-merge-modules": " ^7.0.0",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { generateFiles as openapiGenerateFiles } from "fumadocs-openapi";
|
||||
import {
|
||||
createGenerator,
|
||||
generateFiles as typescriptGenerateFiles,
|
||||
@@ -14,18 +13,12 @@ const apiRefOut = "./src/content/docs/api";
|
||||
// clean generated files
|
||||
rimrafSync(out, {
|
||||
filter(v) {
|
||||
return !v.endsWith("index.mdx") && !v.endsWith("meta.json");
|
||||
return !v.endsWith("index.md") && !v.endsWith("meta.json");
|
||||
},
|
||||
});
|
||||
|
||||
void openapiGenerateFiles({
|
||||
input: ["../../packages/cloud/openapi.json"],
|
||||
output: "./src/content/docs/cloud/api",
|
||||
groupBy: "tag",
|
||||
});
|
||||
|
||||
void typescriptGenerateFiles(generator, {
|
||||
input: ["./src/content/docs/api/**/*.mdx"],
|
||||
input: ["./src/content/docs/api/**/*.md"],
|
||||
output: (file) => path.resolve(path.dirname(file), path.basename(file)),
|
||||
transformOutput,
|
||||
});
|
||||
@@ -34,19 +27,22 @@ function transformOutput(filePath: string, content: string) {
|
||||
const fileName = path.basename(filePath);
|
||||
let title = fileName.split(".")[0];
|
||||
if (title === "index") title = "LlamaIndex API Reference";
|
||||
return `---\ntitle: ${title}\n---\n\n${transformAbsoluteUrl(content, filePath)}`;
|
||||
return `---\ntitle: ${title}\n---\n\n${transformAbsoluteUrl(
|
||||
content.replace(/(?<!\\)\{([^}]+)(?<!\\)}/g, "\\{$1\\}"),
|
||||
filePath,
|
||||
)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the content by converting relative MDX links to absolute docs API links
|
||||
* Example: [text](../type-aliases/TaskHandler.mdx) -> [text](/docs/api/type-aliases/TaskHandler)
|
||||
* [text](BaseChatEngine.mdx) -> [text](/docs/api/classes/BaseChatEngine)
|
||||
* [text](BaseVectorStore.mdx#constructors) -> [text](/docs/api/classes/BaseVectorStore#constructors)
|
||||
* [text](TaskStep.mdx) -> [text](/docs/api/type-aliases/TaskStep)
|
||||
* Transforms the content by converting relative MD links to absolute docs API links
|
||||
* Example: [text](../type-aliases/TaskHandler.md) -> [text](/docs/api/type-aliases/TaskHandler)
|
||||
* [text](BaseChatEngine.md) -> [text](/docs/api/classes/BaseChatEngine)
|
||||
* [text](BaseVectorStore.md#constructors) -> [text](/docs/api/classes/BaseVectorStore#constructors)
|
||||
* [text](TaskStep.md) -> [text](/docs/api/type-aliases/TaskStep)
|
||||
*/
|
||||
function transformAbsoluteUrl(content: string, filePath: string) {
|
||||
const group = path.dirname(filePath).split(path.sep).pop();
|
||||
return content.replace(/\]\(([^)]+)\.mdx([^)]*)\)/g, (_, slug, anchor) => {
|
||||
return content.replace(/\]\(([^)]+)\.md([^)]*)\)/g, (_, slug, anchor) => {
|
||||
const slugParts = slug.split("/");
|
||||
const fileName = slugParts[slugParts.length - 1];
|
||||
const fileGroup = slugParts[slugParts.length - 2] ?? group;
|
||||
|
||||
@@ -28,14 +28,14 @@ interface RelativeLinkResult {
|
||||
* Get all valid documentation routes from the content directory
|
||||
*/
|
||||
async function getValidRoutes(): Promise<Set<string>> {
|
||||
const mdxFiles = await glob("**/*.mdx", { cwd: CONTENT_DIR });
|
||||
const mdxFiles = await glob("**/*.mdx?", { cwd: CONTENT_DIR });
|
||||
|
||||
const routes = new Set<string>();
|
||||
|
||||
// Add each MDX file as a valid route
|
||||
for (const file of mdxFiles) {
|
||||
// Remove .mdx extension and normalize to route format
|
||||
let route = file.replace(/\.mdx$/, "");
|
||||
let route = file.replace(/\.mdx?$/, "");
|
||||
|
||||
// Handle index files
|
||||
if (route.endsWith("/index")) {
|
||||
@@ -131,7 +131,7 @@ function findRelativeLinksInFile(
|
||||
* Find relative links in all MDX files
|
||||
*/
|
||||
async function findRelativeLinks(): Promise<RelativeLinkResult[]> {
|
||||
const mdxFiles = await glob("**/*.mdx", { cwd: CONTENT_DIR });
|
||||
const mdxFiles = await glob("**/*.mdx?", { cwd: CONTENT_DIR });
|
||||
const results: RelativeLinkResult[] = [];
|
||||
|
||||
for (const file of mdxFiles) {
|
||||
@@ -150,7 +150,7 @@ async function findRelativeLinks(): Promise<RelativeLinkResult[]> {
|
||||
}
|
||||
|
||||
async function validateLinks(): Promise<LinkValidationResult[]> {
|
||||
const mdxFiles = await glob("**/*.mdx", { cwd: CONTENT_DIR });
|
||||
const mdxFiles = await glob("**/*.mdx?", { cwd: CONTENT_DIR });
|
||||
const validRoutes = await getValidRoutes();
|
||||
|
||||
const results: LinkValidationResult[] = [];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }) {
|
||||
@@ -13,23 +9,9 @@ export default function Layout({ children }: { children: ReactNode }) {
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
{...baseOptions}
|
||||
links={[]}
|
||||
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}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,8 +11,3 @@ export const CodeNodeParserDemo = dynamic(() =>
|
||||
(mod) => mod.CodeNodeParserDemo,
|
||||
),
|
||||
);
|
||||
export const WorkflowStreamingDemo = dynamic(() =>
|
||||
import("@/components/demo/workflow-streaming-ui").then(
|
||||
(mod) => mod.WorkflowStreamingDemo,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
"use client";
|
||||
import FlowInput from "@/components/flow-input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
StartEvent,
|
||||
StopEvent,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "@llamaindex/workflow";
|
||||
import { ReactNode, startTransition, useState } from "react";
|
||||
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
||||
|
||||
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 max = 1000;
|
||||
const min = 100;
|
||||
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<number>],
|
||||
outputs: [StopEvent<number>],
|
||||
},
|
||||
async (context, event) => {
|
||||
const total = event.data;
|
||||
for (let i = 0; i < total; i++) {
|
||||
context.sendEvent(new ComputeEvent(i));
|
||||
}
|
||||
console.log("waiting");
|
||||
const computeResults = await Promise.all(
|
||||
Array.from({ length: total }).map(() =>
|
||||
context.requireEvent(ComputeResultEvent),
|
||||
),
|
||||
);
|
||||
context.data.sum = computeResults.reduce(
|
||||
(acc, result) => acc + result.data,
|
||||
0,
|
||||
);
|
||||
console.log("stop");
|
||||
return new StopEvent(context.data.sum);
|
||||
},
|
||||
);
|
||||
|
||||
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);
|
||||
},
|
||||
);
|
||||
|
||||
function ScrollToBottom() {
|
||||
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
||||
|
||||
return (
|
||||
!isAtBottom && (
|
||||
<button
|
||||
className="i-ph-arrow-circle-down-fill absolute bottom-0 left-[50%] translate-x-[-50%] rounded-lg text-4xl"
|
||||
onClick={() => scrollToBottom()}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkflowStreamingDemo() {
|
||||
const [ui, setUI] = useState<ReactNode[]>([
|
||||
<div key={0} className="bg-gray-100 dark:bg-gray-800">
|
||||
Waiting for workflow to start
|
||||
</div>,
|
||||
]);
|
||||
const [total, setTotal] = useState<number>(10);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-start gap-2">
|
||||
<div className="flex flex-row items-center justify-center">
|
||||
<div className="mr-2 text-lg">Compute total</div>{" "}
|
||||
<FlowInput value={total} onChange={(value) => setTotal(value)} />
|
||||
</div>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
startTransition(() => {
|
||||
setUI([]);
|
||||
});
|
||||
const context = workflow.run(total, {
|
||||
sum: 0,
|
||||
});
|
||||
let i = 0;
|
||||
for await (const event of context) {
|
||||
console.log(event);
|
||||
if (event instanceof ComputeEvent) {
|
||||
setUI((ui) => [
|
||||
...ui,
|
||||
<div key={i++} className="bg-yellow-100 dark:bg-yellow-800">
|
||||
Computing task id: {event.data}
|
||||
</div>,
|
||||
]);
|
||||
} else if (event instanceof ComputeResultEvent) {
|
||||
setUI((ui) => [
|
||||
...ui,
|
||||
<div key={i++} className="bg-green-100 dark:bg-green-800">
|
||||
Computed task id: {event.data}
|
||||
</div>,
|
||||
]);
|
||||
} else if (event instanceof StartEvent) {
|
||||
setUI((ui) => [
|
||||
...ui,
|
||||
<div key={i++} className="bg-blue-100 dark:bg-blue-800">
|
||||
Started workflow with total {event.data}
|
||||
</div>,
|
||||
]);
|
||||
} else if (event instanceof StopEvent) {
|
||||
setUI((ui) => [
|
||||
...ui,
|
||||
<div key={i++} className="bg-red-100 dark:bg-red-800">
|
||||
Workflow stopped
|
||||
</div>,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
Start Workflow
|
||||
</Button>
|
||||
<StickToBottom className="flex max-h-96 w-full flex-col gap-2 overflow-y-auto rounded-lg border border-gray-200 p-2">
|
||||
<StickToBottom.Content className="flex flex-col gap-2">
|
||||
{ui}
|
||||
</StickToBottom.Content>
|
||||
<ScrollToBottom />
|
||||
</StickToBottom>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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", "..."]
|
||||
}
|
||||
@@ -57,6 +57,41 @@ const researchAgent = agent({
|
||||
});
|
||||
```
|
||||
|
||||
## MCP tools
|
||||
|
||||
If you have a MCP server running, you can fetch tools from the server and use them in your agents.
|
||||
|
||||
```ts
|
||||
// 1. Import MCP tools adapter
|
||||
import { mcp } from "@llamaindex/tools";
|
||||
import { agent } from "llamaindex";
|
||||
|
||||
// 2. Initialize a MCP client
|
||||
// by npx
|
||||
const server = mcp({
|
||||
command: "npx",
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
verbose: true,
|
||||
});
|
||||
// or by SSE
|
||||
const server = mcp({
|
||||
url: "http://localhost:8000/mcp",
|
||||
verbose: true,
|
||||
});
|
||||
|
||||
// 3. Get tools from MCP server
|
||||
const tools = await server.tools();
|
||||
|
||||
// Now you can create an agent with the tools
|
||||
const agent = agent({
|
||||
name: "My Agent",
|
||||
systemPrompt: "You are a helpful assistant that can use the provided tools to answer questions.",
|
||||
llm: openai({ model: "gpt-4o" }),
|
||||
tools: tools,
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Function tool
|
||||
|
||||
You can still use the `FunctionTool` class to define a tool.
|
||||
|
||||
@@ -2,149 +2,260 @@
|
||||
title: Workflows
|
||||
---
|
||||
|
||||
A `Workflow` in LlamaIndexTS 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 in LlamaIndexTS work by defining step functions that handle specific event types and emit new events.
|
||||
|
||||
When a step function is added to a workflow, you need to specify the input and optionally the output event types (used for validation). The specification of the input events ensures each step only runs when an accepted event is ready.
|
||||
|
||||
You can create a `Workflow` to do anything! Build an agent, a RAG flow, an extraction flow, or anything else you want.
|
||||
A `Workflow` in LlamaIndex is a lightweight, event-driven abstraction used to chain together several events. Workflows are made up of `handlers`, with each one responsible for processing specific event types and emitting new events.
|
||||
|
||||
Workflows are designed to be flexible and can be used to build agents, RAG flows, extraction flows, or anything else you want to implement.
|
||||
|
||||
```package-install
|
||||
npm i @llamaindex/workflow
|
||||
npm i @llama-flow/core @llamaindex/openai
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
As an illustrative example, let's consider a naive workflow where a joke is generated and then critiqued.
|
||||
Let's explore a simple workflow example where a joke is generated and then critiqued and iterated on:
|
||||
|
||||
<include cwd>../../examples/workflow/joke.ts</include>
|
||||
```typescript
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { createWorkflow, workflowEvent } from "@llama-flow/core";
|
||||
import { withStore } from "@llama-flow/core/middleware/store";
|
||||
|
||||
There's a few moving pieces here, so let's go through this piece by piece.
|
||||
// Create LLM instance
|
||||
const llm = new OpenAI({ model: "gpt-4.1-mini", apiKey: "..."});
|
||||
|
||||
// Define our workflow events
|
||||
const startEvent = workflowEvent<string>(); // Input topic for joke
|
||||
const jokeEvent = workflowEvent<{ joke: string }>(); // Intermediate joke
|
||||
const critiqueEvent = workflowEvent<{ joke: string, critique: string }>(); // Intermediate critique
|
||||
const resultEvent = workflowEvent<{ joke: string, critique: string }>(); // Final joke + critique
|
||||
|
||||
// Create our workflow
|
||||
const jokeFlow = withStore(
|
||||
() => ({
|
||||
numIterations: 0,
|
||||
maxIterations: 3,
|
||||
}),
|
||||
createWorkflow()
|
||||
);
|
||||
|
||||
// Define handlers for each step
|
||||
jokeFlow.handle([startEvent], async (event) => {
|
||||
// Prompt the LLM to write a joke
|
||||
const prompt = `Write your best joke about ${event.data}. Write the joke between <joke> and </joke> tags.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke = response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ?? response.text;
|
||||
return jokeEvent.with({ joke: joke });
|
||||
});
|
||||
|
||||
jokeFlow.handle([jokeEvent], async (event) => {
|
||||
// Prompt the LLM to critique the joke
|
||||
const prompt = `Give a thorough critique of the following joke. If the joke needs improvement, put "IMPROVE" somewhere in the critique: ${event.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// If the critique includes "IMPROVE", keep iterating, else, return the result
|
||||
if (response.text.includes("IMPROVE")) {
|
||||
return critiqueEvent.with({ joke: event.data.joke, critique: response.text });
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: event.data.joke, critique: response.text });
|
||||
});
|
||||
|
||||
jokeFlow.handle([critiqueEvent], async (event) => {
|
||||
// Keep track of the number of iterations
|
||||
const store = jokeFlow.getStore();
|
||||
store.numIterations++;
|
||||
|
||||
// Write a new joke based on the previous joke and critique
|
||||
const prompt = `Write a new joke based on the following critique and the original joke. Write the joke between <joke> and </joke> tags.\n\nJoke: ${event.data.joke}\n\nCritique: ${event.data.critique}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke = response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ?? response.text;
|
||||
|
||||
// If we've done less than the max number of iterations, keep iterating
|
||||
// else, return the result
|
||||
if (store.numIterations < store.maxIterations) {
|
||||
return jokeEvent.with({ joke: joke });
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: joke, critique: event.data.critique });
|
||||
});
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const { stream, sendEvent } = jokeFlow.createContext();
|
||||
sendEvent(startEvent.with("pirates"));
|
||||
|
||||
let result: { joke: string, critique: string } | undefined;
|
||||
|
||||
for await (const event of stream) {
|
||||
// console.log(event.data); optionally log the event data
|
||||
if (resultEvent.include(event)) {
|
||||
result = event.data;
|
||||
break; // Stop when we get the final result
|
||||
}
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
```
|
||||
|
||||
There are a few moving pieces here, so let's go through this step by step.
|
||||
|
||||
### Defining Workflow Events
|
||||
|
||||
```typescript
|
||||
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
|
||||
const startEvent = workflowEvent<string>(); // Input topic for joke
|
||||
const jokeEvent = workflowEvent<{ joke: string }>(); // Intermediate joke
|
||||
const critiqueEvent = workflowEvent<{ joke: string, critique: string }>(); // Intermediate critique
|
||||
const resultEvent = workflowEvent<{ joke: string, critique: string }>(); // Final joke + critique
|
||||
```
|
||||
|
||||
Events are user-defined classes that extend `WorkflowEvent` and contain arbitrary data provided as template argument. In this case, our workflow relies on a single user-defined event, the `JokeEvent` with a `joke` attribute of type `string`.
|
||||
Events are defined using the `workflowEvent` function and contain arbitrary data provided as a generic type. In this example, we have four events:
|
||||
- `startEvent`: Takes a string input (the joke topic)
|
||||
- `jokeEvent`: Contains an object with a joke property
|
||||
- `critiqueEvent`: Contains both the joke and its critique, used for the feedback loop
|
||||
- `resultEvent`: Contains the final joke and critique after any iterations
|
||||
|
||||
### Setting up the Workflow Class
|
||||
### Setting up the Workflow with Store Middleware
|
||||
|
||||
```typescript
|
||||
const llm = new OpenAI();
|
||||
...
|
||||
const jokeFlow = new Workflow<unknown, string, string>();
|
||||
```
|
||||
|
||||
Our workflow is implemented by initiating the `Workflow` class with three generic types: the context type (unknown), input type (string), and output type (string). The context type is `unknown`, as we're not using a shared context in this example.
|
||||
|
||||
For simplicity, we created an `OpenAI` llm instance that we're using for inference in our workflow.
|
||||
|
||||
### Workflow Entry Points
|
||||
|
||||
```typescript
|
||||
const generateJoke = async (_: unknown, ev: StartEvent<string>) => {
|
||||
const prompt = `Write your best joke about ${ev.data}.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new JokeEvent({ joke: response.text });
|
||||
};
|
||||
```
|
||||
|
||||
Here, we come to the entry-point of our workflow. While events are user-defined, there are two special-case events, the `StartEvent` and the `StopEvent`. These events are predefined, but we can specify the payload type using generic types. We're using `StartEvent<string>` to indicate that we're going to send an input of type string.
|
||||
|
||||
To add this step to the workflow, we use the `addStep` method with an object specifying the input and output event types:
|
||||
|
||||
```typescript
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [JokeEvent],
|
||||
},
|
||||
generateJoke
|
||||
const jokeFlow = withStore(
|
||||
() => ({
|
||||
numIterations: 0,
|
||||
maxIterations: 3,
|
||||
}),
|
||||
createWorkflow()
|
||||
);
|
||||
```
|
||||
|
||||
### Workflow Exit Points
|
||||
Our workflow is implemented using the `createWorkflow()` function, enhanced with the `withStore` middleware. The store provides shared state across all handlers, which in this case tracks:
|
||||
- `numIterations`: Counts how many iterations of joke improvement we've done
|
||||
- `maxIterations`: Sets a limit to prevent infinite loops
|
||||
|
||||
This store will be accesible within workflows by using the `jokeFlow.getStore()` function.
|
||||
|
||||
### Adding Handlers with Loops
|
||||
|
||||
We have three key handlers in our workflow:
|
||||
|
||||
1. The first handler processes the `startEvent`, generates an initial joke, and emits a `jokeEvent`:
|
||||
|
||||
```typescript
|
||||
const critiqueJoke = async (_: unknown, ev: JokeEvent) => {
|
||||
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
|
||||
jokeFlow.handle([startEvent], async (event) => {
|
||||
// Prompt the LLM to write a joke
|
||||
const prompt = `Write your best joke about ${event.data}. Write the joke between <joke> and </joke> tags.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent(response.text);
|
||||
};
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke = response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ?? response.text;
|
||||
return jokeEvent.with({ joke: joke });
|
||||
});
|
||||
```
|
||||
|
||||
Here, we have our second and last step in the workflow. We know it's the last step because the special `StopEvent` is returned. When the workflow encounters a returned `StopEvent`, it immediately stops the workflow and returns the result. Note that we're using the generic type `StopEvent<string>` to indicate that we're returning a string.
|
||||
|
||||
Add this step to the workflow:
|
||||
2. The second handler handles the `jokeEvent`, critiques the joke, and either:
|
||||
- Emits a `critiqueEvent` if the joke needs improvement
|
||||
- Emits a `resultEvent` if the joke is good enough
|
||||
|
||||
```typescript
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
critiqueJoke
|
||||
);
|
||||
jokeFlow.handle([jokeEvent], async (event) => {
|
||||
// Prompt the LLM to critique the joke
|
||||
const prompt = `Give a thorough critique of the following joke. If the joke needs improvement, put "IMPROVE" somewhere in the critique: ${event.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// If the critique includes "IMPROVE", keep iterating, else, return the result
|
||||
if (response.text.includes("IMPROVE")) {
|
||||
return critiqueEvent.with({ joke: event.data.joke, critique: response.text });
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: event.data.joke, critique: response.text });
|
||||
});
|
||||
```
|
||||
|
||||
3. The third handler processes the `critiqueEvent`, generates an improved joke based on the critique, and either:
|
||||
- Loops back to the joke evaluation (if under the iteration limit)
|
||||
- Emits the final `resultEvent` (if iteration limit reached)
|
||||
|
||||
```typescript
|
||||
jokeFlow.handle([critiqueEvent], async (event) => {
|
||||
// Keep track of the number of iterations
|
||||
const store = jokeFlow.getStore();
|
||||
store.numIterations++;
|
||||
|
||||
// Write a new joke based on the previous joke and critique
|
||||
const prompt = `Write a new joke based on the following critique and the original joke. Write the joke between <joke> and </joke> tags.\n\nJoke: ${event.data.joke}\n\nCritique: ${event.data.critique}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
|
||||
// Parse the joke from the response
|
||||
const joke = response.text.match(/<joke>([\s\S]*?)<\/joke>/)?.[1]?.trim() ?? response.text;
|
||||
|
||||
// If we've done less than the max number of iterations, keep iterating
|
||||
// else, return the result
|
||||
if (store.numIterations < store.maxIterations) {
|
||||
return jokeEvent.with({ joke: joke });
|
||||
}
|
||||
|
||||
return resultEvent.with({ joke: joke, critique: event.data.critique });
|
||||
});
|
||||
```
|
||||
|
||||
### Running the Workflow
|
||||
|
||||
```typescript
|
||||
const result = await jokeFlow.run("pirates");
|
||||
console.log(result.data.result);
|
||||
```
|
||||
async function main() {
|
||||
const { stream, sendEvent } = jokeFlow.createContext();
|
||||
sendEvent(startEvent.with("pirates"));
|
||||
|
||||
Lastly, we run the workflow. The `.run()` method is async, so we use await here to wait for the result.
|
||||
let result: { joke: string, critique: string } | undefined;
|
||||
|
||||
## Working with Shared Context/State
|
||||
|
||||
Optionally, you can choose to use a shared context between steps by specifying a context type when creating the workflow. Here's an example where multiple steps access a shared state:
|
||||
|
||||
```typescript
|
||||
import { HandlerContext } from "llamaindex";
|
||||
|
||||
type MyContextData = {
|
||||
query: string;
|
||||
intermediateResults: any[];
|
||||
for await (const event of stream) {
|
||||
// console.log(event.data); optionally log the event data
|
||||
if (resultEvent.include(event)) {
|
||||
result = event.data;
|
||||
break; // Stop when we get the final result
|
||||
}
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
const query = async (context: HandlerContext<MyContextData>, ev: MyEvent) => {
|
||||
// get the query from the context
|
||||
const query = context.data.query;
|
||||
// do something with context and event
|
||||
const val = ...
|
||||
// store in context
|
||||
context.data.intermediateResults.push(val);
|
||||
|
||||
return new StopEvent({ result });
|
||||
};
|
||||
```
|
||||
|
||||
## Waiting for Multiple Events
|
||||
To run the workflow, we:
|
||||
1. Create a workflow context with `createContext()`
|
||||
2. Trigger the initial event with `sendEvent()`
|
||||
3. Listen to the event stream and process events as they arrive
|
||||
4. Use `include()` to check if an event is of a specific type
|
||||
5. Break the loop when we receive our final result
|
||||
|
||||
The context does more than just hold data, it also provides utilities to buffer and wait for multiple events.
|
||||
### Using Stream Utilities
|
||||
|
||||
For example, you might have a step that waits for a query and retrieved nodes before synthesizing a response:
|
||||
Workflows provide utility functions to make working with event streams easier:
|
||||
|
||||
```typescript
|
||||
const synthesize = async (context: Context, ev1: QueryEvent, ev2: RetrieveEvent) => {
|
||||
const subPrompts = [`Answer this query using the context provided: ${ev1.data.query}`, `Context: ${ev2.data.context}`];
|
||||
const prompt = subPrompts.join("\n");
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent({ result: response.text });
|
||||
};
|
||||
import { collect } from "@llama-flow/core/stream/consumer";
|
||||
import { until } from "@llama-flow/core/stream/until";
|
||||
|
||||
// Create a workflow context and send the initial event
|
||||
const { stream, sendEvent } = jokeFlow.createContext();
|
||||
sendEvent(startEvent.with("pirates"));
|
||||
|
||||
// Collect all events until we get a resultEvent
|
||||
const allEvents = await collect(until(stream, resultEvent));
|
||||
|
||||
// The last event will be the resultEvent
|
||||
const finalEvent = allEvents[allEvents.length - 1];
|
||||
console.log(finalEvent.data); // Output the joke and critique
|
||||
```
|
||||
|
||||
Passing multiple events, we can buffer and wait for ALL expected events to arrive. The receiving step function will only be called once all events have arrived.
|
||||
The stream utilities make it easier to work with the asynchronous event flow. In this example, we use:
|
||||
- `collect`: Aggregates all events into an array
|
||||
- `until`: Creates a stream that emits events until a condition is met (in this case, until a resultEvent is received)
|
||||
|
||||
## Manually Triggering Events
|
||||
You can combine these utilities with other stream operators like `filter` and `map` to create powerful processing pipelines.
|
||||
|
||||
Normally, events are triggered by returning another event during a step. However, events can also be manually dispatched using the `ctx.sendEvent(event)` method within a workflow.
|
||||
## Next Steps
|
||||
|
||||
## Examples
|
||||
|
||||
You can find many useful examples of using workflows in the [examples folder](https://github.com/run-llama/LlamaIndexTS/blob/main/examples/workflow).
|
||||
To learn more about workflows, check out [the documentation in the tutorial section](../../../llamaflow).
|
||||
|
||||
@@ -5,6 +5,12 @@ title: DiscordReader
|
||||
DiscordReader is a simple data loader that reads all messages in a given Discord channel and returns them as Document objects.
|
||||
It uses the [@discordjs/rest](https://github.com/discordjs/discord.js/tree/main/packages/rest) library to fetch the messages.
|
||||
|
||||
## Installation
|
||||
|
||||
```package-install
|
||||
npm install @llamaindex/discord
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
First step is to create a Discord Application and generating a bot token [here](https://discord.com/developers/applications).
|
||||
@@ -12,7 +18,7 @@ In your Discord Application, go to the `OAuth2` tab and generate an invite URL b
|
||||
This will invite the bot with the necessary permissions to read messages.
|
||||
Copy the URL in your browser and select the server you want your bot to join.
|
||||
|
||||
<include cwd>../../examples/readers/src/discord.ts</include>
|
||||
<include cwd>../../examples/discord/reader.ts</include>
|
||||
|
||||
### Params
|
||||
|
||||
|
||||
@@ -21,27 +21,18 @@ To install readers call:
|
||||
|
||||
We offer readers for different file formats.
|
||||
|
||||
```ts twoslash
|
||||
import { CSVReader } from '@llamaindex/readers/csv'
|
||||
import { PDFReader } from '@llamaindex/readers/pdf'
|
||||
import { JSONReader } from '@llamaindex/readers/json'
|
||||
import { MarkdownReader } from '@llamaindex/readers/markdown'
|
||||
import { HTMLReader } from '@llamaindex/readers/html'
|
||||
// you can find more readers in the documentation
|
||||
```ts twoslash
|
||||
import { CSVReader } from '@llamaindex/readers/csv';
|
||||
import { DocxReader } from '@llamaindex/readers/docx';
|
||||
import { HTMLReader } from '@llamaindex/readers/html';
|
||||
import { ImageReader } from '@llamaindex/readers/image';
|
||||
import { JSONReader } from '@llamaindex/readers/json';
|
||||
import { MarkdownReader } from '@llamaindex/readers/markdown';
|
||||
import { ObsidianReader } from '@llamaindex/readers/obsidian';
|
||||
import { PDFReader } from '@llamaindex/readers/pdf';
|
||||
import { TextFileReader } from '@llamaindex/readers/text';
|
||||
```
|
||||
|
||||
Additionally the following loaders exist without separate documentation:
|
||||
|
||||
- `AssemblyAIReader` transcribes audio using [AssemblyAI](https://www.assemblyai.com/).
|
||||
- [AudioTranscriptReader](/docs/api/classes/AudioTranscriptReader): loads entire transcript as a single document.
|
||||
- [AudioTranscriptParagraphsReader](/docs/api/classes/AudioTranscriptParagraphsReader): creates a document per paragraph.
|
||||
- [AudioTranscriptSentencesReader](/docs/api/classes/AudioTranscriptSentencesReader): creates a document per sentence.
|
||||
- [AudioSubtitlesReader](/docs/api/classes/AudioTranscriptParagraphsReader): creates a document containing the subtitles of a transcript.
|
||||
- [NotionReader](/docs/api/classes/NotionReader) loads [Notion](https://www.notion.so/) pages.
|
||||
- [SimpleMongoReader](/docs/api/classes/SimpleMongoReader) loads data from a [MongoDB](https://www.mongodb.com/).
|
||||
|
||||
Check the [LlamaIndexTS Github](https://github.com/run-llama/LlamaIndexTS) for the most up to date overview of integrations.
|
||||
|
||||
## SimpleDirectoryReader
|
||||
|
||||
[Open in StackBlitz](https://stackblitz.com/github/run-llama/LlamaIndexTS/tree/main/examples/readers?file=src/simple-directory-reader.ts&title=Simple%20Directory%20Reader)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -58,25 +58,9 @@ We will convert our text into embeddings using the `VectorStoreIndex` class thro
|
||||
const index = await VectorStoreIndex.fromDocuments(documents);
|
||||
```
|
||||
|
||||
### Configure a retriever
|
||||
|
||||
Before LlamaIndex can send a query to the LLM, it needs to find the most relevant chunks to send. That's the purpose of a `Retriever`. We're going to get `VectorStoreIndex` to act as a retriever for us
|
||||
|
||||
```javascript
|
||||
const retriever = await index.asRetriever();
|
||||
```
|
||||
|
||||
### Configure how many documents to retrieve
|
||||
|
||||
By default LlamaIndex will retrieve just the 2 most relevant chunks of text. This document is complex though, so we'll ask for more context.
|
||||
|
||||
```javascript
|
||||
retriever.similarityTopK = 10;
|
||||
```
|
||||
|
||||
### Use index.queryTool
|
||||
|
||||
`index.queryTool` creates a `QueryEngineTool` that can be used be an agent to query data from the index.
|
||||
`index.queryTool` creates a `QueryEngineTool` that can be used be an agent to query data from the index:
|
||||
|
||||
```javascript
|
||||
const tools = [
|
||||
@@ -85,9 +69,17 @@ const tools = [
|
||||
name: "san_francisco_budget_tool",
|
||||
description: `This tool can answer detailed questions about the individual components of the budget of San Francisco in 2023-2024.`,
|
||||
},
|
||||
options: { similarityTopK: 10 },
|
||||
}),
|
||||
];
|
||||
```
|
||||
|
||||
The `metadata` that we're setting helps the agent to decide when to use the tool.
|
||||
Note that by default LlamaIndex will retrieve just the 2 most relevant chunks of text. This document is complex though, so we'll ask for more context by setting `similarityTopK` to 10.
|
||||
|
||||
Now, we can create an agent using the `QueryEngineTool`:
|
||||
|
||||
```javascript
|
||||
// Create an agent using the tools array
|
||||
const ragAgent = agent({ tools });
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const tools = [
|
||||
name: "san_francisco_budget_tool",
|
||||
description: `This tool can answer detailed questions about the individual components of the budget of San Francisco in 2023-2024.`,
|
||||
},
|
||||
options: { similarityTopK: 10 },
|
||||
}),
|
||||
tool({
|
||||
name: "sumNumbers",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"basic_agent",
|
||||
"rag",
|
||||
"agents",
|
||||
"workflow",
|
||||
"../../llamaflow",
|
||||
"local_llm",
|
||||
"chatbot",
|
||||
"structured_data_extraction"
|
||||
|
||||
@@ -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)
|
||||
-234
@@ -1,234 +0,0 @@
|
||||
---
|
||||
title: Inputs / Outputs (Outdated)
|
||||
description: This page has been replaced with newer documentation
|
||||
---
|
||||
|
||||
# ⚠️ 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.
|
||||
|
||||
## 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,111 +0,0 @@
|
||||
---
|
||||
title: Getting Started with Workflows
|
||||
description: Learn how to use LlamaIndex's lightweight workflow engine for TypeScript
|
||||
---
|
||||
|
||||
Workflows are a simple and lightweight engine for TypeScript. Built with ❤️ by LlamaIndex.
|
||||
|
||||
- Minimal core API (\<\=2kb)
|
||||
- 100% Type safe
|
||||
- Event-driven, stream oriented programming
|
||||
- Support for multiple JS runtimes/frameworks
|
||||
|
||||
## Installation
|
||||
|
||||
It's directly included with the `llamaindex` package:
|
||||
|
||||
```shell
|
||||
npm i llamaindex
|
||||
```
|
||||
|
||||
But can also be installed separately:
|
||||
|
||||
```shell
|
||||
npm i @llama-flow/core
|
||||
|
||||
# or with yarn
|
||||
yarn add @llama-flow/core
|
||||
|
||||
# or with pnpm
|
||||
pnpm add @llama-flow/core
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **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
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Let's build a simple workflow that processes a text input:
|
||||
|
||||
### 1. Define events
|
||||
|
||||
First, we need to define the events that will flow through our workflow:
|
||||
|
||||
```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);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Run the workflow
|
||||
|
||||
Finally, we can execute our workflow:
|
||||
|
||||
```ts
|
||||
// Create a workflow context and send the initial event
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with("42"));
|
||||
|
||||
// Process the stream to get the result
|
||||
import { pipeline } from "node:stream/promises";
|
||||
|
||||
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'}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(result); // "Result: positive"
|
||||
```
|
||||
|
||||
Or using the stream utilities:
|
||||
|
||||
```ts
|
||||
import { collect, until } from "llamaindex";
|
||||
|
||||
// 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'}`);
|
||||
```
|
||||
|
||||
Ready to learn more? Check out our [detailed examples](./basic-workflow.mdx) to see llama-flow in action!
|
||||
@@ -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 +0,0 @@
|
||||
{
|
||||
"title": "Workflows",
|
||||
"description": "Event-driven workflow engine for TypeScript",
|
||||
"defaultOpen": false,
|
||||
"pages": [
|
||||
"index",
|
||||
"basic-workflow",
|
||||
"streaming",
|
||||
"advanced-events",
|
||||
"llamaindex-integration"
|
||||
]
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
---
|
||||
title: Streaming with Workflows
|
||||
description: Learn how to build streaming workflows
|
||||
---
|
||||
|
||||
LlamaIndex workflows are designed from the ground up to work with streaming data. The streaming capabilities make it perfect for:
|
||||
|
||||
- Building real-time applications
|
||||
- Handling large datasets incrementally
|
||||
- Creating responsive UIs that update as data becomes available
|
||||
- Implementing long-running tasks with partial results
|
||||
|
||||
## Basic Streaming
|
||||
|
||||
Every workflow context provides a stream of events:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<string>();
|
||||
const intermediateEvent = workflowEvent<string>();
|
||||
const resultEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
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");
|
||||
});
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Stream Utilities
|
||||
|
||||
Workflows provide utility functions to make working with streams easier:
|
||||
|
||||
```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
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Server-Sent Events (SSE)
|
||||
|
||||
Workflows are also suitable for implementing Server-Sent Events:
|
||||
|
||||
```ts
|
||||
import { createWorkflow, workflowEvent } from "llamaindex";
|
||||
import express from 'express';
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent<void>();
|
||||
const dataEvent = workflowEvent<string>();
|
||||
|
||||
// Create workflow
|
||||
const workflow = createWorkflow();
|
||||
|
||||
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}`)
|
||||
);
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
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)
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"pages": ["llamaindex", "llamaflow", "cloud", "api"]
|
||||
"pages": ["llamaindex", "api"]
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export type {
|
||||
HandlerContext,
|
||||
Workflow,
|
||||
WorkflowContext,
|
||||
} from "@llamaindex/workflow";
|
||||
@@ -3,12 +3,19 @@
|
||||
"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/**"
|
||||
"src/content/docs/api/**",
|
||||
"tsconfig.json"
|
||||
],
|
||||
"env": [
|
||||
"LLAMA_CLOUD_API_KEY",
|
||||
|
||||
@@ -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/**",
|
||||
@@ -22,7 +20,7 @@
|
||||
"categoryOrder": ["Classes", "Enums", "Functions", "Interfaces", "Types"],
|
||||
"sort": ["source-order"],
|
||||
"entryFileName": "index.md",
|
||||
"fileExtension": ".mdx",
|
||||
"fileExtension": ".md",
|
||||
"hidePageTitle": true,
|
||||
"hidePageHeader": true,
|
||||
"hideGroupHeadings": true,
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/cloudflare-worker-agent-test
|
||||
|
||||
## 0.0.157
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.157",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/llama-parse-browser-test
|
||||
|
||||
## 0.0.59
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @llamaindex/cloud@4.0.4
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/llama-parse-browser-test",
|
||||
"private": true,
|
||||
"version": "0.0.58",
|
||||
"version": "0.0.59",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -10,8 +10,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^5.4.16",
|
||||
"vite-plugin-wasm": "^3.3.0"
|
||||
"vite": "^6.3.3",
|
||||
"vite-plugin-wasm": "^3.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@llamaindex/cloud": "workspace:*"
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/next-agent-test
|
||||
|
||||
## 0.1.157
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.157",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# test-edge-runtime
|
||||
|
||||
## 0.1.156
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.156",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# @llamaindex/next-node-runtime
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
- @llamaindex/huggingface@0.1.7
|
||||
- @llamaindex/readers@3.1.1
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1e59695]
|
||||
- @llamaindex/readers@3.1.0
|
||||
|
||||
## 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.24",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# vite-import-llamaindex
|
||||
|
||||
## 0.0.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.23",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
@@ -16,7 +16,7 @@
|
||||
"@size-limit/preset-big-lib": "^11.1.6",
|
||||
"size-limit": "^11.1.6",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^5.4.16"
|
||||
"vite": "^6.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"llamaindex": "workspace:*"
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/waku-query-engine-test
|
||||
|
||||
## 0.0.157
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.157",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,107 @@
|
||||
# examples
|
||||
|
||||
## 0.3.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
- llamaindex@0.10.3
|
||||
- @llamaindex/anthropic@0.3.4
|
||||
- @llamaindex/google@0.2.5
|
||||
- @llamaindex/openai@0.3.5
|
||||
- @llamaindex/vercel@0.1.3
|
||||
- @llamaindex/cloud@4.0.4
|
||||
- @llamaindex/node-parser@2.0.3
|
||||
- @llamaindex/assemblyai@0.1.2
|
||||
- @llamaindex/clip@0.0.53
|
||||
- @llamaindex/cohere@0.0.17
|
||||
- @llamaindex/deepinfra@0.0.53
|
||||
- @llamaindex/discord@0.1.2
|
||||
- @llamaindex/huggingface@0.1.7
|
||||
- @llamaindex/jinaai@0.0.13
|
||||
- @llamaindex/mistral@0.1.3
|
||||
- @llamaindex/mixedbread@0.0.17
|
||||
- @llamaindex/notion@0.1.2
|
||||
- @llamaindex/ollama@0.1.3
|
||||
- @llamaindex/perplexity@0.0.10
|
||||
- @llamaindex/portkey-ai@0.0.45
|
||||
- @llamaindex/replicate@0.0.45
|
||||
- @llamaindex/astra@0.0.17
|
||||
- @llamaindex/azure@0.1.13
|
||||
- @llamaindex/chroma@0.0.17
|
||||
- @llamaindex/elastic-search@0.1.3
|
||||
- @llamaindex/firestore@1.0.10
|
||||
- @llamaindex/milvus@0.1.12
|
||||
- @llamaindex/mongodb@0.0.18
|
||||
- @llamaindex/pinecone@0.1.3
|
||||
- @llamaindex/postgres@0.0.46
|
||||
- @llamaindex/qdrant@0.1.12
|
||||
- @llamaindex/supabase@0.1.2
|
||||
- @llamaindex/upstash@0.0.17
|
||||
- @llamaindex/weaviate@0.0.17
|
||||
- @llamaindex/voyage-ai@1.0.9
|
||||
- @llamaindex/readers@3.1.1
|
||||
- @llamaindex/tools@0.0.8
|
||||
- @llamaindex/workflow@1.0.4
|
||||
- @llamaindex/deepseek@0.0.13
|
||||
- @llamaindex/fireworks@0.0.13
|
||||
- @llamaindex/groq@0.0.68
|
||||
- @llamaindex/together@0.0.13
|
||||
- @llamaindex/vllm@0.0.39
|
||||
|
||||
## 0.3.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [82d4b46]
|
||||
- @llamaindex/server@0.1.6
|
||||
- @llamaindex/tools@0.0.7
|
||||
|
||||
## 0.3.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [294f502]
|
||||
- @llamaindex/tools@0.0.6
|
||||
|
||||
## 0.3.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1e59695]
|
||||
- Updated dependencies [1e59695]
|
||||
- Updated dependencies [1e59695]
|
||||
- Updated dependencies [1e59695]
|
||||
- Updated dependencies [1e59695]
|
||||
- Updated dependencies [1e59695]
|
||||
- @llamaindex/assemblyai@0.1.1
|
||||
- @llamaindex/mongodb@0.0.17
|
||||
- @llamaindex/azure@0.1.12
|
||||
- @llamaindex/readers@3.1.0
|
||||
- @llamaindex/notion@0.1.1
|
||||
- @llamaindex/discord@0.1.1
|
||||
|
||||
## 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
|
||||
|
||||
@@ -9,6 +9,12 @@ async function main() {
|
||||
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
verbose: true,
|
||||
});
|
||||
// You can also connect to the MCP server using SSE
|
||||
// See: https://modelcontextprotocol.io/docs/concepts/transports#server-sent-events-sse
|
||||
// const server = mcp({
|
||||
// url: "http://localhost:8000/mcp",
|
||||
// verbose: true,
|
||||
// });
|
||||
|
||||
try {
|
||||
// Create an agent that uses the MCP tools
|
||||
@@ -21,9 +27,7 @@ async function main() {
|
||||
});
|
||||
|
||||
// Run a task
|
||||
const response = await myAgent.run(
|
||||
"what are the files in the current directory?",
|
||||
);
|
||||
const response = await myAgent.run("What are the available tools?");
|
||||
|
||||
console.log("Agent response:", response.data);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Anthropic } from "@llamaindex/anthropic";
|
||||
import fs from "fs";
|
||||
|
||||
// Note that: Anthropic only supports PDF files for now with limited models
|
||||
// See: https://docs.anthropic.com/en/docs/build-with-claude/pdf-support?q=pdf#supported-platforms-and-models
|
||||
|
||||
async function main() {
|
||||
if (!process.env.ANTHROPIC_API_KEY) {
|
||||
throw new Error("Please set the ANTHROPIC_API_KEY environment variable.");
|
||||
}
|
||||
|
||||
const llm = new Anthropic({
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
model: "claude-3-7-sonnet",
|
||||
});
|
||||
|
||||
const result = await llm.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What's in this document? Describe it in detail.",
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(result.message);
|
||||
}
|
||||
|
||||
void main().catch(console.error);
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
AudioTranscriptReader,
|
||||
TranscribeParams,
|
||||
} from "@llamaindex/readers/assembly-ai";
|
||||
} from "@llamaindex/assemblyai";
|
||||
import { program } from "commander";
|
||||
import { VectorStoreIndex } from "llamaindex";
|
||||
import { stdin as input, stdout as output } from "node:process";
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CosmosClient } from "@azure/cosmos";
|
||||
import { DefaultAzureCredential } from "@azure/identity";
|
||||
import { AzureCosmosDBNoSQLConfig } from "@llamaindex/azure";
|
||||
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
import {
|
||||
AzureCosmosDBNoSQLConfig,
|
||||
SimpleCosmosDBReader,
|
||||
SimpleCosmosDBReaderLoaderConfig,
|
||||
} from "@llamaindex/readers/cosmosdb";
|
||||
} from "@llamaindex/azure";
|
||||
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
import * as dotenv from "dotenv";
|
||||
import {
|
||||
Settings,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DiscordReader } from "@llamaindex/readers/discord";
|
||||
import { DiscordReader } from "@llamaindex/discord";
|
||||
|
||||
async function main() {
|
||||
// Create an instance of the DiscordReader. Set token here or DISCORD_TOKEN environment variable
|
||||
+24
-1
@@ -1,11 +1,12 @@
|
||||
import { Gemini, GEMINI_MODEL } from "@llamaindex/google";
|
||||
import fs from "fs";
|
||||
|
||||
(async () => {
|
||||
if (!process.env.GOOGLE_API_KEY) {
|
||||
throw new Error("Please set the GOOGLE_API_KEY environment variable.");
|
||||
}
|
||||
const gemini = new Gemini({
|
||||
model: GEMINI_MODEL.GEMINI_PRO,
|
||||
model: GEMINI_MODEL.GEMINI_PRO_1_5,
|
||||
});
|
||||
const result = await gemini.chat({
|
||||
messages: [
|
||||
@@ -18,4 +19,26 @@ import { Gemini, GEMINI_MODEL } from "@llamaindex/google";
|
||||
],
|
||||
});
|
||||
console.log(result);
|
||||
|
||||
// chat with file
|
||||
const resultWithFile = await gemini.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What's in this document? Describe it in detail.",
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(resultWithFile);
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# Run LlamaIndex Server with simple steps
|
||||
|
||||
1. Setup environment variables
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=<your-openai-api-key>
|
||||
```
|
||||
|
||||
2. Run the server
|
||||
|
||||
```bash
|
||||
npx tsx llamaindex-server/simple-workflow/index.ts
|
||||
```
|
||||
|
||||
3. Open the app at `http://localhost:4000` and start chatting with the agent
|
||||
|
||||

|
||||
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
@@ -0,0 +1,19 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { LlamaIndexServer } from "@llamaindex/server";
|
||||
import { weather } from "@llamaindex/tools";
|
||||
import "dotenv/config";
|
||||
import { agent } from "llamaindex";
|
||||
|
||||
const weatherAgent = agent({
|
||||
tools: [weather()],
|
||||
llm: new OpenAI({ model: "gpt-4o-mini" }),
|
||||
});
|
||||
|
||||
new LlamaIndexServer({
|
||||
workflow: () => weatherAgent,
|
||||
uiConfig: {
|
||||
appTitle: "Weather Agent",
|
||||
starterQuestions: ["Ho Chi Minh city weather", "New York weather"],
|
||||
},
|
||||
port: 4000,
|
||||
}).start();
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { SimpleMongoReader } from "@llamaindex/readers/mongo";
|
||||
import { SimpleMongoReader } from "@llamaindex/mongodb";
|
||||
import { Document, VectorStoreIndex } from "llamaindex";
|
||||
import { MongoClient } from "mongodb";
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { MongoDBAtlasVectorSearch } from "@llamaindex/mongodb";
|
||||
import { SimpleMongoReader } from "@llamaindex/readers/mongo";
|
||||
import {
|
||||
MongoDBAtlasVectorSearch,
|
||||
SimpleMongoReader,
|
||||
} from "@llamaindex/mongodb";
|
||||
import * as dotenv from "dotenv";
|
||||
import { storageContextFromDefaults, VectorStoreIndex } from "llamaindex";
|
||||
import { MongoClient } from "mongodb";
|
||||
|
||||
@@ -11,7 +11,6 @@ const workflow = new Workflow<ContextData, string, string>();
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
async (context, startEvent) => {
|
||||
const input = startEvent.data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NotionReader } from "@llamaindex/readers/notion";
|
||||
import { NotionReader } from "@llamaindex/notion";
|
||||
import { Client } from "@notionhq/client";
|
||||
import { program } from "commander";
|
||||
import { VectorStoreIndex } from "llamaindex";
|
||||
@@ -8,8 +8,6 @@ import { createInterface } from "node:readline/promises";
|
||||
program
|
||||
.argument("[page]", "Notion page id (must be provided)")
|
||||
.action(async (page, _options) => {
|
||||
// Initializing a client
|
||||
|
||||
if (!process.env.NOTION_TOKEN) {
|
||||
console.log(
|
||||
"No NOTION_TOKEN found in environment variables. You will need to register an integration https://www.notion.com/my-integrations and put it in your NOTION_TOKEN environment variable.",
|
||||
@@ -64,10 +62,8 @@ program
|
||||
const documents = await reader.loadData(page);
|
||||
console.log(documents);
|
||||
|
||||
// Split text and create embeddings. Store them in a VectorStoreIndex
|
||||
const index = await VectorStoreIndex.fromDocuments(documents);
|
||||
|
||||
// Create query engine
|
||||
const queryEngine = index.asQueryEngine();
|
||||
|
||||
const rl = createInterface({ input, output });
|
||||
@@ -80,7 +76,6 @@ program
|
||||
|
||||
const response = await queryEngine.query({ query });
|
||||
|
||||
// Output response
|
||||
console.log(response.toString());
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
import fs from "fs";
|
||||
|
||||
(async () => {
|
||||
const llm = new OpenAI({ model: "gpt-4.5-preview", temperature: 0.1 });
|
||||
const llm = new OpenAI({ model: "gpt-4o" });
|
||||
|
||||
// complete api
|
||||
const response1 = await llm.complete({ prompt: "How are you?" });
|
||||
@@ -13,7 +14,51 @@ import { OpenAI, OpenAIEmbedding } from "@llamaindex/openai";
|
||||
});
|
||||
console.log(response2.message.content);
|
||||
|
||||
// embeddings
|
||||
// chat with file
|
||||
const response3 = await llm.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What's in this document? Describe it in detail.",
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
console.log(response3.message.content);
|
||||
|
||||
// chat with image
|
||||
const response4 = await llm.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What's in this image? Describe it in detail.",
|
||||
},
|
||||
{
|
||||
type: "image_url",
|
||||
image_url: {
|
||||
url: "https://storage.googleapis.com/cloud-samples-data/vision/face/faces.jpeg",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log("Single Image Analysis:", response4.message.content);
|
||||
|
||||
// // embeddings
|
||||
const embedModel = new OpenAIEmbedding();
|
||||
const texts = ["hello", "world"];
|
||||
const embeddings = await embedModel.getTextEmbeddingsBatch(texts);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { openaiResponses } from "@llamaindex/openai";
|
||||
import fs from "fs";
|
||||
|
||||
async function main() {
|
||||
if (!process.env.OPENAI_API_KEY) {
|
||||
throw new Error("Please set the OPENAI_API_KEY environment variable.");
|
||||
}
|
||||
|
||||
const llm = openaiResponses({
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
});
|
||||
|
||||
const result = await llm.chat({
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "What's in this document? Describe it in detail.",
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
void main().catch(console.error);
|
||||
+46
-42
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/examples",
|
||||
"version": "0.3.8",
|
||||
"version": "0.3.13",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
@@ -11,56 +11,60 @@
|
||||
"@azure/cosmos": "^4.1.1",
|
||||
"@azure/identity": "^4.4.1",
|
||||
"@azure/search-documents": "^12.1.0",
|
||||
"@llamaindex/anthropic": "^0.3.3",
|
||||
"@llamaindex/astra": "^0.0.16",
|
||||
"@llamaindex/azure": "^0.1.11",
|
||||
"@llamaindex/chroma": "^0.0.16",
|
||||
"@llamaindex/clip": "^0.0.51",
|
||||
"@llamaindex/cloud": "^4.0.3",
|
||||
"@llamaindex/cohere": "^0.0.16",
|
||||
"@llamaindex/core": "^0.6.2",
|
||||
"@llamaindex/deepinfra": "^0.0.51",
|
||||
"@llamaindex/anthropic": "^0.3.4",
|
||||
"@llamaindex/astra": "^0.0.17",
|
||||
"@llamaindex/azure": "^0.1.13",
|
||||
"@llamaindex/chroma": "^0.0.17",
|
||||
"@llamaindex/clip": "^0.0.53",
|
||||
"@llamaindex/cloud": "^4.0.4",
|
||||
"@llamaindex/cohere": "^0.0.17",
|
||||
"@llamaindex/core": "^0.6.3",
|
||||
"@llamaindex/deepinfra": "^0.0.53",
|
||||
"@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/milvus": "^0.1.11",
|
||||
"@llamaindex/mistral": "^0.1.2",
|
||||
"@llamaindex/mixedbread": "^0.0.16",
|
||||
"@llamaindex/mongodb": "^0.0.16",
|
||||
"@llamaindex/elastic-search": "^0.1.2",
|
||||
"@llamaindex/node-parser": "^2.0.2",
|
||||
"@llamaindex/ollama": "^0.1.2",
|
||||
"@llamaindex/openai": "^0.3.3",
|
||||
"@llamaindex/pinecone": "^0.1.2",
|
||||
"@llamaindex/portkey-ai": "^0.0.44",
|
||||
"@llamaindex/postgres": "^0.0.45",
|
||||
"@llamaindex/qdrant": "^0.1.11",
|
||||
"@llamaindex/readers": "^3.0.2",
|
||||
"@llamaindex/replicate": "^0.0.44",
|
||||
"@llamaindex/upstash": "^0.0.16",
|
||||
"@llamaindex/vercel": "^0.1.2",
|
||||
"@llamaindex/vllm": "^0.0.37",
|
||||
"@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/supabase": "^0.1.1",
|
||||
"@llamaindex/tools": "^0.0.5",
|
||||
"@llamaindex/firestore": "^1.0.10",
|
||||
"@llamaindex/google": "^0.2.5",
|
||||
"@llamaindex/groq": "^0.0.68",
|
||||
"@llamaindex/huggingface": "^0.1.7",
|
||||
"@llamaindex/milvus": "^0.1.12",
|
||||
"@llamaindex/mistral": "^0.1.3",
|
||||
"@llamaindex/mixedbread": "^0.0.17",
|
||||
"@llamaindex/mongodb": "^0.0.18",
|
||||
"@llamaindex/elastic-search": "^0.1.3",
|
||||
"@llamaindex/node-parser": "^2.0.3",
|
||||
"@llamaindex/ollama": "^0.1.3",
|
||||
"@llamaindex/openai": "^0.3.5",
|
||||
"@llamaindex/pinecone": "^0.1.3",
|
||||
"@llamaindex/portkey-ai": "^0.0.45",
|
||||
"@llamaindex/postgres": "^0.0.46",
|
||||
"@llamaindex/qdrant": "^0.1.12",
|
||||
"@llamaindex/readers": "^3.1.1",
|
||||
"@llamaindex/replicate": "^0.0.45",
|
||||
"@llamaindex/upstash": "^0.0.17",
|
||||
"@llamaindex/vercel": "^0.1.3",
|
||||
"@llamaindex/vllm": "^0.0.39",
|
||||
"@llamaindex/voyage-ai": "^1.0.9",
|
||||
"@llamaindex/weaviate": "^0.0.17",
|
||||
"@llamaindex/workflow": "^1.0.4",
|
||||
"@llamaindex/deepseek": "^0.0.13",
|
||||
"@llamaindex/fireworks": "^0.0.13",
|
||||
"@llamaindex/together": "^0.0.13",
|
||||
"@llamaindex/jinaai": "^0.0.13",
|
||||
"@llamaindex/perplexity": "^0.0.10",
|
||||
"@llamaindex/server": "^0.1.6",
|
||||
"@llamaindex/supabase": "^0.1.2",
|
||||
"@llamaindex/tools": "^0.0.8",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
"@pinecone-database/pinecone": "^4.0.0",
|
||||
"@llamaindex/assemblyai": "^0.1.2",
|
||||
"@llamaindex/discord": "^0.1.2",
|
||||
"@llamaindex/notion": "^0.1.2",
|
||||
"@vercel/postgres": "^0.10.0",
|
||||
"ai": "^4.0.0",
|
||||
"ajv": "^8.17.1",
|
||||
"commander": "^12.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"js-tiktoken": "^1.0.14",
|
||||
"llamaindex": "^0.10.1",
|
||||
"llamaindex": "^0.10.3",
|
||||
"mongodb": "6.7.0",
|
||||
"postgres": "^3.4.4",
|
||||
"wikipedia": "^2.1.2",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
"start:pdf": "node --import tsx ./src/pdf.ts",
|
||||
"start:llamaparse": "node --import tsx ./src/llamaparse.ts",
|
||||
"start:notion": "node --import tsx ./src/notion.ts",
|
||||
"start:assemblyai": "node --import tsx ./src/assemblyai.ts",
|
||||
"start:llamaparse-dir": "node --import tsx ./src/simple-directory-reader-with-llamaparse.ts",
|
||||
"start:llamaparse-json": "node --import tsx ./src/llamaparse-json.ts",
|
||||
"start:discord": "node --import tsx ./src/discord.ts",
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Workflow Examples
|
||||
|
||||
These examples demonstrate LlamaIndexTS's workflow system. Check out [its documentation](https://ts.llamaindex.ai/modules/workflows) for more information.
|
||||
|
||||
## Running the Examples
|
||||
|
||||
To run the examples, make sure to run them from the parent folder called `examples`). For example, to run the joke workflow, run `npx tsx workflow/joke.ts`.
|
||||
@@ -1,157 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import {
|
||||
HandlerContext,
|
||||
StartEvent,
|
||||
StopEvent,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "llamaindex";
|
||||
|
||||
const MAX_REVIEWS = 3;
|
||||
|
||||
type Context = {
|
||||
specification: string;
|
||||
numberReviews: number;
|
||||
};
|
||||
|
||||
// Using the o1-preview model (see https://platform.openai.com/docs/guides/reasoning?reasoning-prompt-examples=coding-planning)
|
||||
const llm = new OpenAI({ model: "o1-preview", temperature: 1 });
|
||||
|
||||
// example specification from https://platform.openai.com/docs/guides/reasoning?reasoning-prompt-examples=coding-planning
|
||||
const specification = `Python app that takes user questions and looks them up in a
|
||||
database where they are mapped to answers. If there is a close match, it retrieves
|
||||
the matched answer. If there isn't, it asks the user to provide an answer and
|
||||
stores the question/answer pair in the database.`;
|
||||
|
||||
// Create custom event types
|
||||
export class MessageEvent extends WorkflowEvent<{ msg: string }> {}
|
||||
|
||||
export class CodeEvent extends WorkflowEvent<{ code: string }> {}
|
||||
|
||||
export class ReviewEvent extends WorkflowEvent<{
|
||||
review: string;
|
||||
code: string;
|
||||
}> {}
|
||||
|
||||
// Helper function to truncate long strings
|
||||
const truncate = (str: string) => {
|
||||
const MAX_LENGTH = 60;
|
||||
if (str.length <= MAX_LENGTH) return str;
|
||||
return str.slice(0, MAX_LENGTH) + "...";
|
||||
};
|
||||
|
||||
// the architect is responsible for writing the structure and the initial code based on the specification
|
||||
const architect = async (
|
||||
context: HandlerContext<Context>,
|
||||
_: StartEvent<string>,
|
||||
) => {
|
||||
const spec = context.data.specification;
|
||||
// write a message to send an update to the user
|
||||
context.sendEvent(
|
||||
new MessageEvent({
|
||||
msg: `Writing app using this specification: ${truncate(spec)}`,
|
||||
}),
|
||||
);
|
||||
const prompt = `Build an app for this specification: <spec>${spec}</spec>. Make a plan for the directory structure you'll need, then return each file in full. Don't supply any reasoning, just code.`;
|
||||
const code = await llm.complete({ prompt });
|
||||
return new CodeEvent({ code: code.text });
|
||||
};
|
||||
|
||||
// the coder is responsible for updating the code based on the review
|
||||
const coder = async (context: HandlerContext<Context>, ev: ReviewEvent) => {
|
||||
// get the specification from the context
|
||||
const spec = context.data.specification;
|
||||
// get the latest review and code
|
||||
const { review, code } = ev.data;
|
||||
// write a message to send an update to the user
|
||||
context.sendEvent(
|
||||
new MessageEvent({
|
||||
msg: `Update code based on review: ${truncate(review)}`,
|
||||
}),
|
||||
);
|
||||
const prompt = `We need to improve code that should implement this specification: <spec>${spec}</spec>. Here is the current code: <code>${code}</code>. And here is a review of the code: <review>${review}</review>. Improve the code based on the review, keep the specification in mind, and return the full updated code. Don't supply any reasoning, just code.`;
|
||||
const updatedCode = await llm.complete({ prompt });
|
||||
return new CodeEvent({ code: updatedCode.text });
|
||||
};
|
||||
|
||||
// the reviewer is responsible for reviewing the code and providing feedback
|
||||
const reviewer = async (context: HandlerContext<Context>, ev: CodeEvent) => {
|
||||
// get the specification from the context
|
||||
const spec = context.data.specification;
|
||||
// get latest code from the event
|
||||
const { code } = ev.data;
|
||||
// update and check the number of reviews
|
||||
context.data.numberReviews++;
|
||||
if (context.data.numberReviews > MAX_REVIEWS) {
|
||||
// the we've done this too many times - return the code
|
||||
context.sendEvent(
|
||||
new MessageEvent({
|
||||
msg: `Already reviewed ${
|
||||
context.data.numberReviews - 1
|
||||
} times, stopping!`,
|
||||
}),
|
||||
);
|
||||
return new StopEvent({ result: code });
|
||||
}
|
||||
// write a message to send an update to the user
|
||||
context.sendEvent(
|
||||
new MessageEvent({
|
||||
msg: `Review #${context.data.numberReviews}: ${truncate(code)}`,
|
||||
}),
|
||||
);
|
||||
const prompt = `Review this code: <code>${code}</code>. Check if the code quality and whether it correctly implements this specification: <spec>${spec}</spec>. If you're satisfied, just return 'Looks great', nothing else. If not, return a review with a list of changes you'd like to see.`;
|
||||
const review = (await llm.complete({ prompt })).text;
|
||||
if (review.includes("Looks great")) {
|
||||
// the reviewer is satisfied with the code, let's return the review
|
||||
context.sendEvent(
|
||||
new MessageEvent({
|
||||
msg: `Reviewer says: ${review}`,
|
||||
}),
|
||||
);
|
||||
return new StopEvent({ result: code });
|
||||
}
|
||||
|
||||
return new ReviewEvent({ review, code });
|
||||
};
|
||||
|
||||
const codeAgent = new Workflow<Context, string, string>();
|
||||
codeAgent.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [CodeEvent],
|
||||
},
|
||||
architect,
|
||||
);
|
||||
codeAgent.addStep(
|
||||
{
|
||||
inputs: [ReviewEvent],
|
||||
outputs: [CodeEvent],
|
||||
},
|
||||
coder,
|
||||
);
|
||||
codeAgent.addStep(
|
||||
{
|
||||
inputs: [CodeEvent],
|
||||
outputs: [ReviewEvent, StopEvent],
|
||||
},
|
||||
reviewer,
|
||||
);
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const run = codeAgent.run(specification).with({
|
||||
specification,
|
||||
numberReviews: 0,
|
||||
});
|
||||
for await (const event of run) {
|
||||
if (event instanceof MessageEvent) {
|
||||
const msg = (event as MessageEvent).data.msg;
|
||||
console.log(`${msg}\n`);
|
||||
} else if (event instanceof StopEvent) {
|
||||
const result = (event as StopEvent<string>).data;
|
||||
console.log("Final code:\n", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,88 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import {
|
||||
HandlerContext,
|
||||
StartEvent,
|
||||
StopEvent,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "llamaindex";
|
||||
|
||||
// Create LLM instance
|
||||
const llm = new OpenAI();
|
||||
|
||||
// Create custom event types
|
||||
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
|
||||
|
||||
export class CritiqueEvent extends WorkflowEvent<{ critique: string }> {}
|
||||
|
||||
export class AnalysisEvent extends WorkflowEvent<{ analysis: string }> {}
|
||||
|
||||
const generateJoke = async (_: unknown, ev: StartEvent<string>) => {
|
||||
const prompt = `Write your best joke about ${ev.data}.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new JokeEvent({ joke: response.text });
|
||||
};
|
||||
|
||||
const critiqueJoke = async (_: unknown, ev: JokeEvent) => {
|
||||
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new CritiqueEvent({ critique: response.text });
|
||||
};
|
||||
|
||||
const analyzeJoke = async (_: unknown, ev: JokeEvent) => {
|
||||
const prompt = `Give a thorough analysis of the following joke: ${ev.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new AnalysisEvent({ analysis: response.text });
|
||||
};
|
||||
|
||||
const reportJoke = async (
|
||||
context: HandlerContext,
|
||||
ev1: AnalysisEvent,
|
||||
ev2: CritiqueEvent,
|
||||
) => {
|
||||
const subPrompts = [ev1.data.analysis, ev2.data.critique];
|
||||
|
||||
const prompt = `Based on the following information about a joke:\n${subPrompts.join(
|
||||
"\n",
|
||||
)}\nProvide a comprehensive report on the joke's quality and impact.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent(response.text);
|
||||
};
|
||||
|
||||
const jokeFlow = new Workflow<unknown, string, string>();
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [JokeEvent],
|
||||
},
|
||||
generateJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [CritiqueEvent],
|
||||
},
|
||||
critiqueJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [AnalysisEvent],
|
||||
},
|
||||
analyzeJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [AnalysisEvent, CritiqueEvent],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
reportJoke,
|
||||
);
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const result = await jokeFlow.run("pirates");
|
||||
console.log(result.data);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,44 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { StartEvent, StopEvent, Workflow, WorkflowEvent } from "llamaindex";
|
||||
|
||||
// Create LLM instance
|
||||
const llm = new OpenAI();
|
||||
|
||||
// Create a custom event type
|
||||
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
|
||||
|
||||
const generateJoke = async (_: unknown, ev: StartEvent<string>) => {
|
||||
const prompt = `Write your best joke about ${ev.data}.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new JokeEvent({ joke: response.text });
|
||||
};
|
||||
|
||||
const critiqueJoke = async (_: unknown, ev: JokeEvent) => {
|
||||
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent(response.text);
|
||||
};
|
||||
|
||||
const jokeFlow = new Workflow<unknown, string, string>();
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [JokeEvent],
|
||||
},
|
||||
generateJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
critiqueJoke,
|
||||
);
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const result = await jokeFlow.run("pirates");
|
||||
console.log(result.data);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,66 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import {
|
||||
HandlerContext,
|
||||
StartEvent,
|
||||
StopEvent,
|
||||
Workflow,
|
||||
WorkflowEvent,
|
||||
} from "llamaindex";
|
||||
|
||||
// Create LLM instance
|
||||
const llm = new OpenAI();
|
||||
|
||||
// Create custom event types
|
||||
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
|
||||
|
||||
export class MessageEvent extends WorkflowEvent<{ msg: string }> {}
|
||||
|
||||
const generateJoke = async (context: HandlerContext, ev: StartEvent) => {
|
||||
context.sendEvent(
|
||||
new MessageEvent({ msg: `Generating a joke about: ${ev.data}` }),
|
||||
);
|
||||
const prompt = `Write your best joke about ${ev.data}.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new JokeEvent({ joke: response.text });
|
||||
};
|
||||
|
||||
const critiqueJoke = async (context: HandlerContext, ev: JokeEvent) => {
|
||||
context.sendEvent(
|
||||
new MessageEvent({ msg: `Write a critique of this joke: ${ev.data.joke}` }),
|
||||
);
|
||||
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent(response.text);
|
||||
};
|
||||
|
||||
const jokeFlow = new Workflow();
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [JokeEvent],
|
||||
},
|
||||
generateJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
critiqueJoke,
|
||||
);
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
const run = jokeFlow.run("pirates");
|
||||
for await (const event of run) {
|
||||
if (event instanceof MessageEvent) {
|
||||
console.log("Message:");
|
||||
console.log((event as MessageEvent).data.msg);
|
||||
} else if (event instanceof StopEvent) {
|
||||
console.log("Result:");
|
||||
console.log((event as StopEvent<string>).data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,48 +0,0 @@
|
||||
import { StartEvent, StopEvent, Workflow } from "llamaindex";
|
||||
|
||||
const longRunning = async (_: unknown, ev: StartEvent<string>) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait for 2 seconds
|
||||
return new StopEvent("We waited 2 seconds");
|
||||
};
|
||||
|
||||
async function timeout() {
|
||||
const workflow = new Workflow<unknown, string, string>({
|
||||
timeout: 1,
|
||||
});
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
longRunning,
|
||||
);
|
||||
try {
|
||||
await workflow.run("Let's start");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function notimeout() {
|
||||
// Increase timeout to 3 seconds - no timeout
|
||||
const workflow = new Workflow<unknown, string, string>({
|
||||
timeout: 3,
|
||||
});
|
||||
workflow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
longRunning,
|
||||
);
|
||||
const result = await workflow.run("Let's start");
|
||||
console.log(result.data);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await timeout();
|
||||
console.log("---");
|
||||
await notimeout();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -1,73 +0,0 @@
|
||||
import { OpenAI } from "@llamaindex/openai";
|
||||
import { StartEvent, StopEvent, Workflow, WorkflowEvent } from "llamaindex";
|
||||
|
||||
// Create LLM instance
|
||||
const llm = new OpenAI();
|
||||
|
||||
// Create a custom event type
|
||||
export class JokeEvent extends WorkflowEvent<{ joke: string }> {}
|
||||
|
||||
const generateJoke = async (_: unknown, ev: StartEvent<string>) => {
|
||||
const prompt = `Write your best joke about ${ev.data}.`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new JokeEvent({ joke: response.text });
|
||||
};
|
||||
|
||||
const critiqueJoke = async (_: unknown, ev: JokeEvent) => {
|
||||
const prompt = `Give a thorough critique of the following joke: ${ev.data.joke}`;
|
||||
const response = await llm.complete({ prompt });
|
||||
return new StopEvent(response.text);
|
||||
};
|
||||
|
||||
async function validateFails() {
|
||||
try {
|
||||
const jokeFlow = new Workflow();
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
// @ts-expect-error outputs should be JokeEvent
|
||||
generateJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [StopEvent],
|
||||
},
|
||||
critiqueJoke,
|
||||
);
|
||||
await jokeFlow.run("pirates").strict();
|
||||
} catch (e) {
|
||||
console.error("Validation failed:", e);
|
||||
}
|
||||
}
|
||||
|
||||
async function validate() {
|
||||
const jokeFlow = new Workflow();
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [StartEvent<string>],
|
||||
outputs: [JokeEvent],
|
||||
},
|
||||
generateJoke,
|
||||
);
|
||||
jokeFlow.addStep(
|
||||
{
|
||||
inputs: [JokeEvent],
|
||||
outputs: [StopEvent<string>],
|
||||
},
|
||||
critiqueJoke,
|
||||
);
|
||||
const result = await jokeFlow.run("pirates").strict();
|
||||
console.log(result.data);
|
||||
}
|
||||
|
||||
// Usage
|
||||
async function main() {
|
||||
await validateFails();
|
||||
console.log("---");
|
||||
await validate();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
+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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/autotool
|
||||
|
||||
## 7.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 7.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- llamaindex@0.10.2
|
||||
|
||||
## 7.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# @llamaindex/autotool-01-node-example
|
||||
|
||||
## 0.0.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
- @llamaindex/autotool@7.0.3
|
||||
|
||||
## 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.104"
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
|
||||
"directory": "packages/autotool"
|
||||
},
|
||||
"version": "7.0.1",
|
||||
"version": "7.0.3",
|
||||
"description": "auto transpile your JS function to LLM Agent compatible",
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/cloud
|
||||
|
||||
## 4.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
|
||||
## 4.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/cloud",
|
||||
"version": "4.0.3",
|
||||
"version": "4.0.4",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -63,5 +63,8 @@
|
||||
"peerDependencies": {
|
||||
"@llamaindex/core": "workspace:*",
|
||||
"@llamaindex/env": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"p-retry": "^6.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/community
|
||||
|
||||
## 0.0.97
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
|
||||
## 0.0.96
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/community",
|
||||
"description": "Community package for LlamaIndexTS",
|
||||
"version": "0.0.96",
|
||||
"version": "0.0.97",
|
||||
"type": "module",
|
||||
"types": "dist/type/index.d.ts",
|
||||
"main": "dist/cjs/index.js",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @llamaindex/core
|
||||
|
||||
## 0.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3ee8c83: feat: support file content type in message content
|
||||
|
||||
## 0.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/core",
|
||||
"type": "module",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.3",
|
||||
"description": "LlamaIndex Core Module",
|
||||
"exports": {
|
||||
"./agent": {
|
||||
|
||||
@@ -17,6 +17,7 @@ export type {
|
||||
LLMMetadata,
|
||||
MessageContent,
|
||||
MessageContentDetail,
|
||||
MessageContentFileDetail,
|
||||
MessageContentImageDetail,
|
||||
MessageContentTextDetail,
|
||||
MessageType,
|
||||
|
||||
@@ -163,9 +163,16 @@ export type MessageContentImageDetail = {
|
||||
detail?: "high" | "low" | "auto";
|
||||
};
|
||||
|
||||
export type MessageContentFileDetail = {
|
||||
type: "file";
|
||||
data: Buffer;
|
||||
mimeType: string;
|
||||
};
|
||||
|
||||
export type MessageContentDetail =
|
||||
| MessageContentTextDetail
|
||||
| MessageContentImageDetail;
|
||||
| MessageContentImageDetail
|
||||
| MessageContentFileDetail;
|
||||
|
||||
/**
|
||||
* Extended type for the content of a message that allows for multi-modal messages.
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @llamaindex/experimental
|
||||
|
||||
## 0.0.173
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- llamaindex@0.10.3
|
||||
|
||||
## 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.173",
|
||||
"type": "module",
|
||||
"types": "dist/type/index.d.ts",
|
||||
"main": "dist/cjs/index.js",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# llamaindex
|
||||
|
||||
## 0.10.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3ee8c83: feat: support file content type in message content
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
- @llamaindex/openai@0.3.5
|
||||
- @llamaindex/cloud@4.0.4
|
||||
- @llamaindex/node-parser@2.0.3
|
||||
- @llamaindex/workflow@1.0.4
|
||||
|
||||
## 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.3",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"keywords": [
|
||||
@@ -25,7 +25,7 @@
|
||||
"@llamaindex/env": "workspace:*",
|
||||
"@llamaindex/node-parser": "workspace:*",
|
||||
"@llamaindex/openai": "workspace:*",
|
||||
"@llamaindex/workflow": "workspace:*",
|
||||
"@llamaindex/workflow": "1.0.4",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"@types/node": "^22.9.0",
|
||||
"ajv": "^8.17.1",
|
||||
|
||||
@@ -51,7 +51,7 @@ export class QueryEngineTool implements BaseTool<QueryEngineParam> {
|
||||
const response = await this.queryEngine.query({ query });
|
||||
|
||||
if (!this.includeSourceNodes) {
|
||||
return { content: response.message.content };
|
||||
return { content: response.message.content } as unknown as JSONValue;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @llamaindex/node-parser
|
||||
|
||||
## 2.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@llamaindex/node-parser",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"description": "Node parser for LlamaIndex",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @llamaindex/anthropic
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 3ee8c83: feat: support file content type in message content
|
||||
- Updated dependencies [3ee8c83]
|
||||
- @llamaindex/core@0.6.3
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@llamaindex/anthropic",
|
||||
"description": "Anthropic Adapter for LlamaIndex",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user