fix: validate copilot model token limits · openclaw/openclaw@ac05545
steipete
·
2026-05-29
·
via Recent Commits to openclaw:main
File tree
extensions/github-copilot
| Original file line number | Diff line number | Diff line change |
|---|
@@ -611,6 +611,59 @@ describe("fetchCopilotModelCatalog", () => {
|
611 | 611 | expect(out[0].name).toBe("GPT-5.5"); |
612 | 612 | }); |
613 | 613 | |
| 614 | +it("falls back from malformed live token limits", async () => { |
| 615 | +const fetchImpl = vi.fn().mockResolvedValue({ |
| 616 | +ok: true, |
| 617 | +status: 200, |
| 618 | +json: async () => ({ |
| 619 | +data: [ |
| 620 | +{ |
| 621 | +id: "gpt-bad-window", |
| 622 | +name: "GPT Bad Window", |
| 623 | +object: "model", |
| 624 | +capabilities: { |
| 625 | +type: "chat", |
| 626 | +limits: { |
| 627 | +max_context_window_tokens: -1, |
| 628 | +max_output_tokens: 128000.5, |
| 629 | +}, |
| 630 | +}, |
| 631 | +}, |
| 632 | +{ |
| 633 | +id: "gpt-bad-output", |
| 634 | +name: "GPT Bad Output", |
| 635 | +object: "model", |
| 636 | +capabilities: { |
| 637 | +type: "chat", |
| 638 | +limits: { |
| 639 | +max_context_window_tokens: Number.POSITIVE_INFINITY, |
| 640 | +max_output_tokens: 0, |
| 641 | +}, |
| 642 | +}, |
| 643 | +}, |
| 644 | +], |
| 645 | +}), |
| 646 | +}); |
| 647 | + |
| 648 | +const out = await fetchCopilotModelCatalog({ |
| 649 | +copilotApiToken: "tid=test", |
| 650 | +baseUrl: "https://api.githubcopilot.com", |
| 651 | +fetchImpl: fetchImpl as unknown as typeof fetch, |
| 652 | +}); |
| 653 | + |
| 654 | +expect(out).toHaveLength(2); |
| 655 | +expect(out[0]).toMatchObject({ |
| 656 | +id: "gpt-bad-window", |
| 657 | +contextWindow: 128000, |
| 658 | +maxTokens: 8192, |
| 659 | +}); |
| 660 | +expect(out[1]).toMatchObject({ |
| 661 | +id: "gpt-bad-output", |
| 662 | +contextWindow: 128000, |
| 663 | +maxTokens: 8192, |
| 664 | +}); |
| 665 | +}); |
| 666 | + |
614 | 667 | it("throws on non-2xx HTTP responses so the caller can fall back to the static catalog", async () => { |
615 | 668 | const fetchImpl = vi.fn().mockResolvedValue({ |
616 | 669 | ok: false, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -6,7 +6,10 @@ import { buildCopilotIdeHeaders, COPILOT_INTEGRATION_ID } from "openclaw/plugin-
|
6 | 6 | import { readProviderJsonArrayFieldResponse } from "openclaw/plugin-sdk/provider-http"; |
7 | 7 | import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared"; |
8 | 8 | import { normalizeModelCompat } from "openclaw/plugin-sdk/provider-model-shared"; |
9 | | -import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime"; |
| 9 | +import { |
| 10 | +asPositiveSafeInteger, |
| 11 | +normalizeOptionalLowercaseString, |
| 12 | +} from "openclaw/plugin-sdk/string-coerce-runtime"; |
10 | 13 | import { |
11 | 14 | resolveCopilotModelCompat, |
12 | 15 | resolveCopilotTransportApi, |
@@ -169,13 +172,8 @@ function mapCopilotApiModelToDefinition(
|
169 | 172 | const input: ModelDefinitionConfig["input"] = supportsVision ? ["text", "image"] : ["text"]; |
170 | 173 | |
171 | 174 | const contextWindow = |
172 | | -typeof limits?.max_context_window_tokens === "number" && limits.max_context_window_tokens > 0 |
173 | | - ? limits.max_context_window_tokens |
174 | | - : DEFAULT_CONTEXT_WINDOW; |
175 | | -const maxTokens = |
176 | | -typeof limits?.max_output_tokens === "number" && limits.max_output_tokens > 0 |
177 | | - ? limits.max_output_tokens |
178 | | - : DEFAULT_MAX_TOKENS; |
| 175 | +asPositiveSafeInteger(limits?.max_context_window_tokens) ?? DEFAULT_CONTEXT_WINDOW; |
| 176 | +const maxTokens = asPositiveSafeInteger(limits?.max_output_tokens) ?? DEFAULT_MAX_TOKENS; |
179 | 177 | const compat = resolveCopilotModelCompat(id); |
180 | 178 | |
181 | 179 | const definition: ModelDefinitionConfig = { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。