Compilation targets
The compiler can emit three formats from the same flow JSON. The right format depends on what's downstream.
| Format | When to use | Wire shape |
|---|---|---|
yaml (default) |
Deploy to ScaiGrid. | YAML manifest accepted by POST /v1/modules/scaicore/cores. |
ir |
Verification, debug, direct-runtime experiments. | SCIR MessagePack binary bundle — identical to scaicore.compiler.serializer.IRSerializer. |
scai |
Human inspection only. | .scai text source. Deprecated. |
Set via the CLI's --format flag (scaiflow compile flow.json --format ir) or via compile_flow(..., output_format="ir") from Python.
YAML — the deploy artifact#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
Why scaiflow_meta#
ScaiCore's YAML parser is intentionally permissive about unknown keys — it loads the body as dict[str, Any] and walks it into IR. ScaiFlow uses this to round-trip fidelity that the documented YAML keywords don't carry:
- The full multi-model registry (
scaiflow_meta.models) — the documented YAML only hascore.model: "<provider>/<model>"(single string). - Source linkage —
source_id,source_version,scaicore_targetso the YAML can be reverse-engineered into a canvas flow. - Per-step
meta—{node_id, node_kind, node_label, node_config}so YAML → canvas import is lossless for ScaiFlow-emitted manifests. - Deploy intents —
publish_as_model,model_visibility_group_idsso the deployer can pick them up alongside the artifact.
Hand-edited YAML (authored outside ScaiFlow) won't have meta on steps; those steps land under metadata.unknown_steps after import. A heuristic mapper is a non-goal until a customer hits the wall.
IR — the binary form#
output_format="ir" produces a SCIR MessagePack bundle starting with the magic bytes SCIR. Wire-identical to what ScaiCore's IRSerializer().serialize(module) produces, so the upstream IRSerializer().deserialize(bytes) + Verifier().verify(module) round-trip end-to-end against ScaiFlow's emit.
When to use:
- Verifying — pass
--verify(default forirformat) to run ScaiCore's own verifier against the bundle. Catches IR shape mismatches the YAML loader silently tolerates. - Direct runtime targets — if a future deploy module accepts SCIR directly (today everything is YAML-via-ScaiGrid).
- Debugging — the IR is the canonical form; reading it as JSON via
deserialize_ir(payload)fromscaiflow_compiler._scaicoreshows exactly what the runtime will execute.
.scai text — deprecated#
A best-effort emission of a .scai-syntax source file. Not what ScaiCore's parser canonically reads (the IR is); kept for inspection and as documentation when ScaiCore's grammar examples drift from what ScaiFlow's compiler actually produces.
The shared analyze stage#
All three emitters share the analyze pass that runs first:
- Schema-validates the flow JSON.
- Builds a
nodes_by_idmap. - Detects dangling edges (source/target node doesn't exist), missing ports.
- Verifies entry-point presence (at least one of
entry_api/entry_webhook/entry_schedule). - Detects control cycles.
- Builds a topological order over control edges.
Failures here surface as CompileError(message, errors=[...]) with one string per problem. The canvas's live preview pane renders them inline; CLI prints them to stderr.
Round-trip#
Lossless for ScaiFlow-emitted YAML (every step has meta carrying its node_id/node_kind/node_label/node_config). Partial for hand-edited YAML (unknown steps preserved under metadata.unknown_steps).
The IR direction is one-way at the moment — no IR → flow JSON importer.