mirror of
https://github.com/run-llama/workflows-ts.git
synced 2026-07-21 06:05:23 -04:00
Updating docs package with code + tests from examples (#183)
Co-authored-by: Marcus Schiesser <mail@marcusschiesser.de>
This commit is contained in:
committed by
GitHub
parent
ff3ea0f181
commit
3cda388550
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@llamaindex/workflow-viz": patch
|
||||
"@llamaindex/workflow-docs": patch
|
||||
---
|
||||
|
||||
Adding an export for type WorkflowWithDrawing in viz; Adding example code + tests in docs
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"root": false,
|
||||
"extends": "//",
|
||||
"files": {
|
||||
"includes": ["src/**", "tests/**"]
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useTemplate": "off",
|
||||
"noNonNullAssertion": "off"
|
||||
},
|
||||
"complexity": {
|
||||
"noStaticOnlyClass": "off",
|
||||
"useOptionalChain": "off",
|
||||
"noBannedTypes": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noUnreachable": "off"
|
||||
},
|
||||
"suspicious": {
|
||||
"noExplicitAny": "off",
|
||||
"noConfusingVoidType": "off",
|
||||
"useIterableCallbackReturn": "off",
|
||||
"noShadowRestrictedNames": "off",
|
||||
"noDoubleEquals": "off",
|
||||
"noAssignInExpressions": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
-1
@@ -1,8 +1,48 @@
|
||||
{
|
||||
"name": "@llamaindex/workflow-docs",
|
||||
"version": "0.1.4",
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"packageManager": "pnpm@10.15.0",
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/run-llama/workflows-ts.git"
|
||||
},
|
||||
"keywords": [
|
||||
"workflows",
|
||||
"typescript",
|
||||
"llamaindex",
|
||||
"ai",
|
||||
"llms",
|
||||
"genai"
|
||||
],
|
||||
"author": "LlamaIndex",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/run-llama/workflows-ts/issues"
|
||||
},
|
||||
"homepage": "https://github.com/run-llama/workflows-ts/blob/main/examples/README.md",
|
||||
"devDependencies": {
|
||||
"@llamaindex/workflow-core": "workspace:*",
|
||||
"@llamaindex/workflow-otel": "workspace:^",
|
||||
"@llamaindex/workflow-viz": "workspace:^",
|
||||
"@opentelemetry/sdk-node": "^0.203.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@llamaindex/workflow-core": "workspace:*",
|
||||
"@llamaindex/workflow-otel": "workspace:^",
|
||||
"@llamaindex/workflow-viz": "workspace:^",
|
||||
"@opentelemetry/sdk-node": "^0.203.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.0.1"
|
||||
},
|
||||
"files": [
|
||||
"workflows"
|
||||
"workflows",
|
||||
"src"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
|
||||
export const workflow = createWorkflow();
|
||||
export const inputEvent = workflowEvent<string | number>();
|
||||
const processStringEvent = workflowEvent<string>();
|
||||
const processNumberEvent = workflowEvent<number>();
|
||||
export const successEvent = workflowEvent<string>();
|
||||
|
||||
workflow.handle([inputEvent], async (context, event) => {
|
||||
if (typeof event.data === "string") {
|
||||
return processStringEvent.with(event.data);
|
||||
} else {
|
||||
return processNumberEvent.with(event.data);
|
||||
}
|
||||
});
|
||||
|
||||
workflow.handle([processStringEvent], async (context, event) => {
|
||||
return successEvent.with(`Processed string ${event.data}`);
|
||||
});
|
||||
|
||||
workflow.handle([processNumberEvent], async (context, event) => {
|
||||
return successEvent.with(`Processed number ${event.data}`);
|
||||
});
|
||||
|
||||
let context1 = workflow.createContext();
|
||||
context1.sendEvent(inputEvent.with("I am some data"));
|
||||
|
||||
const result = await context1.stream.until(successEvent).toArray();
|
||||
console.log(result.at(-1)!.data);
|
||||
|
||||
let context2 = workflow.createContext();
|
||||
context2.sendEvent(inputEvent.with(1));
|
||||
|
||||
const result2 = await context2.stream.until(successEvent).toArray();
|
||||
console.log(result2.at(-1)!.data);
|
||||
@@ -0,0 +1,70 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
import { createStatefulMiddleware } from "@llamaindex/workflow-core/middleware/state";
|
||||
|
||||
// Define the events we'll use
|
||||
export const startEvent = workflowEvent<string>(); // Triggers the fan-out process
|
||||
export const processItemEvent = workflowEvent<number>(); // Individual items to process
|
||||
export const resultEvent = workflowEvent<string>(); // Results from processed items
|
||||
export const completeEvent = workflowEvent<string[]>(); // Final aggregated results
|
||||
const { withState } = createStatefulMiddleware(() => ({
|
||||
itemsToProcess: 10,
|
||||
itemsProcessed: 0,
|
||||
processResults: [] as string[],
|
||||
}));
|
||||
export const workflow = withState(createWorkflow());
|
||||
workflow.handle([startEvent], async (context, start) => {
|
||||
const { sendEvent, state } = context;
|
||||
state.itemsProcessed = 0; // Reset counter for this execution
|
||||
|
||||
// Fan out: emit multiple events to be processed in parallel
|
||||
for (let i = 0; i < state.itemsToProcess; i++) {
|
||||
sendEvent(processItemEvent.with(i));
|
||||
}
|
||||
});
|
||||
workflow.handle([processItemEvent], async (context, event) => {
|
||||
const { sendEvent, state } = context;
|
||||
|
||||
// Simulate some async work (like API calls, database operations, etc.)
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.random() * 100));
|
||||
|
||||
// Process the item
|
||||
const processedValue = `Processed: ${event.data}`;
|
||||
|
||||
// Update the shared counter after processing completes
|
||||
state.itemsProcessed++;
|
||||
|
||||
// Return the result event
|
||||
sendEvent(resultEvent.with(processedValue));
|
||||
});
|
||||
workflow.handle([resultEvent], async (context, event) => {
|
||||
const { sendEvent, state } = context;
|
||||
|
||||
// store the processed message
|
||||
state.processResults.push(event.data);
|
||||
|
||||
// return completeEvent if the processing is completed
|
||||
if (state.itemsProcessed === state.itemsToProcess) {
|
||||
sendEvent(completeEvent.with(state.processResults));
|
||||
}
|
||||
});
|
||||
async function runFanOut() {
|
||||
console.log("Running fan-out workflow");
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
|
||||
// Start the fan-out process
|
||||
sendEvent(startEvent.with("Start fan-out"));
|
||||
|
||||
// Listen to all events as they occur
|
||||
for await (const event of stream) {
|
||||
if (processItemEvent.include(event)) {
|
||||
console.log(`Processing item: ${event.data}`);
|
||||
} else if (resultEvent.include(event)) {
|
||||
console.log(`Result received: ${event.data}`);
|
||||
} else if (completeEvent.include(event)) {
|
||||
console.log("Final aggregated results:", event.data);
|
||||
break; // All done!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runFanOut().catch(console.error);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
import { createStatefulMiddleware } from "@llamaindex/workflow-core/middleware/state";
|
||||
|
||||
// Define events
|
||||
export const startEvent = workflowEvent<string>();
|
||||
export const humanRequestEvent = workflowEvent<void>();
|
||||
export const humanResponseEvent = workflowEvent<string>();
|
||||
export const stopEvent = workflowEvent<string>();
|
||||
|
||||
const { withState } = createStatefulMiddleware(() => ({}));
|
||||
export const workflow = withState(createWorkflow());
|
||||
|
||||
// Workflow that needs human input
|
||||
workflow.handle([startEvent], () => {
|
||||
return humanRequestEvent.with();
|
||||
});
|
||||
|
||||
workflow.handle([humanResponseEvent], (context, event) => {
|
||||
return stopEvent.with(`Human said: ${event.data}`);
|
||||
});
|
||||
|
||||
// Usage with snapshot/resume
|
||||
const { sendEvent, snapshot, stream } = workflow.createContext();
|
||||
sendEvent(startEvent.with("begin"));
|
||||
|
||||
// Wait for a human request and take a snapshot
|
||||
await stream.until(humanRequestEvent).toArray();
|
||||
const snapshotData = await snapshot();
|
||||
|
||||
// Later (in another request): resume and provide human input
|
||||
const resumedContext = workflow.resume(snapshotData);
|
||||
resumedContext.sendEvent(humanResponseEvent.with("hello world"));
|
||||
|
||||
const resultEvent = await resumedContext.stream.untilEvent(stopEvent);
|
||||
console.log(resultEvent.data); // "Human said: hello world"
|
||||
@@ -0,0 +1,50 @@
|
||||
import * as branching from "./branching";
|
||||
import * as fanInFanOut from "./fan_in_fan_out";
|
||||
import * as humanInTheLoop from "./human_in_the_loop";
|
||||
import * as loops from "./loops";
|
||||
import * as state from "./state";
|
||||
import * as tracingBase from "./tracing_base";
|
||||
import * as tracingPlugin from "./tracing_plugin";
|
||||
import * as workflowViz from "./workflow_viz";
|
||||
|
||||
// Branching exports
|
||||
export const branchingWorkflow = branching.workflow;
|
||||
export const branchingInputEvent = branching.inputEvent;
|
||||
export const branchingSuccessEvent = branching.successEvent;
|
||||
|
||||
// Fan-in Fan-out exports
|
||||
export const fanInFanOutWorkflow = fanInFanOut.workflow;
|
||||
export const fanInFanOutStartEvent = fanInFanOut.startEvent;
|
||||
export const fanInFanOutProcessItemEvent = fanInFanOut.processItemEvent;
|
||||
export const fanInFanOutResultEvent = fanInFanOut.resultEvent;
|
||||
export const fanInFanOutCompleteEvent = fanInFanOut.completeEvent;
|
||||
|
||||
// Human in the loop exports
|
||||
export const humanInTheLoopWorkflow = humanInTheLoop.workflow;
|
||||
export const humanInTheLoopStartEvent = humanInTheLoop.startEvent;
|
||||
export const humanInTheLoopHumanRequestEvent = humanInTheLoop.humanRequestEvent;
|
||||
export const humanInTheLoopHumanResponseEvent =
|
||||
humanInTheLoop.humanResponseEvent;
|
||||
export const humanInTheLoopStopEvent = humanInTheLoop.stopEvent;
|
||||
|
||||
// Loops exports
|
||||
export const loopsWorkflow = loops.workflow;
|
||||
export const loopsStartEvent = loops.startEvent;
|
||||
export const loopsStopEvent = loops.stopEvent;
|
||||
|
||||
// State exports
|
||||
export const stateWorkflow = state.workflow;
|
||||
export const stateStartEvent = state.startEvent;
|
||||
export const stateStopEvent = state.stopEvent;
|
||||
|
||||
// Tracing base exports
|
||||
export const tracingBaseWorkflow = tracingBase.workflow;
|
||||
export const tracingBaseStepEvent = tracingBase.stepEvent;
|
||||
|
||||
// Tracing plugin exports
|
||||
export const tracingPluginWorkflow = tracingPlugin.workflow;
|
||||
|
||||
// Workflow viz exports
|
||||
export const workflowVizWorkflow = workflowViz.workflow;
|
||||
export const workflowVizStartEvent = workflowViz.startEvent;
|
||||
export const workflowVizDoneEvent = workflowViz.doneEvent;
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
import { createStatefulMiddleware } from "@llamaindex/workflow-core/middleware/state";
|
||||
|
||||
type AgentWorkflowState = {
|
||||
counter: number;
|
||||
max_counter: number;
|
||||
};
|
||||
|
||||
const { withState } = createStatefulMiddleware(
|
||||
(state: AgentWorkflowState) => state,
|
||||
);
|
||||
export const workflow = withState(createWorkflow());
|
||||
|
||||
export const startEvent = workflowEvent<void>();
|
||||
const increaseCounterEvent = workflowEvent<void>();
|
||||
export const stopEvent = workflowEvent<number>();
|
||||
|
||||
workflow.handle([startEvent], async (context, { data }) => {
|
||||
const { sendEvent, state } = context;
|
||||
if (state.counter < state.max_counter) {
|
||||
sendEvent(increaseCounterEvent.with());
|
||||
} else {
|
||||
sendEvent(stopEvent.with(state.counter));
|
||||
}
|
||||
});
|
||||
|
||||
workflow.handle([increaseCounterEvent], async (context, { data }) => {
|
||||
const { sendEvent, state } = context;
|
||||
state.counter += 1;
|
||||
sendEvent(startEvent.with());
|
||||
});
|
||||
|
||||
const { stream, sendEvent } = workflow.createContext({
|
||||
counter: 0,
|
||||
max_counter: 5,
|
||||
});
|
||||
|
||||
sendEvent(startEvent.with());
|
||||
|
||||
const result = await stream.untilEvent(stopEvent);
|
||||
|
||||
// should print 5 since the workflow is looping
|
||||
console.log(result.data);
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createWorkflow } from "@llamaindex/workflow-core";
|
||||
import { createStatefulMiddleware } from "@llamaindex/workflow-core/middleware/state";
|
||||
import { workflowEvent } from "@llamaindex/workflow-core";
|
||||
|
||||
type MyWorkflowState = {
|
||||
previous_message: string;
|
||||
};
|
||||
|
||||
const { withState } = createStatefulMiddleware(
|
||||
(state: MyWorkflowState) => state,
|
||||
);
|
||||
export const workflow = withState(createWorkflow());
|
||||
|
||||
export const startEvent = workflowEvent<{ userInput: string }>();
|
||||
export const stopEvent = workflowEvent<{ result: string }>();
|
||||
|
||||
workflow.handle([startEvent], async (context, { data }) => {
|
||||
const { sendEvent, state } = context;
|
||||
const { userInput } = data;
|
||||
|
||||
const previous_message = state.previous_message;
|
||||
state.previous_message = userInput;
|
||||
|
||||
return stopEvent.with({
|
||||
result:
|
||||
"Processed message: " +
|
||||
userInput +
|
||||
" previous message: " +
|
||||
previous_message,
|
||||
});
|
||||
});
|
||||
const { stream, sendEvent } = workflow.createContext({
|
||||
previous_message: "my initial previous message",
|
||||
});
|
||||
|
||||
sendEvent(startEvent.with({ userInput: "Hello, how are you?" }));
|
||||
|
||||
const result = await stream.untilEvent(stopEvent);
|
||||
console.log(result.data);
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
import { withTraceEvents } from "@llamaindex/workflow-core/middleware/trace-events";
|
||||
import { openTelemetry } from "@llamaindex/workflow-otel";
|
||||
|
||||
import { NodeSDK } from "@opentelemetry/sdk-node";
|
||||
import {
|
||||
ConsoleSpanExporter,
|
||||
SimpleSpanProcessor,
|
||||
} from "@opentelemetry/sdk-trace-base";
|
||||
|
||||
// Initialize OpenTelemetry SDK (use your preferred exporter in real deployments)
|
||||
const sdk = new NodeSDK({
|
||||
traceExporter: new ConsoleSpanExporter(),
|
||||
spanProcessor: new SimpleSpanProcessor(new ConsoleSpanExporter()),
|
||||
});
|
||||
sdk.start();
|
||||
|
||||
// Define events
|
||||
const startEvent = workflowEvent();
|
||||
export const stepEvent = workflowEvent<{ value: string }>();
|
||||
|
||||
// Create workflow and attach the OpenTelemetry plugin
|
||||
export const workflow = withTraceEvents(createWorkflow(), {
|
||||
plugins: [openTelemetry],
|
||||
});
|
||||
|
||||
// Handlers automatically produce spans (including errors)
|
||||
workflow.handle([startEvent], (context) => {
|
||||
context.sendEvent(stepEvent.with({ value: "hello!" }));
|
||||
context.sendEvent(stepEvent.with({ value: "crash!" })); // demonstrates error spans
|
||||
});
|
||||
|
||||
workflow.handle([stepEvent], (_context, event) => {
|
||||
if (event.data.value === "crash!") {
|
||||
throw new Error("The ultimate error happened!");
|
||||
}
|
||||
});
|
||||
|
||||
// Run
|
||||
const { sendEvent, stream } = workflow.createContext();
|
||||
sendEvent(startEvent.with());
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createWorkflow, workflowEvent } from "@llamaindex/workflow-core";
|
||||
import {
|
||||
withTraceEvents,
|
||||
createHandlerDecorator,
|
||||
} from "@llamaindex/workflow-core/middleware/trace-events";
|
||||
|
||||
const startEvent = workflowEvent();
|
||||
|
||||
// Create a decorator-based plugin
|
||||
type Timing = { startedAt: number | null };
|
||||
const timingPlugin = createHandlerDecorator<Timing>({
|
||||
debugLabel: "timing",
|
||||
getInitialValue: () => ({ startedAt: null }),
|
||||
onBeforeHandler:
|
||||
(h, _ctx, metadata) =>
|
||||
async (...args) => {
|
||||
metadata.startedAt = Date.now();
|
||||
try {
|
||||
// @ts-expect-error - Expecting: A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)
|
||||
return await h(...(args as any));
|
||||
} finally {
|
||||
const durationMs = Date.now() - (metadata.startedAt ?? Date.now());
|
||||
console.log("[trace] handler duration (ms):", durationMs);
|
||||
}
|
||||
},
|
||||
onAfterHandler: () => ({ startedAt: null }),
|
||||
});
|
||||
|
||||
// Attach your plugin to the workflow
|
||||
export const workflow = withTraceEvents(createWorkflow(), {
|
||||
plugins: [timingPlugin],
|
||||
});
|
||||
|
||||
workflow.handle([startEvent], async () => {
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
});
|
||||
|
||||
const { sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with());
|
||||
@@ -0,0 +1,11 @@
|
||||
import { workflow } from "./workflow_viz";
|
||||
|
||||
const container = document.getElementById("app") as HTMLElement;
|
||||
|
||||
// Optional settings:
|
||||
// - layout: "force" | "none" (defaults to "force")
|
||||
// - Any Sigma renderer setting can be passed as well, e.g. `defaultEdgeColor`
|
||||
workflow.draw(container, {
|
||||
layout: "force",
|
||||
defaultEdgeColor: "#999",
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
createWorkflow,
|
||||
workflowEvent,
|
||||
type Workflow,
|
||||
} from "@llamaindex/workflow-core";
|
||||
import { withDrawing } from "@llamaindex/workflow-viz";
|
||||
|
||||
// Define events (debug labels are used for node names in the graph)
|
||||
export const startEvent = workflowEvent<string>({ debugLabel: "start" });
|
||||
export const doneEvent = workflowEvent<string>({ debugLabel: "done" });
|
||||
|
||||
// Decorate your workflow to enable drawing
|
||||
export const workflow = withDrawing(createWorkflow());
|
||||
|
||||
workflow.handle([startEvent], (_ctx, start) => {
|
||||
return doneEvent.with(`Hello ${start.data}`);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { workflow, inputEvent, successEvent } from "../src/branching";
|
||||
|
||||
describe("Branching workflow should return expected results", () => {
|
||||
test("Sending event with context1", async () => {
|
||||
let context1 = workflow.createContext();
|
||||
context1.sendEvent(inputEvent.with("I am some data"));
|
||||
|
||||
const result = await context1.stream.until(successEvent).toArray();
|
||||
expect(result.at(-1)!.data).toBe("Processed string I am some data");
|
||||
});
|
||||
test("Sending event with context2", async () => {
|
||||
let context2 = workflow.createContext();
|
||||
context2.sendEvent(inputEvent.with(1));
|
||||
|
||||
const result2 = await context2.stream.until(successEvent).toArray();
|
||||
expect(result2.at(-1)!.data).toBe("Processed number 1");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
workflow,
|
||||
startEvent,
|
||||
completeEvent,
|
||||
processItemEvent,
|
||||
resultEvent,
|
||||
} from "../src/fan_in_fan_out";
|
||||
|
||||
describe("Fan-In/Fan-Out workflow should stream expected events", () => {
|
||||
test("Test Fan-In/Fan-Out e2e with streaming", async () => {
|
||||
const { stream, sendEvent } = workflow.createContext();
|
||||
sendEvent(startEvent.with("Start fan-out"));
|
||||
|
||||
for await (const event of stream) {
|
||||
if (processItemEvent.include(event)) {
|
||||
expect(event.data).toBeLessThan(10);
|
||||
} else if (resultEvent.include(event)) {
|
||||
expect(event.data.startsWith("Processed: ")).toBeTruthy();
|
||||
} else if (completeEvent.include(event)) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const contained = `Processed: ${i}`;
|
||||
expect(event.data).toContain(contained);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
workflow,
|
||||
startEvent,
|
||||
stopEvent,
|
||||
humanRequestEvent,
|
||||
humanResponseEvent,
|
||||
} from "../src/human_in_the_loop";
|
||||
|
||||
describe("Human in the loop returns expected results", () => {
|
||||
test("Test Human In The Loop e2e", async () => {
|
||||
const { sendEvent, snapshot, stream } = workflow.createContext();
|
||||
sendEvent(startEvent.with("begin"));
|
||||
|
||||
// Wait for a human request and take a snapshot
|
||||
await stream.until(humanRequestEvent).toArray();
|
||||
const snapshotData = await snapshot();
|
||||
|
||||
// Later (in another request): resume and provide human input
|
||||
const resumedContext = workflow.resume(snapshotData);
|
||||
resumedContext.sendEvent(humanResponseEvent.with("hello world"));
|
||||
|
||||
const events = await resumedContext.stream.until(stopEvent).toArray();
|
||||
expect(events[events.length - 1].data).toBe("Human said: hello world");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { workflow, startEvent, stopEvent } from "../src/loops";
|
||||
|
||||
describe("Loops Workflow returns expected results", () => {
|
||||
test("Test Loops Workflow e2e", async () => {
|
||||
const { stream, sendEvent } = workflow.createContext({
|
||||
counter: 0,
|
||||
max_counter: 5,
|
||||
});
|
||||
|
||||
sendEvent(startEvent.with());
|
||||
|
||||
const result = await stream.until(stopEvent).toArray();
|
||||
|
||||
expect(result[result.length - 1].data).toBe(5);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { workflow, startEvent, stopEvent } from "../src/state";
|
||||
|
||||
describe("State Workflow returns expected results", () => {
|
||||
test("Test State Workflow e2e", async () => {
|
||||
const { stream, sendEvent } = workflow.createContext({
|
||||
previous_message: "my initial previous message",
|
||||
});
|
||||
|
||||
sendEvent(startEvent.with({ userInput: "Hello, how are you?" }));
|
||||
|
||||
const result = await stream.untilEvent(stopEvent);
|
||||
expect(result.data.result).toBe(
|
||||
"Processed message: Hello, how are you? previous message: my initial previous message",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./lib",
|
||||
"tsBuildInfoFile": "./lib/.tsbuildinfo",
|
||||
"paths": {
|
||||
"@llamaindex/workflow-docs": ["./src/index.ts"]
|
||||
}
|
||||
},
|
||||
"include": ["./src", "./workflows"]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "node",
|
||||
include: ["tests/*.test.ts"],
|
||||
},
|
||||
});
|
||||
@@ -35,7 +35,7 @@ const completeEvent = workflowEvent<string[]>(); // Final aggregated results
|
||||
|
||||
Next, we create our workflow and set up tracking variables in the state:
|
||||
|
||||
```javascript
|
||||
```ts
|
||||
const { withState } = createStatefulMiddleware(() => ({
|
||||
itemsToProcess: 10,
|
||||
itemsProcessed: 0,
|
||||
|
||||
@@ -33,7 +33,7 @@ const workflow = withState(createWorkflow());
|
||||
|
||||
And then, in your step handling logic, you can access and modify state properties:
|
||||
|
||||
```ts
|
||||
```ts
|
||||
import { workflowEvent } from "@llamaindex/workflow-core";
|
||||
|
||||
const startEvent = workflowEvent<{ userInput: string }>();
|
||||
@@ -52,7 +52,7 @@ workflow.handle([startEvent], async (context, { data }) => {
|
||||
|
||||
Before running the workflow, remember to initialize the context object with the state:
|
||||
|
||||
```typescript
|
||||
```ts
|
||||
const { stream, sendEvent } = workflow.createContext({
|
||||
previous_message: "my initial previous message",
|
||||
});
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.15.0",
|
||||
"scripts": {
|
||||
"build": "turbo build --filter=\"./packages/*\"",
|
||||
"test": "turbo test --filter=\"./packages/*\"",
|
||||
"build": "turbo build --filter=\"./packages/*\" --filter=\"./docs\"",
|
||||
"test": "turbo test --filter=\"./packages/*\" --filter=\"./docs\"",
|
||||
"typecheck": "tsc -b --diagnostics",
|
||||
"format": "biome format .",
|
||||
"format:write": "biome format --write .",
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { withDrawing } from "./drawing";
|
||||
export { withDrawing, type WithDrawingWorkflow } from "./drawing";
|
||||
|
||||
Generated
+17
-1
@@ -277,7 +277,23 @@ importers:
|
||||
specifier: 5.8.3
|
||||
version: 5.8.3
|
||||
|
||||
docs: {}
|
||||
docs:
|
||||
devDependencies:
|
||||
'@llamaindex/workflow-core':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/core
|
||||
'@llamaindex/workflow-otel':
|
||||
specifier: workspace:^
|
||||
version: link:../packages/otel
|
||||
'@llamaindex/workflow-viz':
|
||||
specifier: workspace:^
|
||||
version: link:../packages/viz
|
||||
'@opentelemetry/sdk-node':
|
||||
specifier: ^0.203.0
|
||||
version: 0.203.0(@opentelemetry/api@1.9.0)
|
||||
'@opentelemetry/sdk-trace-base':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1(@opentelemetry/api@1.9.0)
|
||||
|
||||
packages/core:
|
||||
devDependencies:
|
||||
|
||||
Reference in New Issue
Block a user