Write Custom Routing Strategies
Experimental. A custom worker-selection policy controls how Dynamo filters and scores eligible workers, then selects one. Dynamo still owns discovery, eligibility, queueing, reservations, accounting, and metrics.
How It Works
This feature replaces the worker-ranking part of Dynamo’s routing pipeline. A WorkerFilter can exclude a host-eligible worker, a WorkerScorer assigns a finite cost to each remaining worker, Dynamo adds costs from all configured scorers, and a WorkerPicker chooses one row from the scored candidates. Filters run in declaration order before scoring, and rejecting every worker returns an error. Lower costs rank first by convention, but the picker can implement deterministic selection, sampling, tie-breaking, or policy-local state. Dynamo continues to own discovery, eligibility, score validation, accounting, and reservation.
For multiple compile-checked policies, see the custom policy examples. Repository contributors who use a coding agent must also provide the worker-selection API rules.
Choose the Policy Stage
An external policy owns its filters, scorers, and picker. Dynamo’s default scorer and picker are internal and can change with the built-in routing algorithm.
Build the Policy
Create the Policy and Catalog Crates
Set the Dynamo checkout and policy project paths:
Add dynamo-kv-router from the same checkout that builds the frontend or EPP:
Filter Workers
A filter answers a hard yes-or-no question about one worker that already passed Dynamo’s eligibility checks. Return true to keep the worker or false to remove it. Use a filter only when the worker must not receive the request; use a scorer for preferences.
This filter keeps workers with at least the configured number of device-resident overlap blocks:
Dynamo runs filters in declaration order before scoring. A worker must pass every configured filter. If no workers remain, selection returns an error.
Pass filters to WorkerSelectionPolicy::new_with_filters. If the policy has no hard requirement, omit filters and use WorkerSelectionPolicy::new.
Score Workers
A scorer expresses a preference without excluding a worker. It returns one finite cost for one worker. Lower total cost is better by convention.
This scorer uses the current number of active requests as its cost:
A policy can stack multiple scorers. Dynamo calls them in declaration order and adds their costs. Dynamo rejects a non-finite contribution or total.
Pick a Worker
A picker makes the final choice after filtering and scoring. It sees every remaining worker and its total cost, then returns one row index. Most policies pick the lowest cost, but a picker can instead sample, break ties, or use policy-local state.
This picker selects the lowest-cost row:
Candidate order is unspecified, so inspect explicit values instead of relying on row order. Dynamo rejects an out-of-range index before accounting or reservation.
Parse Parameters and Create the Factory
The provider runs once at startup. Parse and validate all parameters there, then capture the validated values in the factory:
Dynamo calls the returned factory once per routing partition. If prefill, decode, or standalone select workers need different components, branch on worker_type. Use the partition identity for distinct model or routing-group state.
Register the Policy
Expose a registration function from the policy crate:
Call this function from the catalog:
Choose a stable, unique type name. Unknown types, duplicate registrations, and invalid parameters stop startup.
Configure an Instance
Create $POLICY_DIR/worker-selection.yaml:
The type selects a registered provider. The name identifies one configured instance. DYN_ROUTER_WORKER_SELECTION_POLICY overrides worker_selection.default by instance name. Set the override to default to select Dynamo’s built-in policy.
Check the Policy and Catalog
Add one focused test for each policy decision and one registration test for every type name.
Available Signals
Request context and worker identity are always available. Dynamo calculates optional per-worker signals only for groups that a filter, scorer, or picker requests.
Request Context
Session Context
session_context() returns None when the request has no session metadata. This policy-facing view contains selected session metadata; it is not Dynamo’s internal request envelope. When present, it provides:
The custom policy examples use input_trigger() to give tool-result turns a cache-local picker path.
Worker Identity and Cost
Optional Worker Inputs
If a component needs no optional worker data, return WorkerInputs::NONE. Combine exact groups with |, such as WorkerInputs::CACHE | WorkerInputs::LOAD.
The cache accessors return raw tier counts. A missing tier or worker entry is zero; Dynamo does not substitute its weighted effective-overlap estimate. Each custom scorer chooses how to combine the raw counts.
A filter or scorer reads requested groups through WorkerCandidate::cache(), load(), or routing(). A picker reads index-aligned arrays through WorkerInputView. Each component must request every group that it reads.
Link the Policy Into Dynamo
Both paths use the same policy crate, catalog, and YAML file. Choose the process that owns worker selection.
Python Frontend
EPP
Add the catalog to the Python binding manifest. Keep the dependency alias dynamo-worker-selection-policy-catalog:
Build the extension with the linked catalog:
Policy Contract
- Return
truefrom a filter to keep a worker andfalseto reject it. - Expect filters to run in declaration order before scoring. Rejecting every worker returns an error.
- Return finite scorer costs.
- Return a valid picker row.
- Treat candidate order as unspecified.
- Request only the signal groups that the component reads.
- Keep blocking I/O and panics out of
keep,score, andpick. - Keep policy state local to the factory-created policy unless cross-partition sharing is a deliberate requirement.
- Build the policy against the same Dynamo revision as the frontend or EPP.
- If a signal adds work, storage, allocation, or another scan, run the worker-selection benchmark.
The example README contains the in-tree package names and build-check commands. For the built-in cost model, see Routing Concepts. For the standalone selection lifecycle, see Standalone Selection Service.