Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions open-sse/config/providers/registry/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
45 changes: 45 additions & 0 deletions tests/unit/provider-registry-github-copilot-gpt-4o.test.ts
Original file line number Diff line number Diff line change
@@ -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");
Comment on lines +15 to +16

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.

medium

Using static import statements is preferred over top-level dynamic await import(...) unless there is a specific reason for lazy loading or conditional importing. Static imports allow for better static analysis, type checking, and are more idiomatic in TypeScript.

Suggested change
const { REGISTRY } = await import("../../open-sse/config/providerRegistry.ts");
const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts");
import { REGISTRY } from "../../open-sse/config/providerRegistry.ts";
import { getModelsByProviderId } from "../../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<string, { models?: ModelEntry[] }>)["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)");
});
Loading