Repository navigation
Record requests refused before reaching any upstream - #366
Merged
Merged
Conversation
A request whose key was recognised but that was refused before routing finished left no trace: no RequestStarted, no RequestRouted, nothing in the request store or in-flight. A mistyped model, a model no upstream offers, a model outside the key's allow list, a request no rule matches, or a gateway with no upstreams configured could not be seen in the traffic view. These now take the same path as a rule denial (Routed::Refused): the request starts with an empty upstream, is routed with an empty attempt chain, and fails with the original error code and arguments. The model on the row is the one the client sent. When no rule decided the route (no match, evaluation error, no upstreams) the deciding rule is empty. The response the client receives is unchanged (same status and body). Requests rejected before the key is identified (missing or invalid key, source address not allowed, non-POST, oversized body) are still not recorded. WebSocket: - Each Responses turn that is not sent (model outside the key's allow list, alias the connected upstream cannot serve, no rule matched, rule evaluation error) records a row, like a rule-denied turn. - A Realtime upgrade refused for its model, and an upgrade whose routing fails, record a row. Route stats do not count an empty rule name as a rule; such rows count toward the route only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs ThinkWatchProject/ThinkWatch-Lite#356
Problem
Once the gateway key is recognised, a request can still be refused before routing finishes: a mistyped model, a model no upstream offers, a model outside the key's
allowlist, a request no rule matches, or a gateway with no upstreams configured. These returned early with?beforeopen(), so there was noRequestStarted, noRequestRouted, no row in the request store and nothing in/in-flight. They were invisible in the traffic view and impossible to diagnose.Change
These cases now go through
Routed::Refused, the same path a rule denial already uses:RequestStartedwith an empty upstream;modelis the name the client sentRequestRoutedwith an empty attempt chainRequestFailedwith the original error code and arguments (gw.model.no_upstream,gw.model.not_allowed,gw.model.alias_unserved,gw.model.pinned_*, the*_rewrittenvariants,gw.route.failed,gw.config.no_upstreams)When the model admission fails, the row is attributed to the rule that decided the route. When no rule decided (no match, evaluation error, no upstreams), the deciding rule is empty; route stats count such rows toward the route only and do not report a rule with an empty name.
The response sent to the client is unchanged (same status code and error body). Requests rejected before the key is identified (missing or invalid key, source address not allowed, non-POST, oversized body) are still not recorded.
WebSocket
allow, alias the connected upstream cannot serve, no rule matched, rule evaluation error), not only rule denials.NotSentcarries which rule the row belongs to (Decider).No schema change and no control API version change.
Tests
tw-gateway/tests/routing_facts.rs: model outside the key'sallow, no rule matched, no upstreams configured; each asserts the start / empty-route / failure events and the unchanged client response.tw-gateway/tests/models.rs: a model no upstream serves.tw-gateway/tests/alias_ws.rs: per-turn alias and key-scope refusals and Realtime upgrade refusals now record a row (the old "no row" assertion is replaced).tw-control/tests/route_stats.rs: refused requests land in/historywith the client's model name and error code, and route stats count them without an empty rule.tw-storeunit test: an empty deciding rule counts toward the route only.🤖 Generated with Claude Code