Typed Decisions
Provider-neutral, versioned semantic choices for routing, model selection, and durable completion gates, with Jev as the first adapter.
Ductor's existing inference proxy generates text, tool calls, and agent actions. The typed-decision plane does something narrower: Ductor supplies bounded state and an exact set of allowed answers, and the evaluator returns a typed answer, probabilities, and confidence.
That distinction is the safety boundary. A decision model can rank choices that Ductor has already authorized; it cannot create authority or execute anything.
How this differs from inference
| Inference proxy | Typed decisions | |
|---|---|---|
| Job | Generate the next response or action | Select or classify from closed options |
| Wire API | Provider chat/messages APIs | System One / Decisions API |
| Output | Text, tool calls, usage | Typed answer, distribution, confidence, usage |
| Policy | Pinned model and tool/runtime bounds | Pinned question set, projection, options, thresholds |
| Failure | Agent/runtime error policy | Deterministic fallback or review policy |
The two layers work together. For example, typed model routing can choose an already-enabled inference provider/model; the chosen model still runs through the normal inference proxy.
Jev without vendor lock-in
Jev is the first evaluator, not an application-level dependency. Routing and agent packages depend on a provider-neutral evaluator contract. The only Jev/System One-specific code is the replaceable infrastructure adapter.
OpenRouter is optional
Ductor can call TypeSafe directly at https://api.typesafe.ai/v1/systemone
or OpenRouter at https://openrouter.ai/api/alpha/decisions. With the decision
plane disabled, Ductor makes no such call and preserves deterministic behavior.
OpenRouter chat-completions configuration under ai_inference is separate.
The typed-decision adapter does not send Jev through the OpenAI-compatible chat
endpoint.
ai:
decision:
enabled: true
provider: typesafe
endpoint: https://api.typesafe.ai/v1/systemone
api_key_env: TYPESAFE_API_KEY
max_concurrent: 32
allowed_data_classes: [public, internal]
circuit_breaker:
enabled: true
consecutive_failures: 5
open_timeout: 30s
rate_limit:
enabled: true
requests_per_minute: 120
burst: 20ai:
decision:
enabled: true
provider: openrouter
endpoint: https://openrouter.ai/api/alpha/decisions
api_key_env: OPENROUTER_API_KEYProvider selection is explicit. Ductor never switches providers because it happens to find a different credential.
Shipped consumers
Semantic recipient routing
Rank an exact, deterministic recipient shortlist; routing still owns eligibility, capacity, assignment, and finalization.
Agent model routing
Select among exact models already enabled by the inference configuration, then pin the choice and evidence into the immutable definition.
Completion gate
Classify a hard-gate-clean turn as close, continue, or review, with a bounded retry count and durable receipt.
Config or persisted policy—not provider code
Provider config contains only endpoint, credential reference, timeout, retry, and protocol limits. Consumer policy owns exact models, versions, question IDs, objectives, projections, thresholds, and rollout mode.
For agents, Ductor copies configured policy into a definition before its first immutable pin. A policy already present in a persisted definition wins. Changing config never changes an active session.
ai:
decision:
agent:
model_routing:
mode: shadow
policy_version: agent-model-policy-v1
candidates:
- { provider: openai, model: gpt-5-mini }
- { provider: anthropic, model: claude-haiku }
allow_fallback: true
evaluator_model: typesafe/jev-1.13-20260917
profile: agent-model
profile_version: v1
state_version: agent-model-state-v1
question_set_version: agent-model-questions-v1
question_id: select_model
objective: Choose the safest capable model for the admitted turn.
min_confidence: 0.8
max_state_bytes: 16384
data_class: internal
completion:
mode: shadow
evaluator_model: typesafe/jev-1.13-20260917
question_set_version: completion-v1
risk_class: medium
failure_policy: review
min_close_confidence: 0.9
max_continue_attempts: 2
state_projection_version: completion-state-v1
data_class: internalUse exact versioned models for enforcement. Moving aliases such as
~typesafe/jev-latest are appropriate only for development or shadow
evaluation because they can change without a Ductor policy version changing.
Enterprise controls
Decision calls use Ductor's DNS-rebind-safe egress transport, reject unclassified or disallowed state before network I/O, apply a Redis-backed per-tenant rate gate, cap concurrent calls, and isolate provider failures with a circuit breaker. They can also reserve tenant spend before provider I/O, reject an estimated call above its ceiling, record durable request/input/output/cost usage, and settle the reservation from actual input tokens.
ai:
decision:
metering:
enabled: true
monthly_budget_usd: 100
max_cost_per_decision_usd: 0.01
reservation_ttl: 5m
pricing_version: openrouter-2026-09-18
input_price_usd_per_million:
typesafe/jev-1.13-20260917: 0.042The price map must contain every requested and possible resolved model ID. Missing tenant identity, pricing, durable usage, or cross-replica budget coordination fails before the provider call.
Each consumer declares public, internal, confidential, or restricted.
Only classes present in allowed_data_classes may leave the deployment;
confidential and restricted projections require an explicit operator opt-in.
The call boundary is ordered so cheap, authoritative denials happen first:
An egress, rate, pricing, budget, or configuration denial makes no provider call. Provider retries remain inside one bounded total deadline. Only transport, timeout, overload, server, and malformed-provider failures count against the circuit; caller validation failures do not.
Receipts retain versioned identifiers, hashes, probabilities, confidence, and bounded outcome classes. They do not retain raw projected state, credentials, provider response bodies, free-form errors, or latency.
Failure, replay, and rollout
| Consumer | Provider failure or low confidence | Historical behavior |
|---|---|---|
| Recipient routing | Follow the profile's declared deterministic pipeline fallback. | original_lock reads the persisted slate and evidence with zero evaluator calls. |
| Agent model routing | Keep the static pinned model-policy result and record a bounded fallback class. | The immutable definition reuses its model receipt; active sessions are not re-routed. |
| Completion gate | Apply the pinned complete-or-review failure policy and bounded recovery counter. | The journal replays the committed completion receipt and ordered text_reset; it does not repeat evaluation. |
Roll out one consumer and one exact model/profile version at a time:
- Enable the provider boundary while leaving consumers
off. - Configure data classes, tenant rate limits, exact pricing, and durable metering.
- Run in
shadowand compare recommendations with deterministic outcomes. - Promote only after latency, cost, fallback, false-route or false-close, and review targets pass.
- Roll back the affected consumer to
off; immutable historical evidence and already pinned definitions remain readable.
Where it should—and should not—go next
Good future fits are case-assignee ranking, opt-in workflow variant selection, governed tool/connector disambiguation, advisory route-authoring review, and RFQ worker shortlisting. Each starts from an exact eligible set and ends at an existing authoritative boundary.
Do not use typed decisions for authentication, authorization, fraud or compliance enforcement, pricing or budget arithmetic, locks or leases, schema validation, idempotency, promotion, deployment, or exact replay.
Related
Inference Proxy
Ductor's provider-neutral inference vertical — unary and internal streaming paths, routing, circuit breakers, tenant budgets, usage, and provider credentials.
Semantic Routing
Use semantic_choice to rank an eligible recipient shortlist while preserving Ductor's deterministic routing authority.