Skip to main content

File Structure

A SirenSpec workflow is a YAML file with a top-level mapping. Three fields are required; the rest are optional.

version

Required. The schema version string. Currently only "0.1" is supported.

agents

Required. A mapping of agent IDs to agent definitions. Each agent wraps an LLM with a system prompt.
See Agents for full agent documentation and Providers for valid model URIs.

nodes

Required. A mapping of node IDs to node definitions. Six node types are supported: Agent node (default) — binds an LLM agent to an output path:
Tool node — invokes an HTTP endpoint or Python callable:
Swrm node — runs multiple agents in parallel with optional synthesis:
Factory node — spawns instances (single agents or full swrms) for each item in a list:
Or with inline swrm (one swrm per item):
Workflow node — executes a sub-workflow inline:
Human node — pauses execution to collect input from a human operator:

Agent node fields

Human node fields

A human node pauses execution to collect a response from a human operator. The node consumes no LLM tokens. The rendered prompt is shown via the configured input source (stdin by default), and the response is written to the workflow context just like an agent’s output. Downstream when: conditions can gate on the response.

Factory node fields

A factory node supports three mutually exclusive execution modes: Mode 1: Agent + for_each — one agent call per item in a runtime list (native list, JSON array, or fenced ```json).
Mode 2: Agent + swarm_size — N identical agent calls on the same input.
Mode 3: Swrm + for_each — one full swrm (parallel specialist agents with optional synthesis) per item in a runtime list (native list, JSON array, or fenced ```json).

Loop Variables

Inside inputs: templates, agent prompts, and swrm agent/synthesis prompts (when using swrm mode), three special variables are available: Each key under inputs: is additionally available in the spawned agent’s system prompt as {{ inputs.<key> }} once resolved. For example, inputs: { task: "{{ item }}" } makes {{ inputs.task }} reference the current item inside the agent’s prompt.

Swrm Within Factory

The swrm field in factory nodes accepts: Agent prompts and synthesis prompts support all standard interpolation namespaces plus the three loop variables ({{ item }}, {{ index }}, {{ total }}). See the following pages for detailed field reference and examples:

Context paths

The writes field uses dot-notation to specify where output is written in the workflow context: Examples:
  • output.reply — final response available in the JSON trace.
  • working.intent — intermediate classification readable by the next node.
  • working.triage.intent — nested intermediate state.

edges

Optional. A list of directed edges connecting nodes. If omitted, all nodes are treated as roots and execute in definition order.

when expressions

The when field enables conditional branching. The expression is evaluated after the source node writes its output to the context. Available names in when expressions: No imports are available and __builtins__ is otherwise cleared, so arbitrary code execution is not possible — only the names above are in scope.
If a when expression raises an error (missing key, syntax error, type mismatch), it is treated as false and the edge is not traversed. Edges without when are always traversed.

input

Optional. A static input message for the first node. Can be overridden at runtime with the --input CLI flag.
If neither input.message nor --input is provided, the CLI exits with an error.

state

Optional. Initial state to seed the workflow context before execution begins.
State is merged into the corresponding working and output context buckets at startup. Nodes can read and overwrite these values during execution.

env_file

Optional. Path to a .env file (relative to the workflow file) loaded into os.environ before execution. This lets provider clients and {{ env.* }} templates pick up credentials without exporting them manually.
The file is loaded eagerly inside load_workflow() — before the Workflow object is returned — so provider clients that read os.environ at construction time see the values regardless of call order. Existing environment variables are never overwritten: if a key in the .env file is already present in os.environ as an empty string, an EnvFileShadowWarning is emitted (the existing empty value wins, which is usually a misconfiguration). A missing env_file raises FileNotFoundError.

defaults

Optional. Workflow-wide defaults for retry and failure handling, inherited by all nodes that do not specify their own.
See Retry Policies for the full field reference.

guardrails

Optional. A list of guardrail names (or guardrail specs with configuration) applied globally to all agents. Defaults to ["injection"] if omitted.

Simple guardrails (no configuration)

Guardrails with configuration

Some guardrails require configuration. Use the name and config fields:
An empty list ([]) disables all guardrails for the entire workflow. Individual agents can override this with their own guardrails field. See Guardrails for full details.

budget

Optional. Workflow-level cumulative budget for token, USD, and wall-clock spend. The executor checks the running totals after every node and enforces the ceilings declared here. At least one of max_tokens, max_cost_usd, or max_duration_s must be set — an empty budget: block is rejected at validation time.

Budget status in the trace

When a budget: block is configured, the trace summary includes a budget block:

Per-node max_tokens_per_call

Agent nodes also support a per-call ceiling:
max_tokens_per_call is forwarded to the provider as the max_tokens API parameter so the LLM truncates its own response. Combined with the workflow budget: block, this gives you two layers of protection: each individual call is bounded and the cumulative spend is bounded.

Complete Example