Repository navigation
Infer Copilot wire API for utility model variants - #64177
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
✅ Ponytail Reviewer completed successfully!
|
|
✅ PR Code Quality Reviewer completed the code quality review.
|
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft. No ADR enforcement needed: PR does not have the "implementation" label and has ≤100 new lines of code in business logic directories.
|
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff
|
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅
|
There was a problem hiding this comment.
Ponytail review: one simplification opportunity found in changed lines only.
net: -4 lines possible.
Generated by ✂️ Ponytail Reviewer for #64177 · codex · gpt53codex · 3.25 AIC · ⌖ 3.29 AIC · ⊞ 12.5K
Comment /ponytail to run again
| return; | ||
| const lookupModelNames = [normalizedModelName]; | ||
| if (normalizedModelName.endsWith("-utility")) { | ||
| lookupModelNames.push(normalizedModelName.slice(0, -"-utility".length)); |
There was a problem hiding this comment.
L476: shrink: repeated linear scan via Object.entries(models).find(...) inside loop. Build one lowercase-key map once, then direct lookup for exact and -utility base.
There was a problem hiding this comment.
Addressed in 2dc44d0: the shared catalog lookup now builds a lowercase-key map once and checks exact IDs before the utility base.
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The focused implementation matches the reported failure and includes appropriate regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Adds fallback wire API inference for Copilot model IDs ending in -utility, resolving them through their cataloged base model.
Changes:
- Preserves explicit overrides and exact-match precedence.
- Adds regression coverage for utility-model inference.
| File | Description |
|---|---|
actions/setup/js/copilot_harness.cjs |
Falls back from utility variants to base catalog models. |
actions/setup/js/copilot_harness.test.cjs |
Tests responses API inference for a utility variant. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Comment MemoryNote This comment is managed by comment memory.It stores persistent context for this thread in the code block at the top of this comment.
|
There was a problem hiding this comment.
Request changes
This patch fixes the direct env auto-configure path, but the reflect-driven SDK path still does an exact models.json lookup and can keep sending *-utility Copilot models to the wrong API.
Blocking theme
The new fallback lives only in applyCopilotWireAPI(). When the harness builds SDK multi-provider config from AWF reflect data, it still uses getCatalogModelEntry() / inferWireApiForModel() with the unresolved utility model name. That means the exact same gpt-5.6-luna-utility case can still miss the catalog and fall back to completions in SDK mode. This needs to be normalized in the shared catalog lookup path and covered by a reflect-path regression.
🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 31.8 AIC · ⌖ 7.23 AIC · ⊞ 20.2K
Comment /review to run again
| } | ||
| return; | ||
| const lookupModelNames = [normalizedModelName]; | ||
| if (normalizedModelName.endsWith("-utility")) { |
There was a problem hiding this comment.
This only fixes the env-based lookup path; SDK/reflect mode still misses *-utility models and will keep selecting the wrong wire API.
💡 Why this still breaks and how to fix it
applyCopilotWireAPI() now falls back from gpt-5.6-luna-utility to gpt-5.6-luna, but the SDK path still resolves its provider config through resolveMultiProviderFromReflect() → getCatalogModelEntry() / inferWireApiForModel(), and those helpers still require an exact catalog key. In that flow the utility model still misses models.json, inferWireApiForModel() falls back to completions, and the original wrong-endpoint failure survives whenever reflect-driven multi-provider resolution is active.
Please move the -utility normalization into the shared catalog lookup (or a shared helper used by both call sites) and add a regression that exercises the reflect/multi-provider path, not just applyCopilotWireAPI().
There was a problem hiding this comment.
Addressed in 2dc44d0: getCatalogModelEntry() now resolves utility variants through the base model, and the reflect multi-provider path has a regression test.
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /tdd — the fix logic itself is sound (correctly falls through the lookup list and returns early on the first hit, whether exact or fallback), but the new test only covers the happy path.
📋 Key Themes & Highlights
Key Themes
- Precedence untested: the PR description states "Exact matches ... retain precedence" over the
-utilityfallback, but no test proves this —lookupModelNames = [exact, base]combined withreturnon first match does enforce it today, but nothing guards against a future reordering regression. - Boundary case untested: when both the exact utility key and its base are absent from the catalog, the loop should exit silently; there's no test locking that in.
Positive Highlights
- ✅ Minimal, well-scoped diff — reuses the existing
Object.entries(...).find(...)case-insensitive lookup pattern instead of introducing a new one. - ✅ Correctly preserves the "return after first match" semantics from the original code, avoiding double-processing.
- ✅ Query-parameter stripping (
?effort=high) still applies before the-utilitysuffix check, sogpt-5.6-luna-utility?effort=highwould also resolve correctly.
Suggested tests are in the inline comments. Neither blocks merge — the implementation is correct as written.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 36.5 AIC · ⌖ 14.5 AIC · ⊞ 10K
Comment /matt to run again
| it("uses the catalog wire API for a Copilot utility model variant", () => { | ||
| process.env.COPILOT_MODEL = "gpt-5.6-luna-utility"; | ||
| applyCopilotWireAPI({ modelsJson: makeModelsJson(), logger: () => {} }); | ||
| expect(process.env.COPILOT_PROVIDER_WIRE_API).toBe("responses"); |
There was a problem hiding this comment.
[/tdd] The PR description explicitly promises "Exact matches ... retain precedence" over the utility-suffix fallback, but no test asserts this. Add a regression test proving the fix's stated contract, not just the happy path.
💡 Suggested test
it("prefers an exact catalog match over the utility-suffix fallback", () => {
process.env.COPILOT_MODEL = "gpt-5.6-luna-utility";
const modelsJson = makeModelsJson();
modelsJson.providers["github-copilot"].models["gpt-5.6-luna-utility"] = { wire_api: "completions" };
applyCopilotWireAPI({ modelsJson, logger: () => {} });
expect(process.env.COPILOT_PROVIDER_WIRE_API).toBe("completions");
});Without this test, a future refactor of lookupModelNames order could silently reverse precedence and route utility-model traffic to the wrong wire API — exactly the class of bug this PR fixes.
@copilot please address this.
There was a problem hiding this comment.
Added an exact-match-over-base regression for applyCopilotWireAPI; the shared lookup preserves this precedence (2dc44d0).
| it("uses the catalog wire API for a Copilot utility model variant", () => { | ||
| process.env.COPILOT_MODEL = "gpt-5.6-luna-utility"; | ||
| applyCopilotWireAPI({ modelsJson: makeModelsJson(), logger: () => {} }); | ||
| expect(process.env.COPILOT_PROVIDER_WIRE_API).toBe("responses"); |
There was a problem hiding this comment.
[/tdd] No test covers the case where a -utility model's base is also absent from the catalog — the fallback loop should still leave COPILOT_PROVIDER_WIRE_API unset rather than throwing or matching something unintended.
💡 Suggested test
it("leaves COPILOT_PROVIDER_WIRE_API unset when a utility model's base is also unknown", () => {
process.env.COPILOT_MODEL = "totally-unknown-model-utility";
applyCopilotWireAPI({ modelsJson: makeModelsJson(), logger: () => {} });
expect(process.env.COPILOT_PROVIDER_WIRE_API).toBeUndefined();
});This boundary case exercises the full lookupModelNames loop (both entries miss) and guards against a future change accidentally matching on a partial/incorrect key.
@copilot please address this.
There was a problem hiding this comment.
Added a regression confirming an unknown utility model with an unknown base leaves the wire API unset (2dc44d0).
There was a problem hiding this comment.
Reviewed with Impeccable audit mode (small, well-scoped bug_fix diff — no UI surface). Confirmed the utility-suffix fallback logic manually:
gpt-5.6-luna-utility→ strips-utility→ matchesgpt-5.6-luna→ correctly setsCOPILOT_PROVIDER_WIRE_API=responses.- Exact catalog matches still take precedence over the fallback (the fallback list is only appended, not prioritized).
?queryparams are stripped before the suffix check, sogpt-5.6-luna-utility?effort=highresolves correctly too.- No match for either the exact or base name silently falls through (no throw), consistent with prior behavior.
- New regression test covers the fallback path directly.
No blocking issues found.
🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 33.4 AIC · ⌖ 13 AIC · ⊞ 8.1K
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added a |
|
@copilot Please address the remaining requested review feedback on the utility-model wire API fallback, refresh the branch if needed, and run the
|
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
The shared catalog lookup now handles utility IDs in the reflect-driven SDK path, with regression coverage for that resolver. Committed as |

Copilot auto billing can resolve to utility-suffixed model IDs that aren’t present in the model catalog, causing requests to use the wrong endpoint. For example,
gpt-5.6-luna-utilitymust use theresponsesAPI configured forgpt-5.6-luna.-utility. Exact matches and explicitCOPILOT_PROVIDER_WIRE_APIoverrides retain precedence.Run: https://github.com/github/gh-aw/actions/runs/36528584087