Skip to main content

Crate turnframe

Crate turnframe 

Source
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"] }
ModuleWhat belongs there
flowworkflow definitions, the pure projector, the view, the registry, case references
turnone user turn: the input, target resolution, reduction, the replay record
understandingwhat a turn was understood to say: units, acts, questions, constraints
understandthe understanding pipeline: its input, tasks, settings and streamed steps
tasksthe engine that runs small verified model tasks under budgets and profiles
interactiondurable cards: payloads, options, status, responses and their validation
commandtyped commands, envelopes, origins, risk and confirmation policy
eventthe claim ledger: committed events, external statuses, operational receipts
responsethe ordered blocks a turn returns, and the claim guard over them
providerthe provider-neutral model layer, capability routing, fallback, and each enabled adapter
storethe persistence traits, the in-memory implementation, the store conformance suite
runtimethe turn pipeline itself: orchestration, configuration, and every stage of it
promptwhere prompt text comes from, and which prompt text produced a turn
errorthe typed failure family every layer speaks
idsnewtype identifiers and versions
observethe metric and tracing signals a runtime emits
localelocales and server-authored localized copy
schemacanonical hashing and JSON Schema fingerprints
telemetry, testing, evaluationwith their feature, below; none is on by default, and none changes the runtime’s safety semantics
FeatureWhat it turns on
openaiprovider::openai: OpenAI, Azure OpenAI and OpenAI-compatible endpoints
anthropicprovider::anthropic: the Anthropic Messages API
geminiprovider::gemini: Google Gemini and Vertex AI
bedrockprovider::bedrock: AWS Bedrock Converse
ollamaprovider::ollama: a local Ollama daemon
all-providersevery adapter above
postgresstore::postgres: the PostgreSQL reference store, its migrations and its expected-revision transactions
promptsthe prompt sources in prompt: prompts compiled in from your own repository, and a bounded cache. No network
langfuseprompts, plus prompt::langfuse: prompts fetched from a Langfuse project over the Langfuse v4 API. A runtime dependency on a remote service
telemetrytelemetry: the turnframe.* metrics observer, tracing spans and the dashboard description
oteltelemetry, plus the OpenTelemetry bridge and the baggage-copying span processor
test-kittesting: scripted providers and tasks, fake stores, workflow exploration and three sample domains
evalevaluation: the model evaluation harness
fullall-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, medium or high. 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 String or Uuid at 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§

