Skip to main content
<Workflow> is the root component of every Smithers workflow. It wraps all tasks and control-flow components into a single named unit that can be executed, cached, and resumed.

Import

import { Workflow } from "smithers-orchestrator";

Props

PropTypeDefaultDescription
namestring(required)A unique name identifying this workflow. Used in run metadata, logging, and the CLI.
cachebooleanfalseWhen true, enables per-node output caching. Completed tasks are skipped on resume and their cached outputs are reused.
childrenReactNodeundefinedThe tasks and control-flow components that make up the workflow graph.

Implicit sequencing

Direct children of <Workflow> are executed sequentially from top to bottom, in the order they appear in JSX. This means a bare <Workflow> behaves identically to wrapping its children in a <Sequence>:
// These two are equivalent:

<Workflow name="example">
  <Task id="first" output="first">{/* ... */}</Task>
  <Task id="second" output="second">{/* ... */}</Task>
</Workflow>

<Workflow name="example">
  <Sequence>
    <Task id="first" output="first">{/* ... */}</Task>
    <Task id="second" output="second">{/* ... */}</Task>
  </Sequence>
</Workflow>
You only need an explicit <Sequence> when nesting sequential groups inside <Parallel> or other control-flow components.

Caching

When cache is enabled, the runtime checks whether a task’s output row already exists in the database before executing it. If the row is present, the task is skipped and its stored output is used for downstream references. This makes workflows resumable after partial failures without re-running completed work.
<Workflow name="pipeline" cache>
  <Task id="expensive-step" output="expensiveStep" agent={myAgent}>
    Perform a costly analysis.
  </Task>
  <Task id="cheap-step" output="cheapStep">
    {{ status: "done" }}
  </Task>
</Workflow>
If expensive-step has already completed and its output is in the database, re-running the workflow skips it and proceeds directly to cheap-step.

Full example

import { smithers, Workflow, Task, Sequence } from "smithers-orchestrator";
import { ToolLoopAgent as Agent } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
import { drizzle } from "drizzle-orm/bun-sqlite";
import {
  sqliteTable,
  text,
  integer,
  primaryKey,
} from "drizzle-orm/sqlite-core";

const inputTable = sqliteTable("input", {
  runId: text("run_id").primaryKey(),
  topic: text("topic").notNull(),
});

const researchTable = sqliteTable(
  "research",
  {
    runId: text("run_id").notNull(),
    nodeId: text("node_id").notNull(),
    findings: text("findings").notNull(),
  },
  (t) => ({
    pk: primaryKey({ columns: [t.runId, t.nodeId] }),
  }),
);

const summaryTable = sqliteTable(
  "summary",
  {
    runId: text("run_id").notNull(),
    nodeId: text("node_id").notNull(),
    summary: text("summary").notNull(),
  },
  (t) => ({
    pk: primaryKey({ columns: [t.runId, t.nodeId] }),
  }),
);

const schema = {
  input: inputTable,
  output: summaryTable,
  research: researchTable,
  summary: summaryTable,
};

const db = drizzle("./workflow.db", { schema });

const researcher = new Agent({
  model: anthropic("claude-sonnet-4-20250514"),
  instructions: "You are a research assistant.",
});

export default smithers(db, (ctx) => (
  <Workflow name="research-pipeline" cache>
    <Task id="research" output={schema.research} agent={researcher}>
      {`Research the topic: ${ctx.input.topic}`}
    </Task>
    <Task id="summary" output={schema.summary}>
      {{ summary: "Workflow complete." }}
    </Task>
  </Workflow>
));

Rendering

Internally, <Workflow> renders as a <smithers:workflow> host element. The runtime traverses this element tree to extract TaskDescriptor objects and build the execution plan.

Notes

  • Every Smithers workflow must have exactly one <Workflow> at the root.
  • The name prop is used for identification in logs, the CLI, and event streams, but does not need to be globally unique across files.
  • Output tables referenced by child tasks must include runId and nodeId columns. Tasks inside a <Ralph> loop additionally need an iteration column.