import { GroqClient, GroqAuthenticationError, GroqTimeoutError, GroqRateLimitError, GroqAPIError, GroqConnectionError, GroqValidationError, GroqError } from '../../src'; import { AvailableModel, ChatMessageTextContent, ChatMessage, ChatMessageContent, ChatMessageImageContent } from '@browserbasehq/stagehand'; import { LLMCache } from "@browserbasehq/stagehand/dist/lib/cache/LLMCache"; import { z } from 'zod'; // Helper function for logging const mockLogger = jest.fn(); describe('GroqClient Integration Tests', () => { let client: GroqClient; let mockCreate: jest.Mock; beforeEach(() => { mockLogger.mockClear(); client = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key' }, }); mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'mixtral-8x7b-32768', choices: [ { message: { role: 'assistant', content: '4' }, finish_reason: 'stop', }, ], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); }); afterEach(() => { jest.restoreAllMocks(); }); it('should successfully create a chat completion', async () => { const response = await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'Hello, what is 2+2?' }], temperature: 0.7, requestId: 'test-request-1', }, logger: mockLogger, }); expect(response).toBeDefined(); expect(response.choices).toBeDefined(); expect(response.choices.length).toBeGreaterThan(0); expect(response.choices[0].message.content).toBeDefined(); expect(response.choices[0].message.content).toEqual(expect.stringContaining('4')); }, 30000); it('should handle an invalid API key', async () => { const invalidClient = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'invalid-api-key', }, }); await expect(invalidClient.createChatCompletion({ options: { messages: [{ role: 'user', content: 'Test' }], requestId: 'test-request-2', }, logger: mockLogger, })).rejects.toThrow(GroqAuthenticationError); }); it('should handle a rate limit error', async () => { // Skip test if mocking is disabled if (process.env.SKIP_MOCK_TESTS) { console.warn('Skipping rate limit test because mocking is disabled'); return; } const mockCreate = jest.fn().mockRejectedValue(new GroqRateLimitError("Rate limit exceeded")); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'Test rate limit' }], requestId: 'test-request-rate-limit', }, logger: mockLogger, })).rejects.toThrow(GroqRateLimitError); }, 30000); it('should handle a timeout error', async () => { // Skip test if mocking is disabled if (process.env.SKIP_MOCK_TESTS) { console.warn('Skipping timeout test because mocking is disabled'); return; } const mockCreate = jest.fn().mockRejectedValue(new GroqTimeoutError("Request timed out")); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'Test timeout' }], requestId: 'test-request-timeout', }, logger: mockLogger, })).rejects.toThrow(GroqTimeoutError); }, 30000); it('should use the specified model', async () => { const modelName = 'llama3-8b-8192' as AvailableModel; const modelClient = new GroqClient({ modelName, clientOptions: { apiKey: process.env.GROQ_API_KEY }, }); expect(modelClient.modelName).toBe(modelName); }); it('should respect the skipTokenLimitCheck option', async () => { const modelName = 'some-unknown-model'; const clientWithSkip = new GroqClient({ modelName: modelName as AvailableModel, clientOptions: { apiKey: process.env.GROQ_API_KEY, skipTokenLimitCheck: true, }, }); // Mock the createChatCompletion to avoid actual API call const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: modelName, choices: [{ message: { content: "Mocked response", role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(clientWithSkip["client"].chat.completions, 'create').mockImplementation(mockCreate); await clientWithSkip.createChatCompletion({ options: { messages: [{ role: 'user', content: 'Test skipTokenLimitCheck' }], maxTokens: 9000, // Intentionally high value requestId: 'test-request-skip-token-limit', }, logger: mockLogger, }); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: 'Skipping token limit validation as configured', })); }); describe('Message Content Handling', () => { it('should handle string type in message content array', async () => { const messages: ChatMessage[] = [ { role: 'user', content: [ { type: 'text', text: 'plain text string' } as ChatMessageTextContent, { type: 'text', text: 'structured text' } as ChatMessageTextContent, ] as ChatMessageContent, }, ]; await client.createChatCompletion({ options: { messages, requestId: 'test-request', }, logger: mockLogger, }); expect(mockCreate).toHaveBeenCalledWith( expect.objectContaining({ messages: [ { role: 'user', content: 'plain text string\nstructured text', }, ], }), ); }); it('should handle various image URL formats in message content', async () => { const messages: ChatMessage[] = [ { role: 'user', content: [ { type: 'text', text: 'Here are some images:' } as ChatMessageTextContent, { type: 'image_url', image_url: { url: 'https://example.com/image1.jpg' } } as ChatMessageImageContent, { type: 'image_url', image_url: { url: '' } } as ChatMessageImageContent, // Test empty URL { type: 'text', text: '' } as ChatMessageTextContent, // Test empty text { type: 'image_url', image_url: { url: 'https://example.com/image2.png' } } as ChatMessageImageContent, ] as ChatMessageContent, }, ]; await client.createChatCompletion({ options: { messages, requestId: 'test-request', }, logger: mockLogger, }); expect(mockCreate).toHaveBeenCalledWith( expect.objectContaining({ messages: [ { role: 'user', content: 'Here are some images:\nhttps://example.com/image1.jpg\n\n\nhttps://example.com/image2.png', }, ], }), ); }); }); describe('System Prompt Handling', () => { let testClient: GroqClient; let testMockCreate: jest.Mock; beforeEach(() => { testClient = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key' }, userProvidedInstructions: undefined, }); testMockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'mixtral-8x7b-32768', choices: [ { message: { role: 'assistant', content: 'Test response' }, finish_reason: 'stop', }, ], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(testClient["client"].chat.completions, 'create').mockImplementation(testMockCreate); }); it('should handle undefined user instructions in combineSystemPrompt', async () => { const messages = [ { role: 'system' as const, content: 'Base system prompt' }, { role: 'user' as const, content: 'Hello' }, ]; await testClient.createChatCompletion({ options: { messages, requestId: 'test-request', }, logger: mockLogger, }); expect(testMockCreate).toHaveBeenCalledWith( expect.objectContaining({ messages: [ { role: 'system', content: 'Base system prompt' }, { role: 'user', content: 'Hello' }, ], }), ); }); }); }); describe('GroqClient Parameter Validation', () => { let client: GroqClient; beforeAll(() => { client = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key', }, }); }); beforeEach(() => { mockLogger.mockClear(); }); it('should throw error for empty messages array', async () => { await expect(client.createChatCompletion({ options: { messages: [], requestId: 'test-empty-messages', }, logger: mockLogger, })).rejects.toThrow('Messages array cannot be empty'); }); it('should throw error for invalid message role', async () => { await expect(client.createChatCompletion({ options: { messages: [{ role: 'invalid' as any, content: 'test' }], requestId: 'test-invalid-role', }, logger: mockLogger, })).rejects.toThrow('Invalid message role: invalid'); }); it('should log warning for unsupported OpenAI parameters', async () => { // Mock the API call to avoid actual request const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-unsupported-params', logprobs: true, logit_bias: { test: 1 }, } as any, logger: mockLogger, }); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: 'Unsupported OpenAI parameters detected and will be ignored', level: 1, auxiliary: expect.objectContaining({ unsupportedParams: { value: JSON.stringify(['logprobs', 'logit_bias']), type: 'object', }, }), })); }); it('should cap maxTokens when exceeding model limit', async () => { // Mock the API call to avoid actual request const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'mixtral-8x7b-32768', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-max-tokens', maxTokens: 50000, // Exceeds the model's limit of 32768 }, logger: mockLogger, }); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: expect.stringContaining('max_tokens (50000) exceeds model limit (32768)'), level: 1, auxiliary: expect.objectContaining({ maxTokens: { value: '50000', type: 'integer', }, modelLimit: { value: '32768', type: 'integer', }, }), })); // Verify the API was called with capped maxTokens expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ max_tokens: 32768, })); }); it('should use default token limit for unknown models', async () => { const unknownModelClient = new GroqClient({ modelName: 'unknown-model' as AvailableModel, clientOptions: { apiKey: 'test-key', }, }); // Mock the API call to avoid actual request const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'unknown-model', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(unknownModelClient["client"].chat.completions, 'create').mockImplementation(mockCreate); await unknownModelClient.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-unknown-model', maxTokens: 10000, }, logger: mockLogger, }); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: expect.stringContaining('Using default token limit (8192) for unknown model'), level: 1, auxiliary: expect.objectContaining({ defaultLimit: { value: '8192', type: 'integer', }, }), })); }); }); describe('GroqClient Error Handling and Tool Calls', () => { let client: GroqClient; beforeAll(() => { client = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key', }, }); }); beforeEach(() => { mockLogger.mockClear(); }); describe('Error Types and Properties', () => { let mockCreate: jest.Mock; beforeEach(() => { mockCreate = jest.fn(); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); }); it('should throw GroqAPIError with correct properties', async () => { mockCreate.mockRejectedValue({ name: 'GroqAPIError', message: 'Invalid request parameters', error: { status: 400, code: 'invalid_request', param: 'messages', message: 'Invalid request parameters', }, }); try { await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-api-error', }, logger: mockLogger, }); fail('Expected GroqAPIError to be thrown'); } catch (error) { expect(error).toBeInstanceOf(GroqAPIError); const apiError = error as GroqAPIError; expect(apiError.status).toBe(400); expect(apiError.code).toBe('invalid_request'); expect(apiError.param).toBe('messages'); expect(apiError.message).toContain('Invalid request parameters'); } }); it('should throw GroqConnectionError for network issues', async () => { mockCreate.mockRejectedValue(new Error('ECONNREFUSED')); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-connection-error', }, logger: mockLogger, })).rejects.toThrow(GroqConnectionError); }); it('should handle retryable errors correctly', async () => { mockCreate.mockRejectedValueOnce({ name: 'GroqRateLimitError', message: 'Rate limit exceeded', error: { status: 429, code: 'rate_limit_exceeded', message: 'Rate limit exceeded', }, }) .mockRejectedValueOnce({ name: 'GroqTimeoutError', message: 'Request timed out', error: { status: 408, code: 'timeout', message: 'Request timed out', }, }) .mockResolvedValueOnce({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); const response = await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-retries', }, retries: 3, // Explicitly set retries to 3 logger: mockLogger, }); expect(mockCreate).toHaveBeenCalledTimes(3); expect(response.choices[0].message.content).toBe('test'); // Verify logging of retry attempts expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: 'Retrying request due to retryable error', level: 1, auxiliary: expect.objectContaining({ retriesRemaining: expect.any(Object), }), })); }); it('should handle connection errors with pattern matching', async () => { const error = { message: 'ECONNREFUSED connection failed' }; mockCreate.mockRejectedValue(error); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-request', }, logger: mockLogger, })).rejects.toThrow(GroqConnectionError); }); it('should re-throw GroqError instances without wrapping', async () => { const mockGroqError = new GroqError('Test error'); mockCreate.mockRejectedValue(mockGroqError); try { await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-error', }, logger: mockLogger, }); fail('Expected error to be thrown'); } catch (err: unknown) { if (!(err instanceof GroqError)) { fail('Expected error to be instance of GroqError'); } expect(err).toBe(mockGroqError); expect(err.message).toBe('Test error'); expect(err).toBeInstanceOf(GroqError); } }); }); describe('Failed Tool Call Generation', () => { let mockCreate: jest.Mock; beforeEach(() => { mockCreate = jest.fn(); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); }); it('should handle direct JSON parse failures', async () => { mockCreate.mockRejectedValue({ name: 'GroqError', message: 'Failed to parse tool call output', error: { status: 400, code: 'failed_generation', error: { failed_generation: '{"invalid json', }, }, }); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-invalid-json', tools: [{ name: 'test', description: 'test', parameters: {}, type: 'function' }], }, logger: mockLogger, })).rejects.toThrow(GroqValidationError); }); it('should handle markdown code block parsing', async () => { mockCreate.mockRejectedValue({ name: 'GroqError', message: 'Failed generation', error: { status: 400, code: 'failed_generation', error: { failed_generation: '```json\n{"arguments": {"key": "value"}}\n```', }, }, }); const response = await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-markdown-json', tools: [{ name: 'test', description: 'test', parameters: {}, type: 'function' }], }, logger: mockLogger, }); expect(response).toEqual({ key: 'value' }); }); it('should handle malformed markdown code blocks', async () => { mockCreate.mockRejectedValue({ name: 'GroqError', message: 'Failed generation', error: { status: 400, code: 'failed_generation', error: { failed_generation: '```json\n{"invalid": "json"\n', }, }, }); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-malformed-markdown', tools: [{ name: 'test', description: 'test', parameters: {}, type: 'function' }], }, logger: mockLogger, })).rejects.toThrow(GroqValidationError); }); it('should handle empty or null failed generation', async () => { mockCreate.mockRejectedValue({ name: 'GroqError', message: 'Failed generation', error: { status: 400, code: 'failed_generation', error: { failed_generation: null, }, }, }); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-empty-generation', tools: [{ name: 'test', description: 'test', parameters: {}, type: 'function' }], }, logger: mockLogger, })).rejects.toThrow(GroqValidationError); }); it('should throw GroqValidationError when parsing tool call fails', async () => { const mockGroqError = new GroqValidationError("Invalid tool call format"); mockCreate.mockRejectedValue({ error: { error: { failed_generation: '{"invalid": json', }, }, }); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-request', tools: [{ name: 'test', description: 'test', parameters: {}, type: 'function' }], }, logger: mockLogger, })).rejects.toThrow(GroqValidationError); // Verify error was logged expect(mockLogger).toHaveBeenCalledWith( expect.objectContaining({ category: 'groq', message: 'Unhandled error in Groq chat completion', level: 2, auxiliary: expect.objectContaining({ error: { value: expect.stringContaining('Failed to parse tool call output'), type: 'string', }, }), }), ); }); it('should handle generic errors in the catch block', async () => { const error = new Error('Unexpected error'); mockCreate.mockRejectedValue(error); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-request', }, logger: mockLogger, })).rejects.toThrow('Unhandled Groq error'); }); }); }); describe('GroqClient Caching', () => { let client: GroqClient; let mockCache: LLMCache; beforeAll(() => { mockCache = { get: jest.fn(), set: jest.fn(), CACHE_MAX_AGE_MS: 3600000, CLEANUP_PROBABILITY: 0.1, cacheDir: '/tmp', cacheFile: 'test-cache.json', lockFile: '/tmp/test-cache.lock', logger: mockLogger, LOCK_TIMEOUT_MS: 5000, lockAcquired: false, clear: jest.fn(), delete: jest.fn(), has: jest.fn(), keys: jest.fn(), size: jest.fn(), values: jest.fn(), entries: jest.fn(), forEach: jest.fn(), [Symbol.iterator]: jest.fn(), acquireLock: jest.fn(), releaseLock: jest.fn(), readFromDisk: jest.fn(), writeToDisk: jest.fn(), ensureCacheDir: jest.fn(), ensureCacheFile: jest.fn(), ensureLockFile: jest.fn(), cleanupStaleEntries: jest.fn(), shouldCleanup: jest.fn(), isStale: jest.fn(), getStats: jest.fn(), toJSON: jest.fn(), } as unknown as LLMCache; client = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key', }, enableCaching: true, cache: mockCache, }); }); beforeEach(() => { mockLogger.mockClear(); (mockCache.get as jest.Mock).mockClear(); (mockCache.set as jest.Mock).mockClear(); }); it('should return cached response when available', async () => { const cachedResponse = { id: 'cached-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { content: 'cached response', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }; (mockCache.get as jest.Mock).mockResolvedValueOnce(cachedResponse); const response = await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-cache', }, logger: mockLogger, }); expect(response).toEqual(cachedResponse); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq_cache', message: 'Returning cached response', level: 1, })); }); it('should cache response when caching is enabled', async () => { const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-cache-set', }, logger: mockLogger, }); expect(mockCache.set).toHaveBeenCalled(); }); }); describe('GroqClient Response Model and Tool Choice', () => { let client: GroqClient; beforeAll(() => { client = new GroqClient({ modelName: 'mixtral-8x7b-32768' as AvailableModel, clientOptions: { apiKey: 'test-key', }, }); }); beforeEach(() => { mockLogger.mockClear(); }); it('should handle response model with successful extraction', async () => { const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { role: 'assistant', content: null, tool_calls: [{ id: 'call-1', type: 'function', function: { name: 'print_extracted_data', arguments: '{"key": "value"}', }, }], }, finish_reason: 'tool_calls', }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); const response = await client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-response-model', response_model: { name: 'TestModel', schema: z.object({ key: z.string() }), }, }, logger: mockLogger, }); expect(response).toEqual({ key: 'value' }); expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ tool_choice: 'required', })); }); it('should handle response model with failed extraction', async () => { const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { role: 'assistant', content: null, tool_calls: [{ id: 'call-1', type: 'function', function: { name: 'print_extracted_data', arguments: 'invalid json', }, }], }, finish_reason: 'tool_calls', }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(client["client"].chat.completions, 'create').mockImplementation(mockCreate); await expect(client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-response-model-fail', response_model: { name: 'TestModel', schema: z.object({ key: z.string() }), }, }, logger: mockLogger, })).rejects.toThrow(GroqValidationError); expect(mockLogger).toHaveBeenCalledWith(expect.objectContaining({ category: 'groq', message: 'Failed to parse structured output', level: 2, })); }); it('should handle r1 model with hidden reasoning format', async () => { const r1Client = new GroqClient({ modelName: 'deepseek-r1-distill-llama-70b' as AvailableModel, clientOptions: { apiKey: 'test-key', }, }); const mockCreate = jest.fn().mockResolvedValue({ id: 'test-id', object: 'chat.completion', created: Date.now(), model: 'test-model', choices: [{ message: { content: 'test', role: 'assistant' }, finish_reason: 'stop' }], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }, }); jest.spyOn(r1Client["client"].chat.completions, 'create').mockImplementation(mockCreate); await r1Client.createChatCompletion({ options: { messages: [{ role: 'user', content: 'test' }], requestId: 'test-r1-model', }, logger: mockLogger, }); expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ reasoning_format: 'hidden', })); }); });