> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dari.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced Decision Pipeline

> Prepare, inspect, and finalize deterministic routing decisions.

Most apps should use `createRouter()` and a routing policy. Use the lower-level API when you need to inspect routing evidence, perform the selector call yourself, or persist state between phases.

## Prepare And Finalize

`prepareRoute(input)` does candidate compatibility, cost estimation, decision recovery, strategy pruning, eval matching, and selector-request construction. Then `finalizeRoute()` parses and validates the selector's answer.

```ts theme={null}
import { finalizeRoute, prepareRoute, type RouteInput } from "@dari/router-core";

const input: RouteInput = {
  candidateModels: ["openai/gpt-5.4-mini", "anthropic/claude-sonnet-4-6"],
  metadataLookup: (model) => modelMetadata[model]!,
  requiredCapabilities: [],
  strategy: "slm",
  pricing: (model) => modelPricing[model] ?? null,
  messages: conversation,
  chainsByModel: new Map([
    ["openai/gpt-5.4-mini", []],
    ["anthropic/claude-sonnet-4-6", []],
  ]),
  prefixHits: [],
  nowMs: Date.now(),
  toolChoiceFp: "",
  responseFormatFp: "",
  selectorModel: "openai/gpt-5.4-mini",
  selectorContextWindowChars: 400_000,
};

const prepared = prepareRoute(input);
if (!prepared.selectorPreparation) throw new Error("Custom routing rules are missing");

const selectorOutput = await callSelector(prepared.selectorPreparation.selectorRequest);
const result = finalizeRoute(prepared, selectorOutput);
console.log(result.decision);
```

<Accordion title="RouteInput fields">
  * **candidateModels** — model IDs to consider
  * **metadataLookup** — resolves model metadata
  * **pricing** — returns model/cache rates or null
  * **messages** — the conversation
  * **chainsByModel** — prefix hashes per model
  * **prefixHits** — stored provider-cache observations
  * **nowMs** — timestamp for cache-warmth checks
  * **toolChoiceFp / responseFormatFp** — prevent incompatible cache reuse
  * **selectorModel / selectorContextWindowChars** — selector config
  * **modelFallbackEnabled / fallbackRequiresDifferentProvider** — optional fallback
</Accordion>

The host provides all inputs explicitly — Router Core reads no env vars, credentials, database, or clock.

`route(input, selector)` is the convenience composition when you don't need to interleave work.

## Inspect Prepared Evidence

The prepared result exposes:

* Compatible model/reasoning pairs
* Cache-aware cost estimates (routing evidence, not billing)
* Candidates removed by pruning
* Recovered previous decision and conversation identity
* Warnings from best-effort cost estimation
* The exact selector request

Unknown pricing stays unknown, not treated as free.

## Selector Output

The selector returns JSON with `selected_model`, `reasoning_effort`, and `reason`. `finalizeRoute()` validates the selected pair appeared in the prepared candidates.

The pipeline can also request a fallback. `modelFallbackEnabled` controls whether one is required, `fallbackRequiresDifferentProvider` controls provider diversity. Fallback execution is host-owned.

## Cache State

Provider caches make model continuity cheaper than switching. Supply:

* **chainsByModel** — ordered prefix hashes per model
* **prefixHits** — stored provider-cache observations
* **nowMs** — determines if an observation is still warm
* **toolChoiceFp / responseFormatFp** — prevent incompatible cache reuse

A fresh conversation uses empty chains and no hits. After a turn, the host records the serving model, reasoning effort, usage, prefix hash, and decision. `prefixChain()` creates Chat Completions hashes; `optionsFingerprints()` creates the expected fingerprint values.

## Anonymous Selection

For training or evaluation, prevent the selector from memorizing model names:

```ts theme={null}
import { assignAnonymousActions, anonymizeSelectorInput, parseAnonymousActionSelection } from "@dari/router-core";

const mapping = assignAnonymousActions(candidates, Math.random); // shuffled letter labels
const anonymized = anonymizeSelectorInput(selectorInput, mapping);
const realPair = parseAnonymousActionSelection(anonymizedOutput, mapping);
```

The second argument is a random source in `[0, 1)` — pass a seeded PRNG to reproduce recorded permutations.

Intended for policy training, not production routing.

## Speculative Execution

Because `prepareRoute` and `finalizeRoute` are separate, the host can start the compatible previous model while the selector runs. See the [Speculative Routing](/framework/speculative-routing) guide for eligibility and failure semantics.

A complete no-network example is at `examples/basic_route.ts` — run it with `bun run example:decision-pipeline`.
