Broomva

Inference

The InferenceBackend trait layer — the runtime contract for agent-loop compute. Hardware vendors compete underneath.

Inference

Inference is the compute contract layer of the Agent OS. It defines the InferenceBackend trait — the interface every hardware and software inference backend must implement to serve agent-loop compute. The Life Agent OS owns this contract; hardware vendors (Apple Silicon, CUDA, Groq, Tenstorrent) compete underneath it.

The strategic positioning: the POSIX of agent silicon. POSIX did not build operating systems — it owned the interface that every OS had to implement. CUDA is NVIDIA's moat not because Nvidia builds great GPUs, but because it owns the runtime contract that every ML framework targets. Spec E is the same play applied to agent-loop compute.

Spec E, E-Sub-A shipped. inference-core and life-inference are on main with 24 passing tests. The InProcessInferenceBackend (in-process dev backend) is real. All remaining backends — inference-mlx, inference-vllm, inference-vigil, inference-autonomic, inference-conformance — are planned in subsequent sub-phases.

Biological analogy

Inference corresponds to the motor cortex / neuromuscular junction in biological systems. The motor cortex issues movement commands through a standard electrochemical contract that every muscle fiber implements, regardless of which muscle group is being activated. The cortex does not care whether the signal goes to a bicep or a quadricep — the contract is uniform.

InferenceBackend plays the same role: the kernel (the motor cortex) issues a StepContext through a standard Rust trait. Whether the step runs on an M4 Max via MLX, a CUDA cluster via vLLM, or an in-process mock during testing, the kernel sees identical behavior. The backend is the muscle; the trait is the neuromuscular junction.

Architecture

Inference is structured as two shipped crates today, with five more planned:

CrateStatusRole
inference-coreShipped (E-Sub-A)InferenceBackend trait, StepContext, Token, CloseCode, InferenceError, BackendCapabilities, routing types
life-inferenceShipped (E-Sub-A)InProcessInferenceBackend, InMemoryKvCache, InferenceRouter, workspace integration
inference-mlxPlanned (E-Sub-B)Apple Silicon backend via MLX
inference-vllmPlanned (E-Sub-C)CUDA backend via vLLM
inference-vigilPlanned (E-Sub-D)15-series metrics instrumentation
inference-autonomicPlanned (E-Sub-E)Lago-backed KvCache replacing InMemoryKvCache
inference-conformancePlanned (E-Sub-F)Full conformance test battery for all backends

The InferenceBackend trait

pub trait InferenceBackend: Send + Sync {
    /// Stable identifier — e.g. "mlx", "vllm", "groq", "tt-wormhole".
    /// Must be unique per deployment. Used in routing, metrics, and logs.
    fn backend_id(&self) -> &str;

    /// Cheap static query. Called frequently; must not block.
    fn capabilities(&self) -> &BackendCapabilities;

    /// Execute one model step. Returns a token stream that closes with
    /// `Token::Done` on success or `InferenceError::Backend { code }` on failure.
    fn step<'a>(
        &'a self,
        ctx: StepContext<'a>,
    ) -> Pin<Box<dyn Stream<Item = Result<Token, InferenceError>> + Send + 'a>>;
}

step is the only non-trivial method. It accepts a StepContext (the compiled prompt, KV cache handle, routing hint, budget constraints) and returns a token stream. Consumers iterate the stream and accumulate tokens until they see Token::Done or an error.

BackendCapabilities

pub struct BackendCapabilities {
    pub supports_tool_use: bool,
    pub supports_speculative_decoding: bool,
    pub max_context_tokens: u32,
    pub supports_streaming: bool,
}

The default implementation of supports_speculative_decoding panics with a clear message. This is a deliberate lock: L5-D3 in the Spec E locked decisions. The #[should_panic] test in inprocess_smoke anchors this contract.

CloseCode

The close codes are distinct from lifegw §6.5 wire codes numerically, though they represent the same semantic categories:

CodeMeaning
NormalInference completed successfully
UnsupportedFrameThe backend received a message type it cannot process
DeadlineBudget limit (token, time, or cost) exceeded
KvEvictedThe KV cache entry for this session was evicted — reconnect required
ToolAwaitTool call pending — caller must execute via Praxis and reconnect

ToolAwait — the reconnect pattern

