Expand description
The ordered request-shaping pipeline, and the one statement of its order.
§The stages
| # | Stage | Lives in | Reads |
|---|---|---|---|
| 1 | Strip prior reasoning | super::messages | messages |
| 2 | Coalesce for capabilities | super::messages | messages |
| 3 | Truncate stale history | super::truncation | messages, payload size |
| 4 | Resolve the sampling hierarchy | super::sampling | top-level keys |
| 5 | Pin cache_prompt | super::sampling | top-level keys |
§The order is load-bearing
1 before 2. Coalescing merges message content. Stripping afterwards
would have to find and excise <think> blocks inside text that has already
been concatenated with "\n\n" separators from other turns.
2 before 3. Both stages 1 and 2 only ever shrink the body, and stage 3 measures it. Truncating first would size its budget against bytes that were about to be discarded anyway, and trim history that did not need trimming.
3 before 4. Stage 3 measures the payload; stage 4 inserts up to seven sampling keys. Resolving sampling first would have truncation size its budget against keys the client never sent. The margin is small, but it is the difference between measuring the conversation and measuring our own additions to it.
4 before 5. cache_prompt is not an InferenceConfig field, so
pinning it last means the resolved sampling patch can never overwrite it.
§Why the seam is &mut Value and not a typed request struct
The proxy forwards requests from arbitrary external clients — IDE
extensions, gateways — which send OpenAI parameters this workspace has
never heard of. Round-tripping through a typed ChatRequest would silently
drop every field the struct does not model: a passthrough regression that
is invisible in tests and painful in the field. Mutating a Value in place
preserves them by construction. The adapter builds its body with json! and
already holds a Value, so this is also the cheaper side for it.
§One pipeline, two callers, no second route
Every request path calls apply. The proxy used to run the stages by hand
with its own truncation pass spliced between them, because truncation gated
on the payload’s size in wire bytes and could reject the request with an
axum response — neither of which fits here. Measuring the serialized
Value and returning a domain error removed both obstacles, so there is now
exactly one implementation of the order above and nothing to keep in sync.
Functions§
- apply
- Apply every request-shaping transform, in order, in place.