Chersus

Documentation Coming soon

Workflows

Conditional edges and bounded loops on the pipeline: branch on a verdict, retry with a cap, route on the result. Planned, coming soon.

Last updated

Workflows extend the pipeline with conditional edges and loops, so a run can branch on a result: “if toxic, rewrite and re-check, up to three times, then route”. They are planned and not live yet; this page describes the contract they will ship with. Every linear pipeline is a valid workflow, so nothing you build on chains today changes.

Billing is unchanged: every executed step bills the characters it processes. A node that runs five times is five billed steps. Conditions and routing are never billed, because neither runs a model.

Conditional edges

The pipeline array stays the primary structure. A node with no next continues to the next item in the array, exactly as chains work today. Conditional flow is added, never required.

A node may carry a next list of edges. The first edge whose when matches is taken; if none match, execution continues to the next array item. goto names the target node id, or "end" to finish the run.

{
  "id": "tox",
  "service": "security.toxicity.flag",
  "next": [
    { "when": { "verdict": "flag" }, "goto": "soften", "max_iterations": 3 },
    { "goto": "intent" }
  ]
}

A when condition reads this node’s own result, using the same vocabulary as stop_if: { "verdict": "flag" }, { "score_gte": 0.9 }, plus path matching for extract results, such as { "path": "data.intent", "equals": "billing.dispute" }. An edge with no when is unconditional and acts as the fallback.

Bounded loops

An edge that points to an earlier node, or to itself, must carry max_iterations: how many times that edge may be taken in one run, with a hard ceiling of 1000. When an edge has spent its iterations and would match again, on_max decides:

  • "skip" (default): the edge is ignored and the remaining edges are evaluated, so an unconditional fallback edge takes over.
  • "halt": the run stops with halted_reason: "max_iterations".

A worked example

If a message is flagged toxic, rewrite the tone and re-check, at most three times; otherwise route the intent. content.tone.rewrite is a forthcoming content service, shown here because it is the natural fit.

{
  "input": { "type": "text", "value": "..." },
  "pipeline": [
    { "id": "shield", "service": "security.jailbreak.shield", "stop_if": { "verdict": "flag" } },
    { "id": "redact", "service": "governance.pii.redact" },
    {
      "id": "tox",
      "service": "security.toxicity.flag",
      "next": [
        { "when": { "verdict": "flag" }, "goto": "soften", "max_iterations": 3 },
        { "goto": "intent" }
      ]
    },
    {
      "id": "soften",
      "service": "content.tone.rewrite",
      "next": [ { "goto": "tox" } ]
    },
    { "id": "intent", "service": "routing.intent.map", "params": { "catalog": "support-v3" } }
  ],
  "options": { "max_steps": 20, "max_ms": 5000 }
}

Worst case: shield, redact, tox, then up to three rounds of soften and tox, then intent via the fallback edge. Ten steps. If tox still flags after the third round, the loop edge is skipped and the fallback edge routes to intent.

Run-level limits

Workflows accept run-level options. Caps protect both sides from runaway runs.

Option Default Ceiling Notes
max_steps 100 1000 Executed steps only
max_ms 30000 60000 Wall clock for the whole run
max_iterations required on backward edges 1000 Set on the edge, not the run

Halting

Any cap hit or stop_if trigger returns status: "halted" with halted_at (the last executed step), a halted_reason (stop_if, max_steps, max_ms, or max_iterations), and the payload as it stood at the halt. Billing covers what ran.

Validation

Before any compute, the validator rejects runs that cannot succeed: unknown goto targets, backward edges without max_iterations, type mismatches along any reachable path, and worst-case step counts above the run’s max_steps. A rejected run never runs, and never bills.

Billing and audit in workflows

The billing rule applies unchanged, counting executed steps. A node that executes five times is five steps; repeated executions are keyed tox#2, tox#3, and so on in results, usage.billed, and the audit trace, so each iteration is billed and traced on its own.

The audit trace records iteration and edge_taken for each executed step: the matched when and goto target, or "sequential". When a run halts, halted_reason appears in the audit header.