AccountId
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.
ActorContext
The authenticated actor of a turn, supplied by the application’s authentication middleware. Trusted after authentication (spec §25.1).
ArgumentSpec
One argument of an operation.
AssistantTurn
The persisted assistant turn (spec §18.1).
CaseCandidate
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).
CaseRevision
Monotonic revision of a mutable case (spec §7, I13).
CommandBatch
A group of envelopes executed under one atomicity scope.
CommandEnvelope
A typed command with everything the executor and the journal need (spec §14.2).
CommandPolicy
The policy attached to a command (spec §14.3).
Commit
Result of executing a command batch (spec §17.1).
CommittedEvent
One committed domain event (spec §17.1).
ConversationId
Identifies one conversation (a chat thread) within an account.
Digest
Re-exported at the root rather than only under schema because CommandOrigin carries 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).
DomainRejection
A domain refused an act or a command (spec §8.2).
EventId
Identifies one committed domain event in the claim ledger.
EventRedaction
The record of an erasure: that a payload was removed, when, and on whose authority.
EventRef
Reference to a committed event without its payload.
IdempotencyKey
Stable idempotency key of a command (I14).
Interaction
A persisted interaction (spec §15.1).
InteractionId
Identifies one persisted interaction (card, confirmation, selection…).
InteractionOption
A stored option (spec §15.3).
InteractionPayload
Immutable content of an interaction (spec §15.1).
InteractionRequirement
What a user-owned phase requires from the user (I6).
InteractionResponse
A structured reply to a persisted interaction (spec §9).
InteractionSpec
What a reducer or workflow asks the engine to create (spec §13.3, I6).
InteractionView
Client-facing projection of an interaction (spec §18.1).
InvariantViolation
A projection violated one of the Flow Map invariants (spec §8.4).
Locale
A BCP-47 language tag such as "it-IT" or "en".
LocalizedText
Server-authored copy with a mandatory fallback and optional translations.
ObligationId
Stable identifier of an obligation: the canonical JSON of its value.
OperationKey
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.
OperationSpec
One operation a workflow offers in a view.
OperationalReceipt
A deterministic, server-rendered statement of what happened (spec §17.3).
OptionId
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).
OrchestratorBuilder
Collects everything one Orchestrator needs.
OrchestratorConfig
Everything the runtime needs to know before it handles a turn (Appendix A).
PlannedAct
One act with its resolution and result.
PolicyDecision
The outcome of evaluating policy for one command.
PolicySnapshot
Point-in-time policy configuration the reducer evaluates against.
RedactedEvent
A committed event whose payload was erased (see the module documentation).
RedactionAuthority
Names the authority under which an event payload was erased, e.g. an erasure ticket, a retention policy key or an operator identifier (EventRedaction).
ReductionPlan
The reducer’s output (spec §13).
RejectionCode
Application-defined rejection code (e.g. "trip.traveler_missing").
ReviewDiffEntry
One line of a review card: a field before and after the proposed change.
RevisionConflict
A command targeted a revision that is no longer current (I13).
ServerNotice
A server-authored notice (spec §18.2).
StaticCaseDirectory
A directory with a fixed list, for tests, examples and single-case surfaces.
TargetToken
Opaque token shown to the model instead of a raw case identifier. The server owns the token → CaseRef mapping.
TurnId
Identifies one user turn. Every command, replay record and response block produced while handling the turn carries it.
TurnInput
One user turn as accepted by the runtime (spec §9).
Understanding
Everything a turn was understood to say.
UnderstoodAct
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.
WorkflowKey
Stable key of a workflow definition, e.g. "trip".
WorkflowNotice
An informational, non-blocking element of a view.
WorkflowRegistry
Heterogeneous set of workflows keyed by WorkflowKey (spec §8.3).
WorkflowRegistryBuilder
Builds a WorkflowRegistry.
WorkflowVersion
Version label of a workflow definition. Must change whenever projection semantics change (spec §8.4).
WorkflowView
The pure projection of a case (spec §8.1).

Enums§

ActMutability
Whether an operation changes a record.
ActTarget
The record an act applies to.
ActionClass
What a stored option authorizes when it is chosen (spec §15.3).
AtomicityScope
How commands are grouped for all-or-nothing execution (spec §13.4).
BuildError
Why an Orchestrator could not be built.
ClaimMode
How the assistant may talk about the outcome of a command (spec §17.2).
CommandOrigin
Who or what authorized a command (spec §14.2).
ConfirmationPolicy
Confirmation a command requires before execution (spec §14.3).
ConstraintKind
A condition the user placed on the whole turn.
ExecutionError
Errors raised while executing a command batch.
ExternalStatus
Fine-grained state of a request another system decides (spec §17.4).
InteractionKind
The shape of an interaction (spec §15.2).
InteractionStatus
Lifecycle status of an interaction (spec §15.4).
OrchestrationMode
How much autonomy the model gets: which risk classes are eligible at all (§11.4).
OrchestratorError
Top-level error of a turn (spec §24).
PhaseOwnership
Who must act for the case to leave its current phase.
PlannedActResult
The explicit result of one act (spec §13.3, I11).
ReceiptEvent
One event as offered to WorkflowDefinition::receipts: its payload is either still in the ledger or has been erased.
ReceiptSeverity
Severity of a receipt.
ResolutionChannel
How a user’s answer to an interaction reached the server (spec §15.7).
ResponseBlock
One ordered block of an assistant turn (spec §18.1).
RiskClass
Risk class of a command, ordered from harmless to regulated (spec §14.3).
StoreError
Errors raised by persistence adapters.
StoredInteractionAction
The server-side meaning of an option (spec §15.3). Never supplied by the client.
TargetPolicy
Which record an operation may be aimed at.

Traits§

CaseDirectory
Names the cases an actor may address (spec §23 step D, §25.4).
TurnConsequences
What a turn’s writes imply on OTHER cases (spec §23 step M).
WorkflowDefinition
A workflow: pure projection plus deterministic compilation and policy (spec §8.2).
WorkflowExecutor
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 origin satisfy policy?

Type Aliases§

ViewOf
Shorthand for the view type of a definition.