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";