Skip to content

Model routing: make the selection advisory instead of pinning requests - #9272

Merged
lpcox merged 5 commits into
mainfrom
copilot/model-routing-advisory-selection
Sep 30, 2026
Merged

lpcox merged 5 commits into
mainfrom
copilot/model-routing-advisory-selection

Conversation

Copilot AI commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Routing picked one (provider, wire_model, endpoint, effort) per run, and the API proxy rejected anything else with 403 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 own model:. This PR turns the selection into a suggestion the agent starts from. AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS stay the only model enforcement surface.

Proxy (containers/api-proxy)

  • Screening removed. Gone: the body model/effort/endpoint checks, the model|reasoning|effort header rejection, the 403 model_routing_mismatch response, and the blanket upgrade rejection (rejectUpgrade). WebSocket upgrades are proxied normally during a routed run.
  • Renamed routing-enforcement.js → routing-observation.js (createRoutingObservation), with a matching update to the Dockerfile COPY list. screenRequest becomes observeRequest, which never rejects or writes a response.
  • Kept: observeFailure, recordNativeFailure, the onSseData hook, trackResponse and drain. They cover inference POSTs to the selected provider. Requests that arrive once draining has started are proxied but not observed.
  • Upstream failures are fatal only for the selected model. A native error, SSE error event or early-closed response ends the run with exit 78 only if the request used the selected provider and model. A different model's upstream error, or a policy-guard 403 for it, does not.
  • server-factory.js: the routing transform is observe-only (returns null) and runs before adapter.getBodyTransform() via composeBodyTransforms. Routed requests therefore get alias resolution and fallback like any other request.
  • Pinning-only exclusions removed from body-handler.js (steering) and upstream-response.js (400 model-fallback buffering).

Deviation telemetry

One model_routing log 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_model and requested_effort are truncated to 200 characters.

Unchanged

  • The router is still invoked once per run, from the task description, and classification and selection are unchanged. /reflect still reports routing, now purely as a seed for the agent's model.
  • Exit 78 still applies to no_route, an unreachable router, and contract or configuration errors.

Docs and smoke

  • docs/awf-config-spec.md §13a and docs/api-proxy-sidecar.md now 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

  • Limiting failure tracking to the selected model is a judgement call. Without it, a sub-agent model's upstream error, or a policy rejection of it, would end the run with exit 78.
  • There is still no effort cap. The follow-on proposed in the issue is a per-model effort limit in model policy.

Copilot AI and others added 2 commits September 30, 2026 22:02
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
… selected model

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Update model routing for advisory selection Model routing: make the selection advisory instead of pinning requests Sep 30, 2026
Copilot AI requested a review from lpcox September 30, 2026 22:09
@lpcox
lpcox marked this pull request as ready for review September 30, 2026 22:09
Copilot AI balanced review requested due to automatic review settings September 30, 2026 22:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 High severity

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>
@lpcox

lpcox commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback

@github-actions

Copy link
Copy Markdown
Contributor

✅ Copilot review passed with no inline comments.

@copilot Add the ready-for-aw label to this PR to trigger agentic CI smoke tests.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Documentation Preview

Documentation has been built for this PR.

Download preview artifact

To view locally:

  1. Download the docs-preview-pr-9272 artifact from the workflow run
  2. Unzip and open index.html in your browser

Built from commit ba6c553

@github-actions

