Cookbook
workflows
Multi-step pipeline: validate → classify (LLM) → branch (billing/support) → summarize.
workflows
Multi-step pipeline: validate → classify (LLM) → branch (billing/support) → summarize.
Demonstrates the Workflow.create / .then / .branch / .commit / .run API
(Adoption Roadmap #5; ADRs D230-D248).
Run (Ollama)
ollama serve &
ollama pull llama3.2:3b
pnpm install
pnpm run runRun (OpenRouter cloud)
export OPENROUTER_API_KEY=sk-or-...
pnpm run runWhat it shows
Workflow.create({ name }).then(fn(...)).then(agentStep(...)).branch(...).commit()declarative DSL.fn(id, handler)for pure-function steps (validation, transformation).agentStep(id, agent, promptTemplate)for LLM-driven steps..branch([[predicate, [...]], [...]], { fallback })first-match-wins routing.WorkflowRun.stepResultsarray with per-step status, attempts, duration.
v1 limitations
- LocalAgent only (
CloudAgentworkflow steps throwUnsupportedRunOperationError, ADR D244). - Saga compensation deferred to v1.2 —
compensate?slot reserved onFnStepbut engine not yet implemented (ADR D238). - Persistence: default in-memory. Use
Workflow.create({ persistence: { backend: "json", dir: ".theokit/workflows" } })for filesystem snapshots.
Other primitives not shown
.parallel([branchA, branchB], { concurrency })— fan-out N concurrent branches..foreach("sourceStepId", innerStep, { concurrency })— map over array output..dowhile(step, cond, { maxIterations })— loop until predicate is false (default cap 100)..sleep(ms)— pause for fixed duration..suspend({ payloadSchema })+Workflow.resume({ runId, payload })— human-in-the-loop.
Code
/**
* Workflow demo (Adoption Roadmap #5; ADRs D230-D248).
*
* Demonstrates a 4-step refund triage pipeline:
* validate → classify (LLM) → branch (billing vs support) → summarize
*
* Run:
* pnpm install
* pnpm run run
*
* Requires `OPENROUTER_API_KEY` OR a local Ollama daemon listening on
* `OLLAMA_HOST` (default http://localhost:11434).
*/
import { Agent, Workflow, agentStep, fn } from "@theokit/sdk";
const OPENROUTER = process.env.OPENROUTER_API_KEY;
const useOllama = OPENROUTER === undefined || OPENROUTER.length === 0;
const baseConfig = useOllama
? {
model: { id: "ollama/llama3.2:3b" },
local: { cwd: process.cwd(), sandboxOptions: { enabled: false } as const },
}
: {
apiKey: OPENROUTER!,
model: { id: "openai/gpt-4o-mini" },
local: { cwd: process.cwd(), sandboxOptions: { enabled: false } as const },
};
async function main(): Promise<void> {
const classifier = await Agent.create({
...baseConfig,
name: "classifier",
systemPrompt:
"You classify customer support requests. Reply with EXACTLY one word: BILLING, SUPPORT, or OTHER.",
});
const billingExpert = await Agent.create({
...baseConfig,
name: "billing",
systemPrompt:
"You are a billing specialist. Answer concisely (1-2 sentences) about invoices, refunds, charges.",
});
const supportExpert = await Agent.create({
...baseConfig,
name: "support",
systemPrompt:
"You are a technical support specialist. Answer concisely (1-2 sentences) about install, config, troubleshooting.",
});
const wf = Workflow.create<{ claim: string }, string>({ name: "refund-pipeline" })
.then(
fn<{ claim: string }, { claim: string; ts: number }>("validate", (input) => {
if (!input.claim || input.claim.length < 3) {
throw new Error("claim must be at least 3 characters");
}
return { ...input, ts: Date.now() };
}),
)
.then(
agentStep(
"classify",
classifier,
(input) => `Classify: "${(input as { claim: string }).claim}"`,
),
)
.branch(
[
[(out) => String(out).toUpperCase().includes("BILLING"), [
agentStep("billing_resolve", billingExpert, "Handle the billing question."),
]],
[(out) => String(out).toUpperCase().includes("SUPPORT"), [
agentStep("support_resolve", supportExpert, "Handle the support question."),
]],
],
{
id: "decide",
fallback: [
fn("escalate", () => "Escalating to a human agent."),
],
},
)
.commit();
console.log("Running workflow…");
const run = await wf.run({ claim: "I was charged twice last month for the Pro plan." });
console.log("");
console.log("Status:", run.status);
console.log("Duration:", run.durationMs, "ms");
console.log("Steps:");
for (const sr of run.stepResults) {
console.log(` - [${sr.kind}] ${sr.stepId} (${sr.status}, ${sr.attempts} attempt, ${sr.durationMs}ms)`);
}
console.log("");
console.log("Final output:", run.output);
await classifier.dispose();
await billingExpert.dispose();
await supportExpert.dispose();
}
main().catch((err) => {
console.error("workflow demo failed:", err);
process.exit(1);
});
Run
cd examples/workflows
cp .env.example .env # fill in keys
pnpm install
pnpm run run