---
title: Basic Usage
description: Learn how to use the LlamaIndex workflow.
---

A `Workflow` in LlamaIndex.TS is an event-driven abstraction used to chain together several events.
Workflows are made up of steps, with each step responsible for handling certain event types and emitting new events.

Workflows are designed for any cases that benefit from event-driven programming, not only for LLM and AI tasks.

import { Tab, Tabs } from "fumadocs-ui/components/tabs";

<Tabs groupId="install" items={["npm", "yarn", "pnpm"]} persist>
	```shell tab="npm"
	npm install @llamaindex/workflow
	```

	```shell tab="yarn"
	yarn add @llamaindex/workflow
	```

	```shell tab="pnpm"
	pnpm add @llamaindex/workflow
	```
</Tabs>

## Start from scratch

Let's start from a Hello World workflow.

```ts twoslash
import { Workflow } from '@llamaindex/workflow';

type ContextData = {
	counter: number;
}
// ---cut---
const contextData: ContextData = { counter: 0 };

const workflow = new Workflow<ContextData, string, string>();
//     ^?



```

First, we define a workflow with 3 generic types: `ContextData`, `Input`, and `Output`.

In general, `ContextData` is used to store the shared data between steps, `Input` is the type of the input event, and `Output` is the type of the output event.

In you code logic, you should **share state between steps via `ContextData`**.

```ts twoslash
import { Workflow, StartEvent, StopEvent } from '@llamaindex/workflow';

type ContextData = {
	counter: number;
}

const contextData: ContextData = { counter: 0 };

const workflow = new Workflow<ContextData, string, string>();
// ---cut---
workflow.addStep({
	inputs: [StartEvent<string>],
	outputs: [StopEvent<string>]
}, async (context, startEvent) => {
	const input = startEvent.data;
	context.data.counter++;
	return new StopEvent(`Hello, ${input}!`);
});
```

In the workflow, we add a step that listens to `StartEvent<string>` and emits `StopEvent<string>`.

The step is an async function that takes two arguments: `context` and `event`.

### `context` type

<AutoTypeTable path="./src/deps/type.ts" name="HandlerContext" />

There are two more properties in `HandlerContext`:

- `sendEvent`: invoke another event in the workflow, other than `StartEvent`, `StopEvent`, or the current event. (Or there will have circular reference)
- `requireEvent`: wait for a specific event to be emitted.

You can use `sendEvent` and `requireEvent` to build complex workflows.

```ts twoslash
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';

type ContextData = {
	counter: number;
}

const contextData: ContextData = { counter: 0 };

const workflow = new Workflow<ContextData, string, string>();

// ---cut---
class AnalysisStartEvent extends WorkflowEvent<string> {}
class AnalysisStopEvent extends WorkflowEvent<boolean> {}
workflow.addStep({
	inputs: [AnalysisStartEvent],
	outputs: [AnalysisStopEvent]
}, async (...args) => {
	// do some analysis
	return new AnalysisStopEvent(true);
})
workflow.addStep({
	inputs: [StartEvent<string>],
	outputs: [StopEvent<string>]
}, async (context, startEvent) => {
	const input = startEvent.data;
	context.sendEvent(new AnalysisStartEvent('start'));
	context.data.counter++;
	const { data } = await context.requireEvent(AnalysisStopEvent);
	return new StopEvent(`Hello, ${input}! Analysis result: ${data ? 'success' : 'fail'}`);
});
```

For example, you can compile `requireEvent` with `waitUntil` in [Vercel Functions](https://vercel.com/docs/functions/functions-api-reference#waituntil) or [Cloudflare Worker](https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil)

```ts twoslash
import { waitUntil } from '@vercel/functions';
import { Workflow, StartEvent, StopEvent, WorkflowEvent } from '@llamaindex/workflow';

type ContextData = {
	counter: number;
}

const contextData: ContextData = { counter: 0 };

const workflow = new Workflow<ContextData, string, string>();

class AnalysisStartEvent extends WorkflowEvent<string> {}
class AnalysisStopEvent extends WorkflowEvent<boolean> {}

// ---cut---
workflow.addStep({
	inputs: [StartEvent<string>],
	outputs: [StopEvent<string>]
}, async (context, startEvent) => {
	const input = startEvent.data;
	context.sendEvent(new AnalysisStartEvent('start'));
	context.data.counter++;
	waitUntil(context.requireEvent(AnalysisStopEvent));
	// note that `waitUntil` is not a promise, it will extend the lifetime of the workflow
	// you can wait for some background tasks to finish
	return new StopEvent(`Hello, ${input}!`);
});
```

## Multiple runs

You can run the same workflow multiple times with different inputs.

```ts twoslash
import { Workflow, StartEvent, StopEvent } from '@llamaindex/workflow';

type ContextData = {
	counter: number;
}

const contextData: ContextData = { counter: 0 };

const workflow = new Workflow<ContextData, string, string>();

workflow.addStep({
	inputs: [StartEvent<string>],
	outputs: [StopEvent<string>]
}, async (context, startEvent) => {
	const input = startEvent.data;
	context.data.counter++;
	return new StopEvent(`Hello, ${input}!`);
});

// ---cut---
{
	const ret = await workflow.run('Alex', contextData);
	console.log(ret.data); // Hello, Alex!
}

{
	const ret = await workflow.run('World', contextData);
	console.log(ret.data); // Hello, World!
}
```

Context is shared between runs, so the counter will be increased.

Ideally, it should be serializable to make sure it can be recovered from HTTP requests or other storage.

### Full example

<iframe
	className="w-full h-[440px]"
	aria-label="Workflow example"
	src="https://stackblitz.com/github/run-llama/LlamaIndexTS/tree/main/examples?file=node/workflow/basic.ts"
/>

## `Workflow` type

<AutoTypeTable path="./src/deps/type.ts" name="Workflow" />

## `WorkflowContext` type

<AutoTypeTable path="./src/deps/type.ts" name="WorkflowContext" />