Copy link
Copy Markdown
Contributor

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 92.70% 92.71% ➡️ +0.01%
Statements 91.21% 91.22% 📈 +0.01%
Functions 89.30% 89.30% ➡️ +0.00%
Branches 84.27% 84.28% 📈 +0.01%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/log-directory-setup.ts 96.2% → 100.0% (+3.78%) 96.3% → 100.0% (+3.71%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Copilot AI commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback

Added regression coverage for exact model comparisons before telemetry truncation, including both boundary cases. Commit: 6cdad9a.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

❌ Smoke Copilot BYOK AOAI (Entra) reports failed. AOAI BYOK (Entra) mode investigation needed...

🪪 BYOK (AOAI Entra) report filed by Smoke Copilot BYOK AOAI (Entra)

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅

🛡️ Egress verdict from Smoke Copilot Network Isolation

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ Build Test Suite completed successfully!

Generated by Build Test Suite for #9272

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Smoke Cloud Hypervisor completed. Cloud Hypervisor + Copilot passed.

Cloud Hypervisor + Copilot smoke test by Smoke Cloud Hypervisor

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • clients2.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "clients2.google.com"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

📰 BREAKING: Report filed by Smoke Copilot

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

🔌 Smoke Services — All services reachable! ✅

🔌 Service connectivity validated by Smoke Services

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

❌ Security Guard failed. Please review the logs for details.

Generated by Security Guard for #9272

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

❌ Smoke Claude failed

Generated by Smoke Claude for #9272

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

❌ Smoke Copilot BYOK AOAI (api-key) reports failed. AOAI BYOK (api-key) mode investigation needed...

🔑 BYOK (AOAI api-key) report filed by Smoke Copilot BYOK AOAI (api-key)

@github-actions

Copy link
Copy Markdown
Contributor

✅ Smoke Test: Copilot BYOK (Direct) Mode — PASS

  • ✅ GitHub MCP: Retrieved merged PRs (e.g., Persist Schema & Spec Sync state across runs #9262: "Persist Schema & Spec Sync state across runs")
  • ✅ GitHub.com Connectivity: HTTP 200
  • ✅ File Write/Read: /tmp/gh-aw/agent/smoke-test-copilot-byok.txt verified
  • ✅ BYOK Inference: Responding in direct BYOK mode (COPILOT_PROVIDER_API_KEY via api-proxy → api.githubcopilot.com)

Running in direct BYOK mode with api-proxy sidecar authentication. All systems operational.

🔑 BYOK report filed by Smoke Copilot BYOK
Add label ready-for-aw to run again

@github-actions github-actions Bot added the smoke-copilot-network-isolation Copilot network-isolation egress smoke test label Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

EGRESS_RESULT allow=pass deny=pass

  • ✅ Allowed: api.github.com → 200
  • ✅ Blocked: example.com failed (TLS cert error, exit≠0)
  • Overall: PASS (triggered by @lpcox)

🛡️ Egress verdict from Smoke Copilot Network Isolation
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Copilot: PASS ✅

  • MCP (last merged PR: "Update vulnerable development dependencies") ✅
  • github.com HTTP 200 ✅
  • File write/read ✅
    Author: @Copilot; assignees: @lpcox @Copilot

📰 BREAKING: Report filed by Smoke Copilot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Cloud Hypervisor + Copilot

  1. list_pull_requests (github/gh-aw-firewall): PASS — call succeeded, returned data.
  2. curl https://github.com: FAIL (inconclusive) — sandbox denied all curl invocations ("Permission denied and could not request permission from user") before any network attempt was made; not an AWF firewall block.
  3. Write/read /tmp/gh-aw/agent/smoke-cloud-hypervisor-*.txt: PASS — wrote and read back unique line successfully.
  4. curl (example.com/redacted) (expect blocked): FAIL (inconclusive) — same sandbox-level curl denial as check 2, so block behavior could not be verified.

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.

Cloud Hypervisor + Copilot smoke test by Smoke Cloud Hypervisor
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke services: ✅ Redis PONG · ✅ pg_isready · ✅ SELECT 1 → PASS

🔌 Service connectivity validated by Smoke Services
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

OTEL smoke test (PR #9272)

  • ✅ S1 Module loading: otel.js loads; exports startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled (+ internals)
  • ✅ S2 Tests: 3 OTEL suites, 68/68 passed
  • ✅ S3 Env forwarding: trace ID/parent span ID in env-passthrough.ts; OTEL endpoint/headers/trace vars in api-proxy-env-config.ts
  • ✅ S4 Token tracker: onUsage callback present in token-tracker-http.js
  • ✅ S5 Diagnostics: /tmp/gh-aw/otel.jsonl exists (~3.4KB), so spans were written

Note: pre-step outputs weren't available, so I re-ran the checks directly.

📡 OTel tracing validated by Smoke OTel Tracing
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia ❌ not run ❌ FAIL
Bun hono ❌ not run ❌ FAIL
C++ fmt ✅ N/A ✅ PASS
C++ json ✅ N/A ✅ PASS
Deno oak ❌ not run ❌ FAIL
Deno std ❌ not run ❌ FAIL
.NET hello-world ✅ N/A ✅ PASS
.NET json-parse ✅ N/A ✅ PASS
Go color ✅ 1/1 passed ✅ PASS
Go env ✅ 1/1 passed ✅ PASS
Go uuid ✅ 1/1 passed ✅ PASS
Java gson ✅ 1/1 passed ✅ PASS
Java caffeine ✅ 1/1 passed ✅ PASS
Node.js clsx ✅ pass ✅ PASS
Node.js execa ✅ pass ✅ PASS
Node.js p-limit ✅ pass ✅ PASS
Rust fd ✅ 1/1 passed ✅ PASS
Rust zoxide ✅ 1/1 passed ✅ PASS

Overall: 6/8 ecosystems passed — FAIL

Failures

  • Bun / Deno: The sandbox denied the curl ... | bash and curl ... | sh install commands ("Permission denied and could not request permission from user"). Neither runtime was installed, so its projects never ran. I did not try to work around the block.
  • Java note: ~/.m2 was not writable, so Maven ran with -Dmaven.repo.local=/tmp/gh-aw/agent/m2. Compile and tests passed.

All eight repositories cloned successfully. The build-test label was not added because of the failures.

Generated by Build Test Suite for #9272 · copilot · auto · 32.1 AIC · ⊞ 11.8K · ◷
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Chroot Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.14 Python 3.12.14 ✅ YES
Node.js v24.21.0 v2.98.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Result: Not all tests passed (Node.js mismatch), so the smoke-chroot label was not added.

Tested by Smoke Chroot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

PRs: "Persist Schema & Spec Sync state across runs" | "docs: use description and classification for routing"
Merged PR review ✅
PR detail ✅
Playwright title ✅
File write/read ✅
Discussion lookup/comment ✅
Build AWF ✅
Overall: PASS

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • clients2.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "clients2.google.com"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex
Add label ready-for-aw to run again

This branch was previously deployed

1 inactive deployment
aoai-model — 6cdad9a3 Deployed Sep 30, 2026 by lpcox via conclusion #1835
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model routing: drop strict request pinning and make the selection advisory

3 participants