Repository navigation
Description
Problem
While reviewing #1042, we ran into a case where completion candidates need different versions of the same request:
Cloud model → request containing the handoff note
Local fallback → original request
RoutingOutcome now has an ordered candidate list, request, fallback_request, and a candidate_requests map. #1042 added the fallback override, #1176 added per-candidate requests, and #1299 exposed the map in Go.
A candidate-specific request takes precedence. Otherwise, later candidates use fallback_request when present. The default is outcome.request, which may already contain routing changes.
Those fields handle the immediate cases, but hosts must apply the same selection rules. Adding public Rust fields breaks callers that construct the struct directly. Hosts using constructors may keep compiling while ignoring a new override.
The question here is whether grouping requests and candidates would make that contract simpler. Per-candidate requests are already supported.
Proposed solution
Consider an ordered completion plan that groups each request with the models that should receive it:
// Design sketch, not existing code.
struct RequestGroup {
request: Request,
models: Vec<ModelId>,
}
struct CompletionPlan {
groups: Vec<RequestGroup>,
}Ordinary routing would produce one group:
Original request → [selected model, fallback models]
VGR could produce two:
Handoff request → [cloud]
Original request → [local]
The host would try candidates in order under its existing fallback rules. It would not need to know which router produced the plan.
Each group would own one request. Iteration could borrow the model/request pairs rather than copy the request for every candidate. Keeping the original and rewritten versions still requires retaining both; this does not remove that cost.
Alternatives considered
- Keep
fallback_requestandcandidate_requests: the current implementation already handles these cases. A grouped plan needs enough benefit to justify migration. - Add a candidate iterator over the existing fields: centralizes request selection, but existing hosts could still ignore it.
- Introduce router-specific outcome types: makes hosts handle router details instead of a common execution format.
Scope notes
This is a design discussion, not a committed rewrite or a claim that the existing fields are inherently wrong.
A completion plan would itself be a broader breaking change. Replacing the old execution fields could force Rust hosts to update; merely adding another accessor would not. Python bindings and custom hosts would need an explicit migration too.
Keep this focused on associating candidates with requests. It does not require a request graph, new routing policy, or moving target preparation, retries or streaming into libsy.
Activity