From 8d147d75dcb4156aa01a9d9cee20fed17c6b232a Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sun, 21 Jun 2026 08:02:12 -0300 Subject: [PATCH] feat(providers): expose gpt-4o on built-in GitHub Copilot (gh) provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Copilot's chat/completions endpoint still serves the original `gpt-4o` model, but the OmniRoute registry only shipped the GPT-5.x family under `github` (alias `gh`), so apps that hard-code `gpt-4o` resolved to "unknown model" against this provider. Add `gpt-4o` (chat/completions, 128k context — no `openai-responses` targetFormat, so it routes through the provider default just like the Claude/Gemini Copilot entries hardened by #2911) to the `github` registry. The GPT-5.x lineup, Claude/Gemini entries and oswe Raptor Mini are untouched. TDD: tests/unit/provider-registry-github-copilot-gpt-4o.test.ts verifies the entry exists, does NOT carry `openai-responses`, and is exposed through `getModelsByProviderId("github")`. The sibling #2911 regression suite still passes. Ported from decolua/9router#98 (single-line addition to PROVIDER_MODELS). Inspired-by: decolua/9router#98 Co-authored-by: Bexultan --- CHANGELOG.md | 1 + .../config/providers/registry/github/index.ts | 3 ++ ...der-registry-github-copilot-gpt-4o.test.ts | 45 +++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/unit/provider-registry-github-copilot-gpt-4o.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 92b0c281c4f1..d5d58527f1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ _In development — bullets added per PR; finalized at release._ - **feat(cluster): opt-in memory + Bifrost cluster profiles** — adds opt-in cluster profiles that wire the memory subsystem and the Bifrost Go sidecar into a clustered deployment (follow-up to #3932). ([#4433](https://github.com/diegosouzapw/OmniRoute/pull/4433) — thanks @KooshaPari) - **feat(models): opt-in low-noise `/v1/models` catalog mode** — a new opt-in mode trims the `/v1/models` response to a quieter, lower-noise catalog for clients that choke on or don't need the full provider/model list. ([#4427](https://github.com/diegosouzapw/OmniRoute/pull/4427) — thanks @Rahulsharma0810) - **feat(ui): expose a `targetFormat` selector in the custom-models form** — the custom-models form now lets you pick the upstream target format explicitly, so a custom model can be pinned to the right wire format instead of relying on inference. ([#4475](https://github.com/diegosouzapw/OmniRoute/pull/4475) — thanks @adivekar-utexas) +- **feat(providers): expose `gpt-4o` on the built-in GitHub Copilot (`gh`) provider** — GitHub Copilot still serves the original `gpt-4o` chat model via its `/chat/completions` endpoint, but the OmniRoute registry only shipped the GPT-5.x family, so clients that explicitly request `gpt-4o` against `gh` got an unknown-model error. `gpt-4o` is now registered under the `github` provider next to the GPT-5.x lineup (chat/completions, 128k context — no `openai-responses` targetFormat). Ported from [9router#98](https://github.com/decolua/9router/pull/98). (thanks @I3eka) ### 🐛 Fixed diff --git a/open-sse/config/providers/registry/github/index.ts b/open-sse/config/providers/registry/github/index.ts index 6eb80bbf4093..d5ab6458c8b6 100644 --- a/open-sse/config/providers/registry/github/index.ts +++ b/open-sse/config/providers/registry/github/index.ts @@ -25,6 +25,9 @@ export const githubProvider: RegistryEntry = { defaultContextLength: 128000, headers: getGitHubCopilotChatHeaders(), models: [ + // 9router#98 — Copilot still serves GPT-4o via chat/completions; keep it + // alongside the GPT-5.x family so apps that hard-code `gpt-4o` resolve here. + { id: "gpt-4o", name: "GPT-4o", contextLength: 128000 }, { id: "gpt-5-mini", name: "GPT-5 Mini", targetFormat: "openai-responses" }, { id: "gpt-5.3-codex", name: "GPT-5.3 Codex", targetFormat: "openai-responses" }, { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", targetFormat: "openai-responses" }, diff --git a/tests/unit/provider-registry-github-copilot-gpt-4o.test.ts b/tests/unit/provider-registry-github-copilot-gpt-4o.test.ts new file mode 100644 index 000000000000..d7dd8f0ce958 --- /dev/null +++ b/tests/unit/provider-registry-github-copilot-gpt-4o.test.ts @@ -0,0 +1,45 @@ +/** + * Port of 9router PR #98 — add GPT-4o to GitHub Copilot (`github`/alias `gh`). + * + * Copilot still serves the original `gpt-4o` chat model via its chat/completions + * endpoint. The OmniRoute registry only ships the GPT-5.x family, so apps that + * explicitly request `gpt-4o` against the `gh` alias get an "unknown model" + * error. Adding the entry restores parity with the upstream Copilot catalog + * without disturbing the GPT-5.x / Claude / Gemini lineups already curated. + * + * GPT-4o is a chat/completions model — it must NOT use `openai-responses`. + */ +import test from "node:test"; +import assert from "node:assert/strict"; + +const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts"); +const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts"); + +type ModelEntry = { id: string; name?: string; targetFormat?: string; [k: string]: unknown }; + +function githubModel(id: string): ModelEntry | undefined { + const provider = (REGISTRY as Record)["github"]; + return provider?.models?.find((m) => m.id === id); +} + +test("9router#98 github/gpt-4o is registered under the gh provider", () => { + const model = githubModel("gpt-4o"); + assert.ok(model, "gpt-4o must be registered under the github (gh) provider"); + assert.equal(typeof model?.name, "string"); +}); + +test("9router#98 github/gpt-4o routes via chat/completions (no openai-responses)", () => { + const model = githubModel("gpt-4o"); + assert.ok(model); + assert.notEqual( + model.targetFormat, + "openai-responses", + "GPT-4o on GitHub Copilot is a chat/completions model — Responses API would reject it" + ); +}); + +test("9router#98 getModelsByProviderId(github) exposes gpt-4o", () => { + const models = getModelsByProviderId("github") as ModelEntry[]; + const gpt4o = models.find((m) => m.id === "gpt-4o"); + assert.ok(gpt4o, "gpt-4o resolvable via getModelsByProviderId(github)"); +});