diff --git a/packages/types/src/providers/xai.ts b/packages/types/src/providers/xai.ts index 3189e593da..e6ee98cdee 100644 --- a/packages/types/src/providers/xai.ts +++ b/packages/types/src/providers/xai.ts @@ -17,6 +17,17 @@ export const xaiModels = { cacheReadsPrice: 0.02, description: "xAI's Grok Code Fast model with 256K context window", }, + "grok-4-fast": { + maxTokens: 8192, + contextWindow: 256000, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.0, // Currently free on OpenRouter + outputPrice: 0.0, // Currently free on OpenRouter + cacheWritesPrice: 0.0, + cacheReadsPrice: 0.0, + description: "xAI's Grok-4 Fast model with 256K context window - currently free on OpenRouter", + }, "grok-4": { maxTokens: 8192, contextWindow: 256000, diff --git a/src/api/providers/__tests__/xai.spec.ts b/src/api/providers/__tests__/xai.spec.ts index 1d3d4a1509..05ab821e64 100644 --- a/src/api/providers/__tests__/xai.spec.ts +++ b/src/api/providers/__tests__/xai.spec.ts @@ -69,6 +69,18 @@ describe("XAIHandler", () => { expect(model.info).toEqual(xaiModels[testModelId]) }) + test("should return grok-4-fast model when specified", () => { + const testModelId = "grok-4-fast" + const handlerWithModel = new XAIHandler({ apiModelId: testModelId }) + const model = handlerWithModel.getModel() + + expect(model.id).toBe(testModelId) + expect(model.info).toEqual(xaiModels[testModelId]) + // Verify it's currently free + expect(model.info.inputPrice).toBe(0.0) + expect(model.info.outputPrice).toBe(0.0) + }) + it("should include reasoning_effort parameter for mini models", async () => { const miniModelHandler = new XAIHandler({ apiModelId: "grok-3-mini", diff --git a/src/api/providers/fetchers/__tests__/openrouter.spec.ts b/src/api/providers/fetchers/__tests__/openrouter.spec.ts index 2f19487564..8d58b7afa9 100644 --- a/src/api/providers/fetchers/__tests__/openrouter.spec.ts +++ b/src/api/providers/fetchers/__tests__/openrouter.spec.ts @@ -382,5 +382,34 @@ describe("OpenRouter API", () => { expect(textResult.maxTokens).toBe(64000) expect(imageResult.maxTokens).toBe(64000) }) + + it("sets xai/grok-4-fast model to free pricing", () => { + const mockModel = { + name: "Grok 4 Fast", + description: "xAI's Grok-4 Fast model", + context_length: 256000, + max_completion_tokens: 8192, + pricing: { + prompt: "0.003", + completion: "0.015", + }, + } + + const result = parseOpenRouterModel({ + id: "xai/grok-4-fast", + model: mockModel, + inputModality: ["text"], + outputModality: ["text"], + maxTokens: 8192, + }) + + // Should override pricing to free + expect(result.inputPrice).toBe(0.0) + expect(result.outputPrice).toBe(0.0) + expect(result.cacheWritesPrice).toBe(0.0) + expect(result.cacheReadsPrice).toBe(0.0) + expect(result.contextWindow).toBe(256000) + expect(result.maxTokens).toBe(8192) + }) }) }) diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index 7e3dd7609f..1aa7da0d45 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -263,5 +263,14 @@ export const parseOpenRouterModel = ({ modelInfo.maxTokens = 32768 } + // Set xai/grok-4-fast model configuration (currently free on OpenRouter) + if (id === "xai/grok-4-fast") { + // Override pricing to reflect current free promotion + modelInfo.inputPrice = 0.0 + modelInfo.outputPrice = 0.0 + modelInfo.cacheWritesPrice = 0.0 + modelInfo.cacheReadsPrice = 0.0 + } + return modelInfo }