From c1b7856e26bcabf5457e2d43e4958adc22683df4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 16 Jul 2026 20:07:36 +0200 Subject: [PATCH] fix(ai): prevent removed xAI models regenerating, closes #6736 --- packages/ai/CHANGELOG.md | 1 + packages/ai/README.md | 2 +- packages/ai/scripts/generate-models.ts | 58 ++---------- .../ai/src/providers/openrouter.models.ts | 6 +- packages/ai/src/providers/xai.models.ts | 90 ------------------- packages/ai/test/context-overflow.test.ts | 2 +- .../ai/test/cross-provider-handoff.test.ts | 2 +- packages/ai/test/stream.test.ts | 2 +- packages/ai/test/total-tokens.test.ts | 22 ++--- packages/ai/test/xai-responses.test.ts | 12 +++ packages/coding-agent/CHANGELOG.md | 4 + 11 files changed, 41 insertions(+), 160 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 88361100..3c253586 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - Fixed Kimi Coding K3 thinking-level metadata to expose only the supported `max` level ([#6737](https://github.com/earendil-works/pi/issues/6737)). +- Fixed catalog generation restoring xAI models removed in 0.80.9 ([#6736](https://github.com/earendil-works/pi/issues/6736)). ## [0.80.9] - 2026-07-16 diff --git a/packages/ai/README.md b/packages/ai/README.md index e1e03582..9ad15373 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -747,7 +747,7 @@ Many models support thinking/reasoning capabilities where they can show their in const model = models.getModel('anthropic', 'claude-sonnet-4-5')!; // or models.getModel('openai', 'gpt-5-mini'); // or models.getModel('google', 'gemini-2.5-flash'); -// or models.getModel('xai', 'grok-code-fast-1'); +// or models.getModel('xai', 'grok-4.5'); // Check if model supports reasoning if (model.reasoning) { diff --git a/packages/ai/scripts/generate-models.ts b/packages/ai/scripts/generate-models.ts index 29a16190..d874a460 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -302,6 +302,13 @@ const OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([ "gpt-5.6-luna", ]); const XAI_RESPONSES_MODEL_ID = "grok-4.5"; +const XAI_BUILTIN_EXCLUDED_MODEL_IDS = new Set([ + "grok-3", + "grok-3-fast", + "grok-4.20-0309-non-reasoning", + "grok-4.20-0309-reasoning", + "grok-code-fast-1", +]); const XAI_RESPONSES_EFFORT_LEVEL_MAP = { off: null, minimal: null, @@ -1783,6 +1790,7 @@ async function generateModels() { // Combine models (models.dev has priority) const allModels = [...modelsDevModels, ...openRouterModels, ...aiGatewayModels].filter( (model) => + !(model.provider === "xai" && XAI_BUILTIN_EXCLUDED_MODEL_IDS.has(model.id)) && !((model.provider === "opencode" || model.provider === "opencode-go") && model.id === "gpt-5.3-codex-spark"), ); @@ -2142,56 +2150,6 @@ async function generateModels() { ]; allModels.push(...codexModels); - // Add missing Grok models - const missingGrokModels: Model<"openai-completions">[] = [ - { - id: "grok-3", - name: "Grok 3", - api: "openai-completions", - baseUrl: "https://api.x.ai/v1", - provider: "xai", - reasoning: false, - input: ["text"], - cost: { input: 3, output: 15, cacheRead: 0.75, cacheWrite: 0 }, - contextWindow: 131072, - maxTokens: 8192, - }, - { - id: "grok-3-fast", - name: "Grok 3 Fast", - api: "openai-completions", - baseUrl: "https://api.x.ai/v1", - provider: "xai", - reasoning: false, - input: ["text"], - cost: { input: 5, output: 25, cacheRead: 1.25, cacheWrite: 0 }, - contextWindow: 131072, - maxTokens: 8192, - }, - { - id: "grok-code-fast-1", - name: "Grok Code Fast 1", - api: "openai-completions", - baseUrl: "https://api.x.ai/v1", - provider: "xai", - reasoning: false, - input: ["text"], - cost: { - input: 0.2, - output: 1.5, - cacheRead: 0.02, - cacheWrite: 0, - }, - contextWindow: 32768, - maxTokens: 8192, - }, - ]; - for (const model of missingGrokModels) { - if (!allModels.some(m => m.provider === model.provider && m.id === model.id)) { - allModels.push(model); - } - } - // Add missing Mistral Medium 3.5 model until models.dev includes it if (!allModels.some(m => m.provider === "mistral" && m.id === "mistral-medium-3.5")) { allModels.push({ diff --git a/packages/ai/src/providers/openrouter.models.ts b/packages/ai/src/providers/openrouter.models.ts index e9c590ea..1767361f 100644 --- a/packages/ai/src/providers/openrouter.models.ts +++ b/packages/ai/src/providers/openrouter.models.ts @@ -4706,9 +4706,9 @@ export const OPENROUTER_MODELS = { thinkingLevelMap: {"xhigh":"xhigh"}, input: ["text"], cost: { - input: 0.9212, - output: 2.8952, - cacheRead: 0.17108, + input: 0.9198, + output: 2.8908, + cacheRead: 0.17082, cacheWrite: 0, }, contextWindow: 1048576, diff --git a/packages/ai/src/providers/xai.models.ts b/packages/ai/src/providers/xai.models.ts index 2c781afe..29be7785 100644 --- a/packages/ai/src/providers/xai.models.ts +++ b/packages/ai/src/providers/xai.models.ts @@ -4,78 +4,6 @@ import type { Model } from "../types.ts"; export const XAI_MODELS = { - "grok-3": { - id: "grok-3", - name: "Grok 3", - api: "openai-completions", - provider: "xai", - baseUrl: "https://api.x.ai/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false}, - reasoning: false, - input: ["text"], - cost: { - input: 3, - output: 15, - cacheRead: 0.75, - cacheWrite: 0, - }, - contextWindow: 131072, - maxTokens: 8192, - } satisfies Model<"openai-completions">, - "grok-3-fast": { - id: "grok-3-fast", - name: "Grok 3 Fast", - api: "openai-completions", - provider: "xai", - baseUrl: "https://api.x.ai/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false}, - reasoning: false, - input: ["text"], - cost: { - input: 5, - output: 25, - cacheRead: 1.25, - cacheWrite: 0, - }, - contextWindow: 131072, - maxTokens: 8192, - } satisfies Model<"openai-completions">, - "grok-4.20-0309-non-reasoning": { - id: "grok-4.20-0309-non-reasoning", - name: "Grok 4.20 (Non-Reasoning)", - api: "openai-completions", - provider: "xai", - baseUrl: "https://api.x.ai/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false}, - reasoning: false, - input: ["text", "image"], - cost: { - input: 1.25, - output: 2.5, - cacheRead: 0.2, - cacheWrite: 0, - }, - contextWindow: 1000000, - maxTokens: 30000, - } satisfies Model<"openai-completions">, - "grok-4.20-0309-reasoning": { - id: "grok-4.20-0309-reasoning", - name: "Grok 4.20 (Reasoning)", - api: "openai-completions", - provider: "xai", - baseUrl: "https://api.x.ai/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false}, - reasoning: true, - input: ["text", "image"], - cost: { - input: 1.25, - output: 2.5, - cacheRead: 0.2, - cacheWrite: 0, - }, - contextWindow: 1000000, - maxTokens: 30000, - } satisfies Model<"openai-completions">, "grok-4.3": { id: "grok-4.3", name: "Grok 4.3", @@ -131,22 +59,4 @@ export const XAI_MODELS = { contextWindow: 256000, maxTokens: 256000, } satisfies Model<"openai-completions">, - "grok-code-fast-1": { - id: "grok-code-fast-1", - name: "Grok Code Fast 1", - api: "openai-completions", - provider: "xai", - baseUrl: "https://api.x.ai/v1", - compat: {"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false}, - reasoning: false, - input: ["text"], - cost: { - input: 0.2, - output: 1.5, - cacheRead: 0.02, - cacheWrite: 0, - }, - contextWindow: 32768, - maxTokens: 8192, - } satisfies Model<"openai-completions">, } as const; diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index b4b93b21..582157a0 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -262,7 +262,7 @@ describe("Context overflow error handling", () => { // ============================================================================= describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => { - it("grok-3-fast - should detect overflow via isContextOverflow", async () => { + it("grok-4.3 - should detect overflow via isContextOverflow", async () => { const model = getModel("xai", "grok-4.3"); const result = await testContextOverflow(model, process.env.XAI_API_KEY!); logResult(result); diff --git a/packages/ai/test/cross-provider-handoff.test.ts b/packages/ai/test/cross-provider-handoff.test.ts index 57a43b0a..8f93e631 100644 --- a/packages/ai/test/cross-provider-handoff.test.ts +++ b/packages/ai/test/cross-provider-handoff.test.ts @@ -79,7 +79,7 @@ const PROVIDER_MODEL_PAIRS: ProviderModelPair[] = [ label: "bedrock-claude-sonnet-4-5", }, // xAI - { provider: "xai", model: "grok-code-fast-1", label: "xai-grok-code-fast-1" }, + { provider: "xai", model: "grok-4.3", label: "xai-grok-4.3" }, // Cerebras { provider: "cerebras", model: "zai-glm-4.7", label: "cerebras-zai-glm-4.7" }, // Cloudflare Workers AI diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index eb5a30dd..3b0a2ce2 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -542,7 +542,7 @@ describe("Generate E2E Tests", () => { }); }); - describe.skipIf(!process.env.XAI_API_KEY)("xAI Provider (grok-code-fast-1 via OpenAI Completions)", () => { + describe.skipIf(!process.env.XAI_API_KEY)("xAI Provider (grok-4.3 via OpenAI Completions)", () => { const llm = getModel("xai", "grok-4.3"); it("should complete basic text generation", { retry: 3 }, async () => { diff --git a/packages/ai/test/total-tokens.test.ts b/packages/ai/test/total-tokens.test.ts index 47f2cd18..477f0828 100644 --- a/packages/ai/test/total-tokens.test.ts +++ b/packages/ai/test/total-tokens.test.ts @@ -244,22 +244,18 @@ describe("totalTokens field", () => { // ========================================================================= describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => { - it( - "grok-3-fast - should return totalTokens equal to sum of components", - { retry: 3, timeout: 60000 }, - async () => { - const llm = getModel("xai", "grok-4.3"); + it("grok-4.3 - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000 }, async () => { + const llm = getModel("xai", "grok-4.3"); - console.log(`\nxAI / ${llm.id}:`); - const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XAI_API_KEY }); + console.log(`\nxAI / ${llm.id}:`); + const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XAI_API_KEY }); - logUsage("First request", first); - logUsage("Second request", second); + logUsage("First request", first); + logUsage("Second request", second); - assertTotalTokensEqualsComponents(first); - assertTotalTokensEqualsComponents(second); - }, - ); + assertTotalTokensEqualsComponents(first); + assertTotalTokensEqualsComponents(second); + }); }); // ========================================================================= diff --git a/packages/ai/test/xai-responses.test.ts b/packages/ai/test/xai-responses.test.ts index 303c62c5..92e46420 100644 --- a/packages/ai/test/xai-responses.test.ts +++ b/packages/ai/test/xai-responses.test.ts @@ -60,6 +60,18 @@ describe("xAI Responses provider", () => { vi.restoreAllMocks(); }); + it("excludes retired and redundant models from the built-in catalog", () => { + for (const modelId of [ + "grok-3", + "grok-3-fast", + "grok-4.20-0309-non-reasoning", + "grok-4.20-0309-reasoning", + "grok-code-fast-1", + ]) { + expect(Object.keys(XAI_MODELS)).not.toContain(modelId); + } + }); + it("uses Responses with low/medium/high efforts only for Grok 4.5", () => { expect(XAI_MODELS["grok-4.5"].api).toBe("openai-responses"); expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.5"])).toEqual(["low", "medium", "high"]); diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 4a98a022..1eb63a85 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed inherited catalog generation restoring xAI models removed in 0.80.9 ([#6736](https://github.com/earendil-works/pi/issues/6736)). + ## [0.80.9] - 2026-07-16 ### New Features