Symptom
switchyard.requests is emitted without a tier attribute when an llm_classifier decision comes from the capable-target fallback or from affinity reuse. The same strong model lands in two time series, one labeled and one not, and a metrics consumer cannot unify them or tell a deliberate strong classification from a fallback. In one production session we saw 1 request labeled tier="weak" and 45 requests on the strong model with no tier at all.
Reproduction
Covered by two tests in the linked PR, which fail on current main:
judge_unavailable_fallback_keeps_the_tier_label: judge upstream errors, the cascade's DefaultTarget decides, and the emitted series has no tier.
affinity_reuse_keeps_the_tier_label: the second request of a pinned session is decided by AffinityRouter and likewise loses the tier.
Expected vs. actual
- Expected: every routed decision carries the tier of the model actually selected, as the context-overflow fallback already does via
fallback_decision's cascade find_map (crates/libsy/src/algorithms/fall_through.rs:221-233).
- Actual:
DefaultTarget and AffinityRouter do not implement Classifier::routing_tier, the trait default returns None (crates/libsy/src/core/classifier.rs:75-77), and the tier attribute is omitted at emission (crates/libsy/src/observability.rs:548-551, decision tier from fall_through.rs:294). The routing log has the twin symptom: an empty tier that is indistinguishable from a route with no tiering.
Environment
- Switchyard version / commit: current
main; first observed on a v0.1.0 build (6ff491ac)
- OS / arch: macOS ARM64, source build
- Inbound format: Chat Completions
- Backend: OpenAI-compatible (observed live via OpenRouter)
Additional context
Related: #205 counts how often the classifier failed open and why; this issue is about which tier actually served, so the two compose. #272 covers why agentic sessions hit the fallback so often. Fix in the linked PR resolves the tier across the cascade with the same find_map pattern the overflow path uses.
Symptom
switchyard.requestsis emitted without atierattribute when anllm_classifierdecision comes from the capable-target fallback or from affinity reuse. The same strong model lands in two time series, one labeled and one not, and a metrics consumer cannot unify them or tell a deliberate strong classification from a fallback. In one production session we saw 1 request labeledtier="weak"and 45 requests on the strong model with no tier at all.Reproduction
Covered by two tests in the linked PR, which fail on current main:
judge_unavailable_fallback_keeps_the_tier_label: judge upstream errors, the cascade'sDefaultTargetdecides, and the emitted series has no tier.affinity_reuse_keeps_the_tier_label: the second request of a pinned session is decided byAffinityRouterand likewise loses the tier.Expected vs. actual
fallback_decision's cascadefind_map(crates/libsy/src/algorithms/fall_through.rs:221-233).DefaultTargetandAffinityRouterdo not implementClassifier::routing_tier, the trait default returnsNone(crates/libsy/src/core/classifier.rs:75-77), and the tier attribute is omitted at emission (crates/libsy/src/observability.rs:548-551, decision tier fromfall_through.rs:294). The routing log has the twin symptom: an empty tier that is indistinguishable from a route with no tiering.Environment
main; first observed on a v0.1.0 build (6ff491ac)Additional context
Related: #205 counts how often the classifier failed open and why; this issue is about which tier actually served, so the two compose. #272 covers why agentic sessions hit the fallback so often. Fix in the linked PR resolves the tier across the cascade with the same
find_mappattern the overflow path uses.