Flows
A flow is a named, ordered composition of tools: a recipe that says "read the document, run these steps in this order, write the result." Where a single tool does one thing, a flow assembles several into an end-to-end workflow (leverage from memory, look up terminology, translate the remainder, check quality) and gives it a name so it can be run, shared, and reused.
Flows separate what to do from how it runs. A flow describes the chain; the
pipeline executor decides how to run it concurrently. The
generated list of flows that ship in a given build is the kapi flows command
and the Command Reference.
In the CLI, a flow is the abstraction behind kapi run <flow>: one named
flow, one pass. kapi up loops the project's default flow (defaults.flow,
or the built-in default when none is set) until the project's gates are met.
How the two verbs relate, and where the raw tool layer sits underneath, is
Understanding the CLI layers.
A flow is a sequence of tools
At its simplest, a flow is a list of tools that Parts stream through in order. The framework models this directly:
type Flow struct {
Name string
Tools []tool.Tool // for single-document / sequential execution
ToolFactories []ToolFactory // for parallel: a fresh tool chain per document
}
The Builder provides a fluent API for assembling one in Go:
f, err := flow.NewFlow("translate").
AddTool(tools.NewMemoryLeverageTool(memoryCfg)).
AddTool(terms.NewTermLookupTool(tb, termCfg)).
AddTool(aitools.NewAITranslateTool(provider, translateCfg)).
AddTool(tools.NewRuleCheckTool(checkCfg)).
Build()
A flow built this way holds concrete tool instances. For running the same flow over many documents at once, a flow can instead hold tool factories: each document then gets its own fresh tool chain, so per-document state never leaks between concurrent runs.
The two authored representations
A flow can be authored in two equivalent forms, and both compile to the same executable graph.
Steps: the human-authored form
The steps format is a YAML list of tools. It is the form people write by hand:
steps:
- tool: recycle
label: Apply memory matches
config:
fuzzyThreshold: 70
- tool: translate
label: Translate the rest
config:
provider: anthropic
- tool: qa
label: Quality checks
Each step names a tool and optionally configures it. Steps run sequentially: each tool's output channel feeds the next tool's input channel. A flow carries only its steps; where content comes from and goes to is a binding decided when you run it rather than part of the flow (see Source and sink).
A check such as qa is just a read-only step: it
attaches findings to each block as annotations rather than rewriting content, so
it typically sits last and a CI gate reads its result.
A step can also fan out. A parallel: block runs several tools on the same
stream concurrently, each on its own branch:
steps:
- tool: create-target
config: { copySource: true }
- parallel:
- tool: qa
- tool: term-check
- tool: xml-validation
Transformers
Some tools rewrite the source itself: redaction replacing sensitive spans with placeholders, a simplifier rephrasing for clarity, a normalizer. These transformers are ordinary steps in the same ordered list as everything else:
steps:
- tool: redact # rewrites the source in place
- tool: translate # translates the redacted source
- tool: qa
A transformer does not edit the block directly: it is a read-only edit producer that returns an edit plan, and a single framework-owned applier performs the rewrite: applying the edits, rebasing surviving run-anchored overlays (segments, term and entity spans) onto the new runs, vaulting any secrets fail-closed, and bounds-checking the result, atomically. Because the applier mutates inline and in order, each transformer settles the source before later steps observe it.
Ordering safety is a placement pass that validates every flow at build and
load time. It rejects a transformer placed after a step that produces a
committed target (rewriting source would orphan the targets; unredact is
exempt because it rewrites both sides), rejects a recoverable transformer such
as redact placed after a step that sends source to a remote service (except
a step producing an input the transformer's configuration requires), and warns
when a transformer sits later than its earliest valid slot, since every
overlay present at apply time must be rebased. See
the tool system AD for the model.
A flow that declares the removed source_transforms: field is rejected at
load with a migration error: list the transformers as ordered steps instead.
Graph: the canonical form
Internally a flow is a directed graph of tool nodes connected by edges:
type FlowDefinition struct {
ID string
Name string
Nodes []FlowNode // tool nodes
Edges []FlowEdge // directed connections
}
The graph is what the visual flow editor reads and writes, and it is the form
that survives to execution. Compilation from steps to graph is mechanical:
StepsToGraph creates a tool node for each step (chained by edges) and a fan-out
for each parallel: block. A parallel: block becomes several tool nodes all
connected from the previous node; the step after it connects from every branch
endpoint (fan-in). Cycles are rejected; the executor runs nodes in topological
order. The flow's ends (where content enters and leaves) are bindings rather
than nodes, covered next.
Because both forms compile to the same graph, the steps you write by hand and the graph you build in the editor are interchangeable: a hand-written flow opens in the editor, and an editor-built flow runs from the CLI.
Assemble a flow in the same node editor the desktop app uses (add, remove, and
reorder tool nodes), then press Run flow to execute it on a file and step
through the result. The graph is serialized to a kapi.yaml recipe and run with the
real kapi engine in your browser via WebAssembly, so the flow you build is the
flow that runs.
Source and sink: the flow's ends
A flow processes a stream of blocks; where that stream comes from and where the
result goes are its source and sink bindings. The same flow runs over a
loose file, the blocks already in a project, a .kpz workspace, or content
imported from an interchange file; only the binding changes:
| Binding | As source | As sink |
|---|---|---|
file (default) | read + parse a file | write a file (round-trip via skeleton) |
store / kpz | existing blocks + overlays | commit overlays; no file |
| interchange | import from XLIFF / PO / a bilingual .kpz | emit interchange for a translator |
none | (none) | discard (analysis / checks only) |
Bindings are resolved when you run the flow, by precedence: an explicit -i / -o
flag, then the project or .kpz you're in, then the flow's own intent, then
auto-detection from the path. A plain path is detected (.kpz → workspace,
.xliff → interchange, a document → file); a scheme: is explicit
(-o store:, -o xliff:hand.xliff). kapi run <flow> --explain prints the
resolved source → sink without running anything.
A flow only ever declares intrinsic intent (a check flow that produces no
document sets sink: none) and never a path. Inside a project, a run with no -o
lands its work in the store (process-only); kapi merge materializes files when
you're ready. See E-04 for the
full model.
Running a flow
A flow is run against a source. The runner resolves the source and sink bindings,
instantiates the tool chain, and hands it to the executor. Composed (multi-tool)
flows run with kapi run; single-tool flows are exposed directly as their tool's
command:
# Run a composed flow (two or more tools) on a file
kapi run translate-qa -i app.xliff --target-lang fr
# List the composed flows available in this build
kapi flows
The demo below runs the pseudo-translate flow: source → pseudo-translate → sink. Because it is a single-tool flow, it is invoked directly as kapi pseudo-translate. The left pane is the CLI invocation; the right pane is the
framework's result, the same file with its source strings replaced by accented
look-alikes:
Built-in flows
The framework ships a set of built-in flows covering common workflows:
AI translation, AI translation with a quality pass, pseudo-translation for
layout testing, memory leverage, rule-based checks, a redact-translate-restore flow
for sensitive content, and media-to-subtitles flows that
turn audio and video into translated subtitle tracks (see
Translate audio & video). Rather than maintain a
list here, run kapi flows or see the Command Reference, both
generated from the live flow set.
Related reading
- Tools: the units a flow composes.
- Pipeline: how the executor runs a flow's graph concurrently.
- Flow Authoring: the full steps-format reference and more examples.