Top-level symbols exported by reactifact (see reactifact/__init__.py). The format
for each group: name — one-line role. Details live in the doc-strings of the
modules — see the auto-generated reference for those
rendered directly from source (signatures, types, full docstring text) on
the docs site, rather than copied here by hand.
As of 0.4.0, reactifact is pre-1.0 but no longer rc — the surface below is
the stable contract, not a moving target.
Public API = every name in reactifact.__all__ (and each submodule's own
__all__ — reactifact.recipes, reactifact.providers, reactifact.viz, reactifact.eval,
reactifact.quick, reactifact.redaction, reactifact.audit, …), which is exactly the set of symbols documented on this page. If it's
importable from reactifact but not in __all__, it's an internal detail with
no compatibility guarantee — e.g. reactifact.relations.RelationGraph and
reactifact.commit_log.CommitLog exist because Context was split into
smaller modules for readability, but neither is exported: Context is the
supported surface, they are not.
SemVer, pre-1.0 style: a minor bump (0.4.0 → 0.5.0) may add symbols
or, rarely, change behavior in a way CHANGELOG.md marks Breaking — pre-1.0
minors are where reactifact is still allowed to correct a design mistake. A
patch bump (0.4.0 → 0.4.1) never removes or renames a public symbol and
never changes documented behavior, only fixes bugs against it.
Every breaking change is called out in CHANGELOG.md under a ### Breaking
heading, even in a pre-1.0 release — see release.md. If you
only read one section before upgrading, read that one.
Anything under reactifact.cli.* beyond the documented python -m reactifact …
subcommands, and anything in a module's tests-only helpers, is implementation
detail regardless of whether it happens to be importable.
Thin sugar over the primitives below for the four common first tasks — every
object exposes the real .agent/.agents and the run's .context, so it
graduates to Consume/Produce/Effects with nothing to rewrite. See
Quickstart §0.
Symbol
Role
agent(system, schema)
one structured LLM call → one typed artifact
rag(sources)
retrieval → materialize → answer, with supported_by provenance
tools_agent(system, tools, human=False)
LLM + tools (human=True → HITL asks)
chat_agent(agents)
a configured ChatAssistant (in-memory store by default)
versioned working state; resources; queries; latest(Model); announce; diff/rollback
View
result of a typed join query (context.view(...))
RuntimeResources
providers + sources + app resources; register(Type, instance) / get(Type) / require(Type) / has(Type) for typed collaborators (ResourceKey[T] when two of one type); string get/set stay as the additional escape hatch; redactor= scrubs trace text; await resources.aclose() closes the llm/embedder HTTP clients (duck-typed) — call it yourself at real shutdown
ResourceKey[T]
a typed handle for registering two resources of one type (primary/replica, per-tenant)
ResourceScope / RuntimeResources.scope(factory)
async with builder that creates resources on the current loop and closes them on exit — the loop-safe way to own providers (see the class docstring)
current_request()
the active turn's request mapping (same as ProduceCall.request)
constructor-style Agent builder — no subclassing needed for plain containers
Consume / consume
declarative (or decorator) reaction declaration; Consume.by_field for scoped events; wakes=False reads as input without waking the agent; debounce=True collapses several same-generation events into one run
reactifact.consume.CorrelatedConsume
fires (and feeds inputs) only for a correlation key where every require type is present and every forbid type is absent — the mechanism behind JoinConsume/AbsentConsume
reactifact.consume.JoinConsume(*parts, key=…)
CorrelatedConsume factory: fires once every listed type exists for the same key
CorrelatedConsume factory: fires for type only where no matching absent_type exists yet for the same key
Produce / produce
the work unit: writes self.effects (or effects slot in a decorated function) → None; model/Patch return is compiled too. Two canonical styles — subclass and @produce function (see effects); reacts_to=(Type, …) restricts which triggering event a produce runs on, when an agent's several produces don't all care about the same one; a produce declaring an optional trigger parameter gets the already-resolved triggering artifact instead of raw event — guaranteed non-None for a CREATED/UPDATED/STALE event when reacts_to is also set
Trigger
secondary (non-artifact) enter condition for a produce; context_condition(artifact, context) for conditions that need other artifacts (joins/correlation); debounce hint consumed by Runtime
wakes agents on events; run / arun / astream (each takes request=Mapping); budget & concurrency; isolate_errors=True + on_agent_error(agent, event, exc) to keep one agent's exception from aborting the whole run (default: propagates, §69)
ProduceCall.request
the turn's request mapping inside a produce (Runtime.arun(request=…) / ChatAssistant.stream(request=…)); empty when none was set
Budget, RunOutcome, RunStats
run limits and the final outcome/stats
Event, EventType
the wire format of "something changed" — ARTIFACT_CREATED/UPDATED/DELETED/STALE
EventHub, ProgressEvent
progress/announce channel consumed by web UIs
Scheduler
filter → rank → LLM tie-break agent-selection policy, callable from the runtime each iteration (see design notes)
FastAPI APIRouter for the SSE contract (/api/chat/stream, /api/runs/{id}, /api/health) — needs the web extra. The wire vocabulary is configurable (rename kinds, filter, reshape payloads, emit a terminal frame); the event schema is published in the route's OpenAPI responses
records every LLM call to JSONL, or replays them exactly; ReplayMiss on divergence
ReplayMiss
a replaying call did not match the recording
verify_run(build, *, recording=None, repeat=2)
runs build(resources) -> Contextrepeat times under a recorded model and strict ids (counter_ids), returns a ReproReport; ok is False when the context_hash differs (real nondeterminism)
counter_ids()
a deterministic id_factory (Model:0000, Model:0001, …) for RuntimeResources(id_factory=…) — makes artifacts created without an explicit id reproducible
one structured call; None on honest failure; validate(model)->bool adds a domain-rule check retried like a parse failure, repair(invalid_or_None, last_reply)->str supplies the retry instruction; on_error(reason, exc) ("no_provider"|"provider_error"|"parse_error"|"validation_error") to distinguish why, without changing the None contract
strict {var} rendering: declared variables, KeyError on missing vars, model-attribute fields ({question.text}); identifier-shaped placeholders only — a literal JSON {"name": …} / {} in the prompt is left verbatim, no escaping; .hash is a stable sha256 of the template
MessagesPrompt([(role, template), …])
renders a chat sequence to list[Message]; .hash covers all rows
Every provider lazily creates its httpx.AsyncClient and rebinds it to the
current event loop, so one provider instance stays usable across repeated
asyncio.run(...) and per-test loops (no RuntimeError: Event loop is closed);
aclose() closes the client for the running loop. The same applies to
WebSource and the OTLP/Langfuse sinks.
key/value checkpoints backing sessions (pg extra for Postgres) — async-native: file I/O runs off-thread, SQLite/Postgres each hold one persistent connection (WAL + busy_timeout on SQLite) serialized by an asyncio.Lock
scenario harness: seed artifacts, run agents, assert (artifacts/tools/path/errors), mode= live/record/replay, fault injection (fail(tool, …), fail_resource(name_or_key, …) — a string name or a typed Type/ResourceKey)
capture(context, *, trace=…)
freeze a GoldenRun — context_hash plus the trace's prompt hashes
assert_golden(context, golden, *, trace=…)
fail loudly when state (or, with trace, prompts) drifted
replay_resources(recording, *, base=…)
a RuntimeResources whose llm replays a recording — an offline regression on a real run