Skip to content

Omens & streaming

The ritual gives off signs as it unfolds; read them as they appear.

Technically: an Omen is a frozen, timestamped dataclass emitted at every engine lifecycle point. Rite.astream() yields them live, filtered by combinable modes:

async for omen in rite.astream(input, mode={"updates", "tokens"}):
    ...
Mode Yields
"updates" (default) SigilCompleted — one per finished Sigil, with its delta
"values" SuperstepCompleted — the full Aether after each superstep
"tokens" TokenEmitted — payloads a Sigil pushes through its writer, while it runs
"omens" The granular lifecycle: RiteBegan, SuperstepBegan, SigilBegan, SigilRetried, SigilCompleted, SealWritten, RiteManifested, plus SpellCallRepaired / SpellCallRejected / DeltaRejected

Live tokens from inside a Sigil

Declare a writer parameter and the engine injects an async callable:

async def chant(aether, writer):
    async for chunk in oracle.stream_generate(aether["messages"]):
        await writer(chunk)          # TokenEmitted, delivered immediately
    return {"chanted": True}

Passing an Omen instance to writer emits it verbatim — Sigils and libraries can define custom observability events without touching the engine.

Semantics worth knowing: SigilCompleted is emitted at real completion time (parallel Sigils appear in completion order — truthful observability), while state still merges deterministically in insertion order. Exceptions propagate to the stream consumer after the emitted Omens drain; closing the generator early cancels the Invocation. For bridging to Server-Sent Events, see examples/sse_flask.py; for recording everything to a file, see Tracing.