From 75efd707f7fe8dd259c8abd78f22256fc176dd39 Mon Sep 17 00:00:00 2001 From: Ismail Pelaseyed Date: Mon, 23 Mar 2026 14:00:49 +0100 Subject: [PATCH] fix: only send reasoningEffort for grok-3-mini (#200) * fix: only send reasoningEffort for grok-3-mini According to xAI documentation, only grok-3-mini supports the reasoning_effort parameter. Other models (grok-3, grok-4, grok-4-fast-reasoning) do not support this parameter and return a 'Bad request' error when it is sent. Changes: - Added supportsReasoningEffort field to ModelInfo interface - Marked only grok-3-mini with supportsReasoningEffort: true - Updated getSupportedReasoningEfforts() to check supportsReasoningEffort instead of the generic reasoning flag - This prevents sending reasoningEffort to unsupported models Fixes the 410 error when using models like grok-code-fast-1 with reasoning effort configured. Co-authored-by: Ismail Pelaseyed * test: update tests to reflect grok-3-mini-only reasoning effort support Updated model tests to verify that only grok-3-mini supports reasoningEffort parameter, and all other models (including reasoning models like grok-4-0709, grok-code-fast-1) correctly return empty supported efforts arrays. Co-authored-by: Ismail Pelaseyed * test: add integration tests for resolveModelRuntime Verify that providerOptions.xai.reasoningEffort is only included for models that support it (currently only grok-3-mini), and is correctly omitted for all other models regardless of their reasoning flag. Co-authored-by: Ismail Pelaseyed * fix: organize imports in client.test.ts for Biome linter Biome requires imports to be sorted alphabetically. Moved @ai-sdk/xai import before vitest import to satisfy the linter. Co-authored-by: Ismail Pelaseyed * fix: add comprehensive tests for reasoningEffort with grok-3-mini - Fixed misleading test name that said 'includes' but asserted undefined - Added new test suite 'with configured reasoning effort' that mocks getReasoningEffortForModel to verify positive cases - Tests now verify that grok-3-mini DOES receive providerOptions when reasoning effort is configured (high or low) - Tests verify that other models (grok-4-0709, grok-code-fast-1, etc.) do NOT receive providerOptions even when effort is configured - Increased test coverage from 90 to 100 tests Addresses review feedback about missing positive test case. Co-authored-by: Ismail Pelaseyed * refactor: remove dead code branches in getSupportedReasoningEfforts Removed unreachable multiAgent and responsesOnly branches since only grok-3-mini currently has supportsReasoningEffort: true, and it has neither of those flags. Added comment explaining that only grok-3-mini supports reasoning_effort per xAI documentation, and it supports 'low' and 'high' efforts. If xAI adds reasoning_effort support to other models in the future, the logic can be expanded as needed. Addresses review feedback about dead code. Co-authored-by: Ismail Pelaseyed --------- Co-authored-by: Cursor Agent Co-authored-by: Ismail Pelaseyed --- src/grok/client.test.ts | 104 ++++++++++++++++++++++++++++++++++++++++ src/grok/models.test.ts | 16 +++++-- src/grok/models.ts | 7 +-- src/types/index.ts | 1 + 4 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 src/grok/client.test.ts diff --git a/src/grok/client.test.ts b/src/grok/client.test.ts new file mode 100644 index 0000000..c2d656e --- /dev/null +++ b/src/grok/client.test.ts @@ -0,0 +1,104 @@ +import { createXai } from "@ai-sdk/xai"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import * as settings from "../utils/settings"; +import { resolveModelRuntime } from "./client"; + +describe("client", () => { + const mockProvider = createXai({ + apiKey: "test-key", + baseURL: "https://api.x.ai/v1", + }); + + describe("resolveModelRuntime", () => { + describe("without configured reasoning effort", () => { + it("does not include providerOptions for grok-3-mini when no effort configured", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-3-mini"); + expect(runtime.modelId).toBe("grok-3-mini"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-4-0709 even though it has reasoning flag", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-4-0709"); + expect(runtime.modelId).toBe("grok-4-0709"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-code-fast-1", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-code-fast-1"); + expect(runtime.modelId).toBe("grok-code-fast-1"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-4-1-fast-reasoning", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-4-1-fast-reasoning"); + expect(runtime.modelId).toBe("grok-4-1-fast-reasoning"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-4.20-multi-agent", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-4.20-multi-agent"); + expect(runtime.modelId).toBe("grok-4.20-multi-agent-0309"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-3", () => { + const runtime = resolveModelRuntime(mockProvider, "grok-3"); + expect(runtime.modelId).toBe("grok-3"); + expect(runtime.providerOptions).toBeUndefined(); + }); + }); + + describe("with configured reasoning effort", () => { + beforeEach(() => { + vi.spyOn(settings, "getReasoningEffortForModel"); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("includes providerOptions with reasoningEffort for grok-3-mini when effort is configured", () => { + vi.mocked(settings.getReasoningEffortForModel).mockReturnValue("high"); + const runtime = resolveModelRuntime(mockProvider, "grok-3-mini"); + expect(runtime.modelId).toBe("grok-3-mini"); + expect(runtime.providerOptions).toEqual({ + xai: { + reasoningEffort: "high", + }, + }); + }); + + it("includes providerOptions with low effort for grok-3-mini when configured", () => { + vi.mocked(settings.getReasoningEffortForModel).mockReturnValue("low"); + const runtime = resolveModelRuntime(mockProvider, "grok-3-mini"); + expect(runtime.modelId).toBe("grok-3-mini"); + expect(runtime.providerOptions).toEqual({ + xai: { + reasoningEffort: "low", + }, + }); + }); + + it("does not include providerOptions for grok-4-0709 even when effort is configured", () => { + vi.mocked(settings.getReasoningEffortForModel).mockReturnValue("high"); + const runtime = resolveModelRuntime(mockProvider, "grok-4-0709"); + expect(runtime.modelId).toBe("grok-4-0709"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-code-fast-1 even when effort is configured", () => { + vi.mocked(settings.getReasoningEffortForModel).mockReturnValue("high"); + const runtime = resolveModelRuntime(mockProvider, "grok-code-fast-1"); + expect(runtime.modelId).toBe("grok-code-fast-1"); + expect(runtime.providerOptions).toBeUndefined(); + }); + + it("does not include providerOptions for grok-4-1-fast-reasoning even when effort is configured", () => { + vi.mocked(settings.getReasoningEffortForModel).mockReturnValue("high"); + const runtime = resolveModelRuntime(mockProvider, "grok-4-1-fast-reasoning"); + expect(runtime.modelId).toBe("grok-4-1-fast-reasoning"); + expect(runtime.providerOptions).toBeUndefined(); + }); + }); + }); +}); diff --git a/src/grok/models.test.ts b/src/grok/models.test.ts index 33e355c..e82404a 100644 --- a/src/grok/models.test.ts +++ b/src/grok/models.test.ts @@ -26,14 +26,22 @@ describe("models", () => { }); it("reports supported reasoning-effort levels", () => { - expect(getSupportedReasoningEfforts("grok-4.20-multi-agent-0309")).toEqual(["low", "medium", "high", "xhigh"]); - expect(getSupportedReasoningEfforts("grok-4-1-fast-reasoning")).toEqual(["low", "high"]); + expect(getSupportedReasoningEfforts("grok-3-mini")).toEqual(["low", "high"]); + expect(getSupportedReasoningEfforts("grok-4.20-multi-agent-0309")).toEqual([]); + expect(getSupportedReasoningEfforts("grok-4-1-fast-reasoning")).toEqual([]); expect(getSupportedReasoningEfforts("grok-4-1-fast-non-reasoning")).toEqual([]); + expect(getSupportedReasoningEfforts("grok-code-fast-1")).toEqual([]); + expect(getSupportedReasoningEfforts("grok-4-0709")).toEqual([]); + expect(getSupportedReasoningEfforts("grok-3")).toEqual([]); }); it("resolves effective reasoning effort with defaults and overrides", () => { - expect(getEffectiveReasoningEffort("grok-4.20-multi-agent-0309")).toBe("low"); - expect(getEffectiveReasoningEffort("grok-4.20-multi-agent-0309", "high")).toBe("high"); + expect(getEffectiveReasoningEffort("grok-3-mini")).toBeUndefined(); + expect(getEffectiveReasoningEffort("grok-3-mini", "high")).toBe("high"); + expect(getEffectiveReasoningEffort("grok-3-mini", "low")).toBe("low"); + expect(getEffectiveReasoningEffort("grok-4.20-multi-agent-0309")).toBeUndefined(); + expect(getEffectiveReasoningEffort("grok-4.20-multi-agent-0309", "high")).toBeUndefined(); expect(getEffectiveReasoningEffort("grok-4-1-fast-reasoning")).toBeUndefined(); + expect(getEffectiveReasoningEffort("grok-code-fast-1", "high")).toBeUndefined(); }); }); diff --git a/src/grok/models.ts b/src/grok/models.ts index 9a77e98..aea1014 100644 --- a/src/grok/models.ts +++ b/src/grok/models.ts @@ -110,6 +110,7 @@ export const MODELS: ModelInfo[] = [ reasoning: false, description: "Budget-friendly compact model", aliases: ["grok-3-mini-fast"], + supportsReasoningEffort: true, }, ]; @@ -149,9 +150,9 @@ export function isKnownModelId(modelId: string): boolean { export function getSupportedReasoningEfforts(modelId: string): ReasoningEffort[] { const modelInfo = getModelInfo(modelId); - if (!modelInfo?.reasoning) return []; - if (modelInfo.multiAgent) return ["low", "medium", "high", "xhigh"]; - if (modelInfo.responsesOnly) return ["low", "medium", "high"]; + if (!modelInfo?.supportsReasoningEffort) return []; + // Currently only grok-3-mini supports reasoning_effort per xAI docs + // It supports "low" and "high" efforts return ["low", "high"]; } diff --git a/src/types/index.ts b/src/types/index.ts index 506697f..0f8372e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -123,6 +123,7 @@ export interface ModelInfo { supportsClientTools?: boolean; supportsMaxOutputTokens?: boolean; defaultReasoningEffort?: ReasoningEffort; + supportsReasoningEffort?: boolean; } export type AgentMode = "agent" | "plan" | "ask";