Repository navigation
Model routing: make the selection advisory instead of pinning requests - #9272
Conversation
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
… selected model Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Model telemetry truncation can misclassify a different model as selected and incorrectly make its failure fatal.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Makes model routing advisory while retaining model-policy enforcement and selected-model failure tracking.
Changes:
- Replaces request pinning with routing deviation telemetry.
- Restores fallback, steering, and WebSocket behavior for routed runs.
- Updates tests, smoke coverage, container packaging, and documentation.
| File | Description |
|---|---|
src/commands/main-action.test.ts |
Updates routing failure fixture. |
docs/awf-config-spec.md |
Documents advisory routing semantics. |
docs/api-proxy-sidecar.md |
Updates proxy routing guidance. |
containers/api-proxy/upstream-response.js |
Enables routed-request fallback handling. |
containers/api-proxy/server-factory.test.js |
Tests observation and WebSocket proxying. |
containers/api-proxy/server-factory.js |
Composes observation and adapter transforms. |
containers/api-proxy/routing-runtime.test.js |
Tests native routed failures. |
containers/api-proxy/routing-runtime.js |
Installs the observation implementation. |
containers/api-proxy/routing-observation.test.js |
Covers telemetry and failure tracking. |
containers/api-proxy/routing-observation.js |
Implements advisory routing observation. |
containers/api-proxy/routing-enforcement.test.js |
Removes enforcement tests. |
containers/api-proxy/routing-enforcement.js |
Removes strict request pinning. |
containers/api-proxy/Dockerfile |
Packages the renamed observation module. |
containers/api-proxy/body-handler.js |
Re-enables steering for routed requests. |
.github/workflows/test-model-routing.yml |
Verifies non-selected models succeed. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
@copilot address review feedback |
|
✅ Copilot review passed with no inline comments. @copilot Add the |
Documentation PreviewDocumentation has been built for this PR. To view locally:
Built from commit ba6c553 |
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
Added regression coverage for exact model comparisons before telemetry truncation, including both boundary cases. Commit: |
|
❌ Smoke Copilot BYOK AOAI (Entra) reports failed. AOAI BYOK (Entra) mode investigation needed...
|
|
🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅
|
|
✅ Build Test Suite completed successfully!
|
|
Smoke Cloud Hypervisor completed. Cloud Hypervisor + Copilot passed.
|
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "clients2.google.com"See Network Configuration for more information.
|
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤
|
|
🔌 Smoke Services — All services reachable! ✅
|
|
❌ Security Guard failed. Please review the logs for details.
|
|
✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓
|
|
❌ Smoke Claude failed
|
|
📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅
|
|
❌ Smoke Copilot BYOK AOAI (api-key) reports failed. AOAI BYOK (api-key) mode investigation needed...
|
✅ Smoke Test: Copilot BYOK (Direct) Mode — PASS
Running in direct BYOK mode with api-proxy sidecar authentication. All systems operational.
|
|
EGRESS_RESULT allow=pass deny=pass
|
|
Smoke Copilot: PASS ✅
|
Smoke Test: Cloud Hypervisor + Copilot
Note: checks 2 and 4 could not execute any curl command at all in this session (permission layer blocked the tool call itself), so firewall allow/deny behavior for outbound HTTPS was not actually exercised.
|
|
Smoke services: ✅ Redis PONG · ✅ pg_isready · ✅ SELECT 1 → PASS
|
|
OTEL smoke test (PR #9272)
Note: pre-step outputs weren't available, so I re-ran the checks directly.
|
🏗️ Build Test Suite Results
Overall: 6/8 ecosystems passed — FAIL Failures
All eight repositories cloned successfully. The
|
Chroot Version Comparison
Result: Not all tests passed (Node.js mismatch), so the
|
|
PRs: "Persist Schema & Spec Sync state across runs" | "docs: use description and classification for routing" Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "clients2.google.com"See Network Configuration for more information.
|

Routing picked one
(provider, wire_model, endpoint, effort)per run, and the API proxy rejected anything else with403 model_routing_mismatch, ending the run with exit 78. Any legitimate second model was therefore fatal, for example a gh-aw sub-agent that declares its ownmodel:. This PR turns the selection into a suggestion the agent starts from.AWF_ALLOWED_MODELS/AWF_DISALLOWED_MODELSstay the only model enforcement surface.Proxy (
containers/api-proxy)model|reasoning|effortheader rejection, the403 model_routing_mismatchresponse, and the blanket upgrade rejection (rejectUpgrade). WebSocket upgrades are proxied normally during a routed run.routing-enforcement.js→routing-observation.js(createRoutingObservation), with a matching update to the DockerfileCOPYlist.screenRequestbecomesobserveRequest, which never rejects or writes a response.observeFailure,recordNativeFailure, theonSseDatahook,trackResponseanddrain. They cover inference POSTs to the selected provider. Requests that arrive once draining has started are proxied but not observed.403for it, does not.server-factory.js: the routing transform is observe-only (returnsnull) and runs beforeadapter.getBodyTransform()viacomposeBodyTransforms. Routed requests therefore get alias resolution and fallback like any other request.body-handler.js(steering) andupstream-response.js(400 model-fallback buffering).Deviation telemetry
One
model_routinglog event per inference request:{ "event": "model_routing", "stage": "request", "routed": "deviated", "deviations": ["model", "effort", "endpoint"], "provider": "copilot", "requested_model": "small-model", "requested_effort": "high", "selected_provider": "copilot", "selected_model": "gpt-5.4-mini", "selected_effort": "low", "selected_endpoint": "/responses" }requested_modelandrequested_effortare truncated to 200 characters.Unchanged
/reflectstill reportsrouting, now purely as a seed for the agent's model.no_route, an unreachable router, and contract or configuration errors.Docs and smoke
docs/awf-config-spec.md§13a anddocs/api-proxy-sidecar.mdnow describe the selection as advisory and say model policy bounds cost.test-model-routing.yml: the non-selected candidate must now succeed (exit 0), replacing the 403 / exit-78 assertion.Review notes