diff --git a/src/api/providers/__tests__/xai.spec.ts b/src/api/providers/__tests__/xai.spec.ts index 1d3d4a1509..f57ed70dee 100644 --- a/src/api/providers/__tests__/xai.spec.ts +++ b/src/api/providers/__tests__/xai.spec.ts @@ -276,8 +276,35 @@ describe("XAIHandler", () => { temperature: 0, messages: expect.arrayContaining([{ role: "system", content: systemPrompt }]), stream: true, - stream_options: { include_usage: true }, + // xAI API doesn't support stream_options }), ) }) + + it("should not include stream_options for Grok-4 model", async () => { + // Create handler with Grok-4 model + const grok4Handler = new XAIHandler({ apiModelId: "grok-4" }) + + // Setup mock for streaming response + mockCreate.mockImplementationOnce(() => { + return { + [Symbol.asyncIterator]: () => ({ + async next() { + return { done: true } + }, + }), + } + }) + + // Start generating a message + const messageGenerator = grok4Handler.createMessage("test prompt", []) + await messageGenerator.next() + + // Verify that stream_options was NOT included + const calls = mockCreate.mock.calls + const lastCall = calls[calls.length - 1][0] + expect(lastCall).not.toHaveProperty("stream_options") + expect(lastCall.stream).toBe(true) + expect(lastCall.model).toBe("grok-4") + }) }) diff --git a/src/api/providers/xai.ts b/src/api/providers/xai.ts index 596c9e89b8..cfeb0956d8 100644 --- a/src/api/providers/xai.ts +++ b/src/api/providers/xai.ts @@ -54,7 +54,7 @@ export class XAIHandler extends BaseProvider implements SingleCompletionHandler temperature: this.options.modelTemperature ?? XAI_DEFAULT_TEMPERATURE, messages: [{ role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages)], stream: true, - stream_options: { include_usage: true }, + // xAI API doesn't support stream_options parameter ...(reasoning && reasoning), }) @@ -75,15 +75,19 @@ export class XAIHandler extends BaseProvider implements SingleCompletionHandler } } + // Note: xAI API doesn't support stream_options, so usage data might not be available in streaming responses if (chunk.usage) { // Extract detailed token information if available // First check for prompt_tokens_details structure (real API response) - const promptDetails = "prompt_tokens_details" in chunk.usage ? chunk.usage.prompt_tokens_details : null; - const cachedTokens = promptDetails && "cached_tokens" in promptDetails ? promptDetails.cached_tokens : 0; + const promptDetails = "prompt_tokens_details" in chunk.usage ? chunk.usage.prompt_tokens_details : null + const cachedTokens = promptDetails && "cached_tokens" in promptDetails ? promptDetails.cached_tokens : 0 // Fall back to direct fields in usage (used in test mocks) - const readTokens = cachedTokens || ("cache_read_input_tokens" in chunk.usage ? (chunk.usage as any).cache_read_input_tokens : 0); - const writeTokens = "cache_creation_input_tokens" in chunk.usage ? (chunk.usage as any).cache_creation_input_tokens : 0; + const readTokens = + cachedTokens || + ("cache_read_input_tokens" in chunk.usage ? (chunk.usage as any).cache_read_input_tokens : 0) + const writeTokens = + "cache_creation_input_tokens" in chunk.usage ? (chunk.usage as any).cache_creation_input_tokens : 0 yield { type: "usage",