From f0534c5b71db4cf2ce393c8d491c174d8453ea6a Mon Sep 17 00:00:00 2001 From: saju01 Date: Fri, 10 Jul 2026 10:57:14 +0530 Subject: [PATCH] fix(github-copilot): honor live prompt token limits (#103275) * fix(github-copilot): honor live prompt token limits * fix(github-copilot): align prompt token budgets * fix(github-copilot): align prompt token budgets --------- Co-authored-by: saju01 Co-authored-by: Peter Steinberger Co-authored-by: Peter Steinberger --- extensions/github-copilot/index.ts | 4 ++-- extensions/github-copilot/model-metadata.ts | 1 + extensions/github-copilot/models.test.ts | 7 +++++++ extensions/github-copilot/models.ts | 5 +++++ extensions/github-copilot/openclaw.plugin.json | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/extensions/github-copilot/index.ts b/extensions/github-copilot/index.ts index 733b4d546a6b..4245a2c92f04 100644 --- a/extensions/github-copilot/index.ts +++ b/extensions/github-copilot/index.ts @@ -375,8 +375,8 @@ export default definePluginEntry({ } } // Try to fetch the live model catalog from Copilot's /models endpoint so - // the runtime tracks per-account entitlements and accurate context - // windows (max_context_window_tokens) without manifest churn. On any + // the runtime tracks per-account entitlements and accurate token limits + // without manifest churn. On any // failure we return an empty model list, which lets the static manifest // catalog continue to be the visible fallback for users. let discoveredModels: Awaited> = []; diff --git a/extensions/github-copilot/model-metadata.ts b/extensions/github-copilot/model-metadata.ts index 277a0c914ff7..81840be77d97 100644 --- a/extensions/github-copilot/model-metadata.ts +++ b/extensions/github-copilot/model-metadata.ts @@ -47,6 +47,7 @@ const STATIC_MODEL_OVERRIDES = new Map>([ name: "GPT-5.5", reasoning: true, contextWindow: 400_000, + contextTokens: 272_000, maxTokens: 128_000, }, ], diff --git a/extensions/github-copilot/models.test.ts b/extensions/github-copilot/models.test.ts index 2915cbf1b2c9..e26062ad07a8 100644 --- a/extensions/github-copilot/models.test.ts +++ b/extensions/github-copilot/models.test.ts @@ -126,6 +126,7 @@ describe("resolveCopilotForwardCompatModel", () => { input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 400_000, + contextTokens: 272_000, maxTokens: 128_000, }); }); @@ -570,6 +571,7 @@ describe("fetchCopilotModelCatalog", () => { input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 400000, + contextTokens: 272000, maxTokens: 128000, compat: { supportedReasoningEfforts: ["low", "medium", "high"] }, }); @@ -578,6 +580,7 @@ describe("fetchCopilotModelCatalog", () => { expect(codex?.input).toEqual(["text"]); expect(codex?.reasoning).toBe(true); expect(codex?.contextWindow).toBe(400000); + expect(codex?.contextTokens).toBeUndefined(); const gemini = out.find((m) => m.id === "gemini-3.1-pro-preview"); expect(gemini?.api).toBe("openai-completions"); @@ -664,6 +667,7 @@ describe("fetchCopilotModelCatalog", () => { type: "chat", limits: { max_context_window_tokens: -1, + max_prompt_tokens: -1, max_output_tokens: 128000.5, }, }, @@ -676,6 +680,7 @@ describe("fetchCopilotModelCatalog", () => { type: "chat", limits: { max_context_window_tokens: Number.POSITIVE_INFINITY, + max_prompt_tokens: Number.POSITIVE_INFINITY, max_output_tokens: 0, }, }, @@ -696,11 +701,13 @@ describe("fetchCopilotModelCatalog", () => { contextWindow: 128000, maxTokens: 8192, }); + expect(out[0]).not.toHaveProperty("contextTokens"); expect(out[1]).toMatchObject({ id: "gpt-bad-output", contextWindow: 128000, maxTokens: 8192, }); + expect(out[1]).not.toHaveProperty("contextTokens"); }); it("throws on non-2xx HTTP responses so the caller can fall back to the static catalog", async () => { diff --git a/extensions/github-copilot/models.ts b/extensions/github-copilot/models.ts index aa110dc710b9..348076a907f1 100644 --- a/extensions/github-copilot/models.ts +++ b/extensions/github-copilot/models.ts @@ -80,6 +80,9 @@ export function resolveCopilotForwardCompatModel( input: staticOverride.input ?? ["text", "image"], cost: staticOverride.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: staticOverride.contextWindow ?? DEFAULT_CONTEXT_WINDOW, + ...(staticOverride.contextTokens !== undefined + ? { contextTokens: staticOverride.contextTokens } + : {}), maxTokens: staticOverride.maxTokens ?? DEFAULT_MAX_TOKENS, ...(staticOverride.thinkingLevelMap ? { thinkingLevelMap: staticOverride.thinkingLevelMap } @@ -218,6 +221,7 @@ function mapCopilotApiModelToDefinition( const contextWindow = asPositiveSafeInteger(limits?.max_context_window_tokens) ?? DEFAULT_CONTEXT_WINDOW; + const contextTokens = asPositiveSafeInteger(limits?.max_prompt_tokens); const maxTokens = asPositiveSafeInteger(limits?.max_output_tokens) ?? DEFAULT_MAX_TOKENS; const compat = mergeCopilotCompat(resolveCopilotModelCompat(id), supports?.reasoning_effort); const api = resolveCopilotApiForVendor(entry.vendor, id); @@ -231,6 +235,7 @@ function mapCopilotApiModelToDefinition( input, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow, + ...(contextTokens !== undefined ? { contextTokens } : {}), maxTokens, ...(thinkingLevelMap ? { thinkingLevelMap } : {}), ...(compat ? { compat } : {}), diff --git a/extensions/github-copilot/openclaw.plugin.json b/extensions/github-copilot/openclaw.plugin.json index 61b10360d6f0..f18095a41eb4 100644 --- a/extensions/github-copilot/openclaw.plugin.json +++ b/extensions/github-copilot/openclaw.plugin.json @@ -108,6 +108,7 @@ "reasoning": true, "input": ["text", "image"], "contextWindow": 400000, + "contextTokens": 272000, "maxTokens": 128000, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } },