Repository navigation
fix(core): follow models.dev packages for Copilot fallback routing - #54210
Conversation
There was a problem hiding this comment.
The change looks sound. In the fallback, Anthropic-tagged models now stay on Messages with <account endpoint>/v1, and OpenAI-tagged ones go to Copilot Responses. That matches what CopilotModels.build does when /models loads, so the two paths agree, and untagged models behave as before.
I couldn't run it against Copilot (no credentials). The Copilot plugin tests pass (32/32), the 3 new ones fail with the base plugin code, and typecheck in packages/core passes.
Public API is unchanged; this only affects Copilot's internal model routing when GET /models fails.
One small test concern, noted inline: the account-endpoint test polls with no limit.
| }), | ||
| }) | ||
| yield* addPlugin() | ||
| // The plugin loads the model list in the background; poll on real time because the |
There was a problem hiding this comment.
This loop has no limit and runs against a real server, so if the routing breaks again the test hangs until the runner's timeout instead of failing with a clear message. Could you cap it (e.g. times/Schedule.recurs or an Effect.timeout), or wait on the plugin's reload directly if there's a hook for it?
There was a problem hiding this comment.
Capped in a83094c with times: 200 (~2s of 10ms real-time polls), so a regression falls through to the assertions instead of hanging. Effect.timeout/Schedule would use the suite's TestClock, which does not advance here, and the plugin has no reload hook to await. Checked by removing the /v1 base URL assignment: the test fails after ~2.3s with Expected "http://localhost:…/v1", Received "https://api.githubcopilot.com/v1".
There was a problem hiding this comment.
That fixes it. In a83094c, Effect.repeat({ until, times: 200 }) at provider-github-copilot.test.ts:446-450 caps the loop, and the wait comes from the real Bun.sleep(10) instead of a schedule. So TestClock isn't involved, and the test can't hang. When the cap is hit, the last model value goes on to the assertions on lines 451-452, so a regression still fails with the baseURL diff, as you saw. I agree with your reasons for not using Effect.timeout or Schedule here.
I ran bun test test/plugin/provider-github-copilot.test.ts at pr/head in a temporary worktree: 32 passed, 0 failed. I didn't repeat your check with the /v1 assignment removed. I removed the worktree afterwards and didn't change the shared checkout.
a83094c to
ca57d9c
Compare
When Copilot's
GET /modelsfails (e.g. a proxy blocks it, as reported in #51466), the plugin rewrote every models.dev model to@ai-sdk/github-copilot, so Claude went to/chat/completionsinstead of Anthropic Messages.The fallback now follows the models.dev package (anomalyco/models.dev#9277 adds the per-model packages):
@ai-sdk/anthropic→ stays on native Anthropic Messages; the base URL becomes<account Copilot endpoint>/v1when known. Catalog variants were already resolved for Messages.@ai-sdk/openai→ bundled Copilot SDK withendpoint: "responses".Until the models.dev change ships, untagged models behave exactly as before. The loaded
/modelspath is unchanged.Verification
bun test test/plugin/provider-github-copilot.test.tsandbun typecheckpass inpackages/core.opencode run --standaloneagainst a local proxy that returns 503 forGET /modelsand forwards everything else to Copilot, with models.dev patched as in [FEATURE]: support for .ts agent files #9277:claude-haiku-5.5#highPOST /chat/completionsPOST /v1/messages(200, tool calls)gpt-5.4-miniPOST /responses(200)gemini-3.8-flashPOST /chat/completions(200)