ToolAwait is the most important close code for agent-loop compute. When a model produces a tool call, the backend cannot continue inference until the tool result is known. Rather than blocking, the backend closes the stream with CloseCode::ToolAwait and includes the tool call in the final InferenceError. The caller:

Receives CloseCode::ToolAwait on the stream. The error payload contains the pending tool call(s).

Dispatches the tool call to Praxis — the canonical tool execution engine. Praxis executes the tool and returns a result.

Reconnects to the same backend (or a new backend via the router), providing the original StepContext plus the tool result appended to the message history.

The backend resumes inference from where it left off, using the KV cache if the session_id is still live in the cache.

This pattern means inference backends never need to know about Praxis, MCP, or any tool implementation. The agent loop in Arcan orchestrates the reconnect. The backend is stateless between reconnects.

Routing

InferenceRouter distributes requests across multiple registered backends:

let mut router = InferenceRouter::new();
router.register(InProcessInferenceBackend::new("mock"));
// future: router.register(MlxBackend::new(mlx_config));
// future: router.register(VllmBackend::new(vllm_config));

let stream = router.step(ctx).await?;

Routing decisions use RoutingHint (caller preference) and WorkloadClass (the classification of the current request — interactive, batch, speculative). The router selects the backend that best matches the workload class given current backend capabilities and load.

KV cache

InMemoryKvCache is the E-Sub-A reference implementation. It stores KV entries keyed by (session_id, backend_id) with a configurable capacity and LRU eviction. When eviction occurs, the backend emits CloseCode::KvEvicted on the next step request for that session.

inference-autonomic (E-Sub-E) will replace InMemoryKvCache with a Lago-backed durable cache. Anima rotation events become KV invalidation signals — when an agent rotates its identity, its cached KV state is automatically purged.

Locked decisions (E-Sub-A)

DecisionChoiceRationale
L5-D1Separate crates/inference/ cluster, sibling of anima/ and autonomic/Clean dependency boundary; inference has no business knowing about identity or policy
L5-D2Lago-backed KvCache by default (planned E-Sub-E)Durability across backend restarts; pairs with Lago replay
L5-D3Speculative decoding opt-in, default impl panicsPrevents silent fallback to slow path; forces explicit capability declaration
L5-D4Spec C₃ §6.5 close codes — distinct from lifegw codesClean interface; lifegw codes are wire-level, CloseCode is semantic
L5-D5Tool dispatch escapes to Praxis via ToolAwait reconnectBackends stay stateless; tool knowledge lives in Arcan/Praxis
L5-D6Anima-scoped KV (session_id keyed) — composes with Spec D rotationIdentity rotation = cache invalidation without extra plumbing
L5-D7Dynamic backend selection per call via RoutingHintWorkload-class routing; interactive vs batch vs speculative can use different hardware
L5-D8Public spec publication deferred post-Phase-1Establish the contract inside the Life OS first; publish when the conformance battery exists

Test coverage

At E-Sub-A ship: 24 tests — 20 unit tests for core types and routing logic, 1 conformance scaffold test, and 3 inprocess_smoke tests including the #[should_panic] lock for L5-D3 (speculative decoding default-impl panic contract). All 14 CI checks passed: Test Linux (11m), Test macOS (13m), Build Release (24m), Lint (4m), MSRV (4m), Security Audit (3m), plus 8 fast-path checks.

Relation to other subsystems

SubsystemRelationship
ArcanArcan's provider layer (today: Anthropic, OpenAI-compatible, Mock) will migrate to InferenceBackend backends. Arcan orchestrates the ToolAwait reconnect loop.
AnimaAnima rotation events invalidate KV cache entries. inference-autonomic (E-Sub-E) wires this signal. The session_id key is Anima-scoped.
PraxisPraxis executes tool calls when the backend emits CloseCode::ToolAwait. Inference backends never call Praxis directly — Arcan mediates.
Vigilinference-vigil (E-Sub-D) instruments all backend calls with 15-series metrics: tokens/sec, time-to-first-token, KV hit rate, close code distribution.
Lagoinference-autonomic (E-Sub-E) backs the KV cache with Lago for durability across restarts.

Strategic note. The Spec E contract is positioned as the bid for "POSIX of agent silicon" — own the runtime contract, hardware vendors compete underneath. The same play that made CUDA NVIDIA's moat. The contract is intentionally minimal at E-Sub-A: backend_id, capabilities, and step. Everything else (routing, caching, metrics, conformance) layers on top without touching the trait surface.

On this page