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
6 changes: 6 additions & 0 deletions src/codex/catalog/provider-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1195,9 +1195,15 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid
const metadata = plainRecord(item.metadata);
const capabilityRecord = plainRecord(metadata?.capabilities) ?? plainRecord(item.capabilities);
const limits = plainRecord(metadata?.limits);
const capabilityLimits = plainRecord(plainRecord(item.capabilities)?.limits);
const contextWindow =
positiveSafeInteger(
limits?.max_context_length,
// GitHub Copilot reports the live context window here instead of in the metadata or
// top-level fields used by other OpenAI-compatible catalogs (#3156). Keep the existing
// metadata field authoritative when both are present: adding this provider-specific
// fallback must not change previously recognized providers.
capabilityLimits?.max_context_window_tokens,
metadata?.context_length,
item.context_length,
item.context_size,
Expand Down
43 changes: 43 additions & 0 deletions tests/codex-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5366,6 +5366,49 @@ describe("Codex catalog routed normalization", () => {
expect(routed?.context_window).toBe(64_000);
});

test("GitHub Copilot capabilities preserve the live context window (#3156)", async () => {
globalThis.fetch = (async () => new Response(JSON.stringify({
data: [{
id: "copilot-wide-model",
capabilities: {
limits: { max_context_window_tokens: 1_000_000 },
},
}, {
id: "copilot-existing-metadata",
metadata: { limits: { max_context_length: 256_000 } },
capabilities: {
limits: { max_context_window_tokens: 1_000_000 },
},
}, {
id: "copilot-invalid-window",
capabilities: {
limits: { max_context_window_tokens: -1 },
},
}],
}))) as typeof fetch;

const models = await gatherRoutedModels({
port: 10100,
defaultProvider: "github-copilot",
providers: {
"github-copilot": {
adapter: "openai-chat",
baseUrl: "https://api.githubcopilot.com",
apiKey: "sk-test",
},
},
});
const routed = buildCatalogEntries(nativeTemplate(), [], models)
.find(entry => entry.slug === "github-copilot/copilot-wide-model");

expect(models.find(model => model.id === "copilot-wide-model")?.contextWindow).toBe(1_000_000);
expect(routed?.context_window).toBe(1_000_000);
expect(routed?.max_context_window).toBe(1_000_000);
expect(routed?.auto_compact_token_limit).toBe(900_000);
expect(models.find(model => model.id === "copilot-existing-metadata")?.contextWindow).toBe(256_000);
expect(models.find(model => model.id === "copilot-invalid-window")?.contextWindow).toBeUndefined();
});

test("liveModels false preserves configured catalog metadata without live fetch", async () => {
let fetchCalls = 0;
globalThis.fetch = (() => {
Expand Down
Loading