Expand description
turnframe: deterministic conversational workflows for Rust, built around the Flow Map
architecture. It is the crate an application installs, with the turnframe-* family
behind feature flags. The model proposes meaning, deterministic code decides effects, and
committed events decide claims; Orchestrator runs one TurnInput through all of it,
and the architecture guide
explains what an adopter writes.
[dependencies]
turnframe = { version = "0.1", features = ["openai", "postgres", "telemetry"] }| Module | What belongs there |
|---|---|
flow | workflow definitions, the pure projector, the view, the registry, case references |
turn | one user turn: the input, target resolution, reduction, the replay record |
understanding | what a turn was understood to say: units, acts, questions, constraints |
understand | the understanding pipeline: its input, tasks, settings and streamed steps |
tasks | the engine that runs small verified model tasks under budgets and profiles |
interaction | durable cards: payloads, options, status, responses and their validation |
command | typed commands, envelopes, origins, risk and confirmation policy |
event | the claim ledger: committed events, external statuses, operational receipts |
response | the ordered blocks a turn returns, and the claim guard over them |
provider | the provider-neutral model layer, capability routing, fallback, and each enabled adapter |
store | the persistence traits, the in-memory implementation, the store conformance suite |
runtime | the turn pipeline itself: orchestration, configuration, and every stage of it |
prompt | where prompt text comes from, and which prompt text produced a turn |
error | the typed failure family every layer speaks |
ids | newtype identifiers and versions |
observe | the metric and tracing signals a runtime emits |
locale | locales and server-authored localized copy |
schema | canonical hashing and JSON Schema fingerprints |
telemetry, testing, evaluation | with their feature, below; none is on by default, and none changes the runtime’s safety semantics |
| Feature | What it turns on |
|---|---|
openai | provider::openai: OpenAI, Azure OpenAI and OpenAI-compatible endpoints |
anthropic | provider::anthropic: the Anthropic Messages API |
gemini | provider::gemini: Google Gemini and Vertex AI |
bedrock | provider::bedrock: AWS Bedrock Converse |
ollama | provider::ollama: a local Ollama daemon |
all-providers | every adapter above |
postgres | store::postgres: the PostgreSQL reference store, its migrations and its expected-revision transactions |
prompts | the prompt sources in prompt: prompts compiled in from your own repository, and a bounded cache. No network |
langfuse | prompts, plus prompt::langfuse: prompts fetched from a Langfuse project over the Langfuse v4 API. A runtime dependency on a remote service |
telemetry | telemetry: the turnframe.* metrics observer, tracing spans and the dashboard description |
otel | telemetry, plus the OpenTelemetry bridge and the baggage-copying span processor |
test-kit | testing: scripted providers and tasks, fake stores, workflow exploration and three sample domains |
eval | evaluation: the model evaluation harness |
full | all-providers, postgres, prompts, telemetry, test-kit and eval |
§A minimal turn, end to end
Nothing below needs an API key, a database or a network. It uses the sample
travel domain, the in-memory stores and a scripted model from the
testing and tasks modules, which is what the
test-kit feature is for. Swap the doubles for a real workflow, a real store and a
real adapter and the rest of the code is unchanged.
use std::sync::Arc;
use serde_json::json;
use turnframe::effort::Effort;
use turnframe::flow::{CaseKey, WorkflowRegistry};
use turnframe::ids::{AccountId, CaseId, CaseRevision, ConversationId, TurnId};
use turnframe::locale::Locale;
use turnframe::provider::provider::ModelProvider;
use turnframe::provider::router::ProviderPool;
use turnframe::runtime::config::{NarrationConfig, OrchestratorConfig};
use turnframe::runtime::orchestrator::{CaseCandidate, Orchestrator, StaticCaseDirectory};
use turnframe::store::conversation::ConversationRecord;
use turnframe::store::stores::Stores;
use turnframe::tasks::testing::ScriptedTasks;
use turnframe::testing::workflows::InMemoryExecutor;
use turnframe::testing::workflows::trip::{TripWorkflow, incomplete_case};
use turnframe::turn::{ActorContext, TurnInput};
let account = AccountId::from("aurora");
let conversation = ConversationId::new();
// 1. A domain. The projector and the executor are the two things an adopter
// writes; here they come ready-made from the test kit.
let trips = Arc::new(InMemoryExecutor::new(TripWorkflow::default()));
trips.seed(&account, &CaseId::from("trip-1"), incomplete_case(), CaseRevision(3));
let workflows = Arc::new(
WorkflowRegistry::builder()
.register(TripWorkflow::default(), Arc::clone(&trips))
.build()?,
);
// 2. A model. Understanding is a few small tasks, each answered by id: split the
// message into requests, check none was missed, route the request to an
// operation, point at the value in the user's words, verify it.
let text = "Set the name of Trip 1 to Lisbon";
let tasks = Arc::new(
ScriptedTasks::new("scripted", "small")
.answer("turn/segment", json!({
"analysis": "One request.",
"units": [{"kind": "request", "words": {"from": 1, "to": 8}, "workflow": "trip"}]
}))
.answer("turn/coverage", json!({"missed": []}))
.answer("u1/route", json!({"operations": ["trip.set_name"]}))
.answer("u1/extract", json!({"arguments": {
"value": {"kind": "words", "text": "Lisbon", "message": "current", "from": 8, "to": 8}
}}))
.answer("u1/verify", json!({
"reason": "The user said so.", "arguments": {"value": "stated"}, "overall": "confirmed"
})),
);
let providers = Arc::new(
ProviderPool::builder()
.provider(Arc::clone(&tasks) as Arc<dyn ModelProvider>)
.build()?,
);
// 3. Persistence, and the records this user may address. The model never sees a
// record identifier: it sees the label, and the runtime issues an opaque token.
let stores = Stores::in_memory();
stores
.conversations()
.create_conversation(ConversationRecord::new(conversation, account.clone(), chrono::Utc::now()))
.await?;
let directory = StaticCaseDirectory::new()
.with_case(CaseCandidate::new(CaseKey::new("trip", "trip-1"), "Trip 1"));
// Receipts, notices and cards only: nothing here writes prose.
let mut config = OrchestratorConfig::conservative();
config.narration = NarrationConfig::conservative().with_enabled(false);
let orchestrator = Orchestrator::builder()
.workflows(workflows)
.providers(providers)
.stores(stores)
.case_directory(Arc::new(directory))
.config(config)
.build()?;
// 4. One turn.
let answer = orchestrator
.handle_turn(TurnInput {
turn_id: TurnId::new(),
conversation_id: conversation,
actor: ActorContext::new(account.clone(), "u1"),
text: Some(text.to_owned()),
interaction_response: None,
attachments: Vec::new(),
origin: None,
locale: Locale::from("en-GB"),
// One reading per task, since the script answers each once; higher levels vote.
effort: Some(Effort::Low),
})
.await?;
// The field really changed, under a new revision.
assert_eq!(trips.revision_of(&account, &CaseId::from("trip-1")), CaseRevision(4));
// And the reply says so only because a committed event backs it.
let receipts: Vec<&str> = answer.receipts().map(|r| r.status_code.as_str()).collect();
assert_eq!(receipts, ["trip.name_set"]);
assert!(answer.receipts().all(|receipt| receipt.is_event_backed()));
assert!(tasks.unanswered().is_empty());Read that in the order the pipeline runs it. The projector turned the stored
trip into a view. Understanding split the message into one request, routed it
to trip.set_name, and pointed at the user’s own words for the value, which
code sliced out of the message. The reducer resolved the one trip in view to a
case and compiled a typed command with an expected revision and an idempotency
key. The executor committed it. The receipt was rendered from the committed event,
not from a model’s prose.
Modules§
- case
- Case references and versioned values: how a record is named, and how a value is carried with the revision it was read at.
- command
- Typed commands and the envelope that carries one: who asked, which case, which expected revision, which idempotency key, and which origin — plus the risk and confirmation policy that decides whether it may run.
- effort
- How much judgment a turn buys:
low,mediumorhigh. More model calls behind each step, never more authority. - error
- The typed failure family: what went wrong, whether it may be retried, and whether an effect may already exist.
- evaluation
- The model evaluation harness: corpora, execution samples kept separate from judge votes, and deterministic assertions over a turn.
- event
- The claim ledger: committed events, the regulated external statuses that must never collapse into “done”, and the receipts rendered from them.
- flow
- Workflow definitions, the pure projector, the view it returns, the registry that hosts several workflows at once, and the case references everything is addressed by.
- ids
- Newtype identifiers, revisions and versions. Nothing here is a bare
StringorUuidat a call site. - interaction
- Durable cards: immutable payloads, server-owned option semantics, compare-and-set resolution, staleness and expiry.
- knowledge
- The knowledge retrieval contract. Retrieved content is evidence for an answer and never authorization for a command.
- locale
- Locales and server-authored localized copy, which is what receipts, notices and card labels are made of.
- observe
- The signals a runtime emits, so metrics and traces are a contract rather than a grep over log lines.
- operation
- What a workflow offers to do: operations, their arguments and examples, and the values a model understands and code computes, such as dates and money.
- plan
- Which records an operation may aim at, whether it changes anything, and the limits a turn’s understanding is held to.
- policy
- The deterministic decision: given a command’s policy and its origin, whether the command may run now, needs confirming first, or is refused.
- prelude
- The set an application reaches for in almost every file.
- prompt
- Where prompt text comes from, and which prompt text produced a turn.
- provider
- The provider-neutral model layer: normalized requests and responses, capabilities declared per provider-model pair, capability-first routing, bounded fallback, and the conformance suite every adapter passes.
- read
- The read-only tool contract. A read tool queries and never mutates, and every result carries where it came from and how far it is trusted.
- reduce
- The whole-turn reduction: one pass over the accepted plan that decides every act together rather than each on its own.
- replay
- Replay records and turn phases: enough of a turn kept to reconstruct why it produced the commands and the response it did.
- response
- The ordered, typed blocks a turn returns, and the claim guard that refuses an operational claim no committed event backs.
- runtime
- The turn pipeline itself, one module per stage, so a trace, a phase marker and a replay record can all point at the same place.
- schema
- Canonical hashing and JSON Schema fingerprints: how a payload, a plan or a configuration is identified by content.
- store
- What must be durable, and with which rules: seven object-safe traits, a deterministic in-memory implementation, and a conformance suite that proves an implementation right without reading its code.
- target
- Deterministic target resolution. The model never sees a record identifier; it gets an opaque per-turn handle, and the map back lives here and stays on the server.
- tasks
- The engine that runs one small, schema-bound model task: profiles, repairs, votes, escalation, budgets, and the record of every call.
- telemetry
- Metrics, tracing spans and the dashboard description a running Turnframe application reports through.
- testing
- The test kit: scripted providers, fake stores with failure injection, bounded workflow exploration, replay assertions, and two complete sample domains (a trip, a traveler and an expense claim) to build examples and tests against.
- turn
- One user turn: what arrives, how targets resolve, how the whole turn reduces to commands, and the record that can explain it afterwards.
- understand
- The understanding pipeline: segment, cover, route, locate, extract, verify and frame, each a small model task that code checks, plus the steps it publishes while it runs.
- understanding
- What a turn was understood to say: its units, the acts they ask for with their arguments and the words each came from, questions, constraints, a typed card answer, disputes, and what could not be understood.
Structs§
- Account
Id - Tenant identifier. Every lookup in the library is scoped by it.
- ActId
- One act of a unit:
u2.a1. Command ids, card keys and minted case ids derive from it. - Actor
Context - The authenticated actor of a turn, supplied by the application’s authentication middleware. Trusted after authentication (spec §25.1).
- Argument
Spec - One argument of an operation.
- Assistant
Turn - The persisted assistant turn (spec §18.1).
- Case
Candidate - One case the actor may address in a conversation.
- CaseId
- Application-owned identifier of a workflow case (a trip, a traveler record…). Opaque to the library; never shown to the model.
- CaseKey
- Identity of a case without a revision: the key used to group views, interactions and commands that belong to the same record.
- CaseRef
- A reference to a case at an expected revision (spec §7, I13).
- Case
Revision - Monotonic revision of a mutable case (spec §7, I13).
- Command
Batch - A group of envelopes executed under one atomicity scope.
- Command
Envelope - A typed command with everything the executor and the journal need (spec §14.2).
- Command
Policy - The policy attached to a command (spec §14.3).
- Commit
- Result of executing a command batch (spec §17.1).
- Committed
Event - One committed domain event (spec §17.1).
- Conversation
Id - Identifies one conversation (a chat thread) within an account.
- Digest
- Re-exported at the root rather than only under
schemabecauseCommandOrigincarries one: an origin cannot be constructed without naming it, and a type needed to build a root-level enum belongs beside it. A lowercase hexadecimal BLAKE3 digest (64 characters). - Domain
Rejection - A domain refused an act or a command (spec §8.2).
- EventId
- Identifies one committed domain event in the claim ledger.
- Event
Redaction - The record of an erasure: that a payload was removed, when, and on whose authority.
- Event
Ref - Reference to a committed event without its payload.
- Idempotency
Key - Stable idempotency key of a command (I14).
- Interaction
- A persisted interaction (spec §15.1).
- Interaction
Id - Identifies one persisted interaction (card, confirmation, selection…).
- Interaction
Option - A stored option (spec §15.3).
- Interaction
Payload - Immutable content of an interaction (spec §15.1).
- Interaction
Requirement - What a user-owned phase requires from the user (I6).
- Interaction
Response - A structured reply to a persisted interaction (spec §9).
- Interaction
Spec - What a reducer or workflow asks the engine to create (spec §13.3, I6).
- Interaction
View - Client-facing projection of an interaction (spec §18.1).
- Invariant
Violation - A projection violated one of the Flow Map invariants (spec §8.4).
- Locale
- A BCP-47 language tag such as
"it-IT"or"en". - Localized
Text - Server-authored copy with a mandatory fallback and optional translations.
- Obligation
Id - Stable identifier of an obligation: the canonical JSON of its value.
- Operation
Key - Key of a semantic operation offered to the interpreter, e.g.
"trip.set_travel_date". Only keys listed in the catalog of this turn may be used. - Operation
Spec - One operation a workflow offers in a view.
- Operational
Receipt - A deterministic, server-rendered statement of what happened (spec §17.3).
- Option
Id - Identifier of a stored option on an interaction. The client echoes it back; the server derives its meaning from the stored option.
- Orchestrator
- The runtime that answers a turn (spec §29).
- Orchestrator
Builder - Collects everything one
Orchestratorneeds. - Orchestrator
Config - Everything the runtime needs to know before it handles a turn (Appendix A).
- Planned
Act - One act with its resolution and result.
- Policy
Decision - The outcome of evaluating policy for one command.
- Policy
Snapshot - Point-in-time policy configuration the reducer evaluates against.
- Redacted
Event - A committed event whose payload was erased (see the module documentation).
- Redaction
Authority - Names the authority under which an event payload was erased, e.g. an
erasure ticket, a retention policy key or an operator identifier
(
EventRedaction). - Reduction
Plan - The reducer’s output (spec §13).
- Rejection
Code - Application-defined rejection code (e.g.
"trip.traveler_missing"). - Review
Diff Entry - One line of a review card: a field before and after the proposed change.
- Revision
Conflict - A command targeted a revision that is no longer current (I13).
- Server
Notice - A server-authored notice (spec §18.2).
- Static
Case Directory - A directory with a fixed list, for tests, examples and single-case surfaces.
- Target
Token - Opaque token shown to the model instead of a raw case identifier. The
server owns the token →
CaseRefmapping. - TurnId
- Identifies one user turn. Every command, replay record and response block produced while handling the turn carries it.
- Turn
Input - One user turn as accepted by the runtime (spec §9).
- Understanding
- Everything a turn was understood to say.
- Understood
Act - One act the turn asks for.
- UserId
- Identifier of the authenticated user acting within an account.
- Versioned
- A value read together with the case revision it was read at.
- Workflow
Key - Stable key of a workflow definition, e.g.
"trip". - Workflow
Notice - An informational, non-blocking element of a view.
- Workflow
Registry - Heterogeneous set of workflows keyed by
WorkflowKey(spec §8.3). - Workflow
Registry Builder - Builds a
WorkflowRegistry. - Workflow
Version - Version label of a workflow definition. Must change whenever projection semantics change (spec §8.4).
- Workflow
View - The pure projection of a case (spec §8.1).
Enums§
- ActMutability
- Whether an operation changes a record.
- ActTarget
- The record an act applies to.
- Action
Class - What a stored option authorizes when it is chosen (spec §15.3).
- Atomicity
Scope - How commands are grouped for all-or-nothing execution (spec §13.4).
- Build
Error - Why an
Orchestratorcould not be built. - Claim
Mode - How the assistant may talk about the outcome of a command (spec §17.2).
- Command
Origin - Who or what authorized a command (spec §14.2).
- Confirmation
Policy - Confirmation a command requires before execution (spec §14.3).
- Constraint
Kind - A condition the user placed on the whole turn.
- Execution
Error - Errors raised while executing a command batch.
- External
Status - Fine-grained state of a request another system decides (spec §17.4).
- Interaction
Kind - The shape of an interaction (spec §15.2).
- Interaction
Status - Lifecycle status of an interaction (spec §15.4).
- Orchestration
Mode - How much autonomy the model gets: which risk classes are eligible at all (§11.4).
- Orchestrator
Error - Top-level error of a turn (spec §24).
- Phase
Ownership - Who must act for the case to leave its current phase.
- Planned
ActResult - The explicit result of one act (spec §13.3, I11).
- Receipt
Event - One event as offered to
WorkflowDefinition::receipts: its payload is either still in the ledger or has been erased. - Receipt
Severity - Severity of a receipt.
- Resolution
Channel - How a user’s answer to an interaction reached the server (spec §15.7).
- Response
Block - One ordered block of an assistant turn (spec §18.1).
- Risk
Class - Risk class of a command, ordered from harmless to regulated (spec §14.3).
- Store
Error - Errors raised by persistence adapters.
- Stored
Interaction Action - The server-side meaning of an option (spec §15.3). Never supplied by the client.
- Target
Policy - Which record an operation may be aimed at.
Traits§
- Case
Directory - Names the cases an actor may address (spec §23 step D, §25.4).
- Turn
Consequences - What a turn’s writes imply on OTHER cases (spec §23 step M).
- Workflow
Definition - A workflow: pure projection plus deterministic compilation and policy (spec §8.2).
- Workflow
Executor - Loads and mutates cases of one workflow (spec §8.2).
Functions§
- check_
view - Checks a typed view against the §8.4 invariants.
- origin_
satisfies - Pure check of I12: does
originsatisfypolicy?
Type Aliases§
- ViewOf
- Shorthand for the view type of a definition.