From 1a584a7a56eb5e7b4ff8ccbd46430f1533282eed Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 22 Sep 2026 00:07:34 +0200 Subject: [PATCH] feat(ai,coding-agent): add Grok 4.7 support --- packages/ai/CHANGELOG.md | 4 +++ packages/ai/README.md | 2 +- packages/ai/scripts/generate-models.ts | 7 +--- packages/ai/test/model-catalog-types.test.ts | 2 ++ packages/ai/test/stream.test.ts | 4 +-- packages/ai/test/xai-responses.test.ts | 32 +++++++++++++++++-- packages/coding-agent/CHANGELOG.md | 4 +++ .../coding-agent/src/core/model-resolver.ts | 2 +- .../coding-agent/test/model-resolver.test.ts | 2 +- 9 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 94240ff85..4e1b21ead 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added Grok 4.7 to the built-in xAI model catalog with long-context pricing metadata. + ### Fixed - Fixed image-only user messages being rejected by some OpenAI-compatible providers because they included an empty text part ([#9797](https://github.com/earendil-works/pi/issues/9797)) diff --git a/packages/ai/README.md b/packages/ai/README.md index 34237bd2b..7395672ff 100644 --- a/packages/ai/README.md +++ b/packages/ai/README.md @@ -833,7 +833,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-4.6'); +// or models.getModel('xai', 'grok-4.7'); // 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 ba4a49373..7b54f34d3 100644 --- a/packages/ai/scripts/generate-models.ts +++ b/packages/ai/scripts/generate-models.ts @@ -2030,12 +2030,7 @@ async function loadModelsDevData(): Promise[]> { compat: { ...XAI_RESPONSES_COMPAT }, reasoning: m.reasoning === true, input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"], - cost: { - input: m.cost?.input || 0, - output: m.cost?.output || 0, - cacheRead: m.cost?.cache_read || 0, - cacheWrite: m.cost?.cache_write || 0, - }, + cost: getModelsDevCost(m.cost), contextWindow: m.limit?.context || 4096, maxTokens: m.limit?.output || 4096, }); diff --git a/packages/ai/test/model-catalog-types.test.ts b/packages/ai/test/model-catalog-types.test.ts index 57f02e0f2..449764a93 100644 --- a/packages/ai/test/model-catalog-types.test.ts +++ b/packages/ai/test/model-catalog-types.test.ts @@ -8,6 +8,8 @@ it("derives model API, ID, and provider literals from grouped model data", () => expectTypeOf(XAI_MODELS["grok-4.5"].provider).toEqualTypeOf<"xai">(); expectTypeOf(XAI_MODELS["grok-4.6"].api).toEqualTypeOf<"openai-responses">(); expectTypeOf(XAI_MODELS["grok-4.6"].id).toEqualTypeOf<"grok-4.6">(); + expectTypeOf(XAI_MODELS["grok-4.7"].api).toEqualTypeOf<"openai-responses">(); + expectTypeOf(XAI_MODELS["grok-4.7"].id).toEqualTypeOf<"grok-4.7">(); expectTypeOf(XAI_MODELS["grok-4.3"].api).toEqualTypeOf<"openai-responses">(); }); diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 362f30143..eb9afa226 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -540,8 +540,8 @@ describe("Generate E2E Tests", () => { }); }); - describe.skipIf(!process.env.XAI_API_KEY)("xAI Provider (grok-4.3 via OpenAI Responses)", () => { - const llm = getModel("xai", "grok-4.3"); + describe.skipIf(!process.env.XAI_API_KEY)("xAI Provider (grok-4.7 via OpenAI Responses)", () => { + const llm = getModel("xai", "grok-4.7"); it("should complete basic text generation", { retry: 3 }, async () => { await basicTextGeneration(llm); diff --git a/packages/ai/test/xai-responses.test.ts b/packages/ai/test/xai-responses.test.ts index d88c35a4d..1aa570177 100644 --- a/packages/ai/test/xai-responses.test.ts +++ b/packages/ai/test/xai-responses.test.ts @@ -132,9 +132,35 @@ describe("xAI Responses provider", () => { } expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.5"])).toEqual(["low", "medium", "high"]); expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.6"])).toEqual(["low", "medium", "high", "xhigh"]); + expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.7"])).toEqual(["low", "medium", "high", "xhigh"]); expect(getSupportedThinkingLevels(XAI_MODELS["grok-4.3"])).toEqual(["off", "low", "medium", "high"]); }); + it("includes Grok 4.7 capabilities and long-context pricing", () => { + expect(XAI_MODELS["grok-4.7"]).toMatchObject({ + api: "openai-responses", + reasoning: true, + input: ["text", "image"], + contextWindow: 500000, + maxTokens: 500000, + cost: { + input: 2, + output: 6, + cacheRead: 0.5, + cacheWrite: 0, + tiers: [ + { + inputTokensAbove: 200000, + input: 4, + output: 12, + cacheRead: 1, + cacheWrite: 0, + }, + ], + }, + }); + }); + it("uses /responses with bearer auth and xAI-compatible request fields", async () => { const captured = await captureRequest( XAI_MODELS["grok-4.5"], @@ -188,9 +214,9 @@ describe("xAI Responses provider", () => { expect(captured.body).not.toHaveProperty("reasoning"); }); - it("uses /responses for Grok 4.6 with xhigh effort and encrypted reasoning", async () => { + it("uses /responses for Grok 4.7 with xhigh effort and encrypted reasoning", async () => { const captured = await captureRequest( - XAI_MODELS["grok-4.6"], + XAI_MODELS["grok-4.7"], { systemPrompt: "You are a careful coding assistant.", messages: [{ role: "user", content: "hello", timestamp: 1 }], @@ -203,7 +229,7 @@ describe("xAI Responses provider", () => { expect(captured.url).toBe("https://api.x.ai/v1/responses"); expect(captured.body).toMatchObject({ - model: "grok-4.6", + model: "grok-4.7", store: false, stream: true, reasoning: { effort: "xhigh" }, diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 7994facf3..978fa5523 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +- Changed the default xAI model to Grok 4.7. + ## [0.87.0] - 2026-09-21 ### New Features diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index 64aa93608..44f22ad8f 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -32,7 +32,7 @@ export const defaultModelPerProvider: Record = { "github-copilot": "gpt-5.4", openrouter: "moonshotai/kimi-k2.6", "vercel-ai-gateway": "zai/glm-5.1", - xai: "grok-4.6", + xai: "grok-4.7", groq: "openai/gpt-oss-120b", cerebras: "gpt-oss-120b", zai: "glm-5.3", diff --git a/packages/coding-agent/test/model-resolver.test.ts b/packages/coding-agent/test/model-resolver.test.ts index 8f37b7054..ebed94c33 100644 --- a/packages/coding-agent/test/model-resolver.test.ts +++ b/packages/coding-agent/test/model-resolver.test.ts @@ -735,7 +735,7 @@ describe("default model selection", () => { }); test("xai default tracks current model", () => { - expect(defaultModelPerProvider.xai).toBe("grok-4.6"); + expect(defaultModelPerProvider.xai).toBe("grok-4.7"); }); test("qwen token plan individual default tracks current model", () => {