fix(ai): prevent removed xAI models regenerating, closes #6736

This commit is contained in:
Armin Ronacher
2026-07-16 20:07:36 +02:00
parent 78ff249436
commit c1b7856e26
11 changed files with 41 additions and 160 deletions
+1
View File
@@ -5,6 +5,7 @@
### Fixed ### 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 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 ## [0.80.9] - 2026-07-16
+1 -1
View File
@@ -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')!; const model = models.getModel('anthropic', 'claude-sonnet-4-5')!;
// or models.getModel('openai', 'gpt-5-mini'); // or models.getModel('openai', 'gpt-5-mini');
// or models.getModel('google', 'gemini-2.5-flash'); // 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 // Check if model supports reasoning
if (model.reasoning) { if (model.reasoning) {
+8 -50
View File
@@ -302,6 +302,13 @@ const OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([
"gpt-5.6-luna", "gpt-5.6-luna",
]); ]);
const XAI_RESPONSES_MODEL_ID = "grok-4.5"; 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 = { const XAI_RESPONSES_EFFORT_LEVEL_MAP = {
off: null, off: null,
minimal: null, minimal: null,
@@ -1783,6 +1790,7 @@ async function generateModels() {
// Combine models (models.dev has priority) // Combine models (models.dev has priority)
const allModels = [...modelsDevModels, ...openRouterModels, ...aiGatewayModels].filter( const allModels = [...modelsDevModels, ...openRouterModels, ...aiGatewayModels].filter(
(model) => (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"), !((model.provider === "opencode" || model.provider === "opencode-go") && model.id === "gpt-5.3-codex-spark"),
); );
@@ -2142,56 +2150,6 @@ async function generateModels() {
]; ];
allModels.push(...codexModels); 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 // 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")) { if (!allModels.some(m => m.provider === "mistral" && m.id === "mistral-medium-3.5")) {
allModels.push({ allModels.push({
@@ -4706,9 +4706,9 @@ export const OPENROUTER_MODELS = {
thinkingLevelMap: {"xhigh":"xhigh"}, thinkingLevelMap: {"xhigh":"xhigh"},
input: ["text"], input: ["text"],
cost: { cost: {
input: 0.9212, input: 0.9198,
output: 2.8952, output: 2.8908,
cacheRead: 0.17108, cacheRead: 0.17082,
cacheWrite: 0, cacheWrite: 0,
}, },
contextWindow: 1048576, contextWindow: 1048576,
-90
View File
@@ -4,78 +4,6 @@
import type { Model } from "../types.ts"; import type { Model } from "../types.ts";
export const XAI_MODELS = { 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": { "grok-4.3": {
id: "grok-4.3", id: "grok-4.3",
name: "Grok 4.3", name: "Grok 4.3",
@@ -131,22 +59,4 @@ export const XAI_MODELS = {
contextWindow: 256000, contextWindow: 256000,
maxTokens: 256000, maxTokens: 256000,
} satisfies Model<"openai-completions">, } 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; } as const;
+1 -1
View File
@@ -262,7 +262,7 @@ describe("Context overflow error handling", () => {
// ============================================================================= // =============================================================================
describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => { 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 model = getModel("xai", "grok-4.3");
const result = await testContextOverflow(model, process.env.XAI_API_KEY!); const result = await testContextOverflow(model, process.env.XAI_API_KEY!);
logResult(result); logResult(result);
@@ -79,7 +79,7 @@ const PROVIDER_MODEL_PAIRS: ProviderModelPair[] = [
label: "bedrock-claude-sonnet-4-5", label: "bedrock-claude-sonnet-4-5",
}, },
// xAI // 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 // Cerebras
{ provider: "cerebras", model: "zai-glm-4.7", label: "cerebras-zai-glm-4.7" }, { provider: "cerebras", model: "zai-glm-4.7", label: "cerebras-zai-glm-4.7" },
// Cloudflare Workers AI // Cloudflare Workers AI
+1 -1
View File
@@ -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"); const llm = getModel("xai", "grok-4.3");
it("should complete basic text generation", { retry: 3 }, async () => { it("should complete basic text generation", { retry: 3 }, async () => {
+9 -13
View File
@@ -244,22 +244,18 @@ describe("totalTokens field", () => {
// ========================================================================= // =========================================================================
describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => { describe.skipIf(!process.env.XAI_API_KEY)("xAI", () => {
it( it("grok-4.3 - should return totalTokens equal to sum of components", { retry: 3, timeout: 60000 }, async () => {
"grok-3-fast - should return totalTokens equal to sum of components", const llm = getModel("xai", "grok-4.3");
{ retry: 3, timeout: 60000 },
async () => {
const llm = getModel("xai", "grok-4.3");
console.log(`\nxAI / ${llm.id}:`); console.log(`\nxAI / ${llm.id}:`);
const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XAI_API_KEY }); const { first, second } = await testTotalTokensWithCache(llm, { apiKey: process.env.XAI_API_KEY });
logUsage("First request", first); logUsage("First request", first);
logUsage("Second request", second); logUsage("Second request", second);
assertTotalTokensEqualsComponents(first); assertTotalTokensEqualsComponents(first);
assertTotalTokensEqualsComponents(second); assertTotalTokensEqualsComponents(second);
}, });
);
}); });
// ========================================================================= // =========================================================================
+12
View File
@@ -60,6 +60,18 @@ describe("xAI Responses provider", () => {
vi.restoreAllMocks(); 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", () => { it("uses Responses with low/medium/high efforts only for Grok 4.5", () => {
expect(XAI_MODELS["grok-4.5"].api).toBe("openai-responses"); expect(XAI_MODELS["grok-4.5"].api).toBe("openai-responses");
expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.5"])).toEqual(["low", "medium", "high"]); expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.5"])).toEqual(["low", "medium", "high"]);
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased] ## [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 ## [0.80.9] - 2026-07-16
### New Features ### New Features