Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

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#

yaml
 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
core:
  name: customer-support-agent
  mode: stateless
  runtime_version: "0.2"
  identity:
    provider: scaikey
    persona: "Customer Support Agent"
  model: scaigrid/openai/gpt-4o          # implicit primary, single-string
  plugins:
    - alias: scaidrive
      endpoint: plugin://scaidrive
  capabilities:
    - bunker:
        lifecycle: ephemeral
        image: python-3.12
  scaiqueue:
    scope: support
    queues: [...]
  flows:
    - name: customer-support-agent
      trigger: { type: api }
      steps:
        - llm_turn: "Classify the customer intent."
          meta: { node_id: node_classify, node_kind: llm_flexible, ... }
        - plugin_call: { alias: scaidrive, method: search, args: {...} }
          meta: { node_id: node_kb, ... }
        - llm_turn: "Generate a response grounded in the retrieved docs."
          guarded: true
          guard: "confidence > 0.7"
          meta: { node_id: node_respond, ... }
        - checkpoint:
            type: hitl_review
            scope: support
            queue: review
          meta: { node_id: node_review, ... }
  scaiflow_meta:
    source_id: flow_abc123
    source_version: 1.0.0
    scaicore_target: "0.2"
    publish_as_model: true
    model_visibility_group_ids: [grp_acme_eng]
    models:                                   # full multi-model registry
      - role: primary
        provider: scaigrid
        model: openai/gpt-4o
        modalities: [text, structured_output]
      - role: fast
        provider: scaigrid
        model: openai/gpt-4o-mini
        modalities: [text]

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 has core.model: "<provider>/<model>" (single string).
  • Source linkagesource_id, source_version, scaicore_target so 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 intentspublish_as_model, model_visibility_group_ids so 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 for ir format) 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) from scaiflow_compiler._scaicore shows 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:

  1. Schema-validates the flow JSON.
  2. Builds a nodes_by_id map.
  3. Detects dangling edges (source/target node doesn't exist), missing ports.
  4. Verifies entry-point presence (at least one of entry_api/entry_webhook/entry_schedule).
  5. Detects control cycles.
  6. 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#

flowchart LR Flow1["flow JSON"] -->|compile yaml| Yaml["YAML"] Yaml -->|import yaml_import| Flow2["flow JSON ≈"]

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).

flowchart LR Flow["flow JSON"] -->|compile ir| SCIR["SCIR bytes"] SCIR -->|deserialize| IR["IRModule dict"]

The IR direction is one-way at the moment — no IR → flow JSON importer.

Updated 2026-05-18 16:05:18 View source (.md) rev 3