"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const openai_1 = __importDefault(require("openai"));
const cache_1 = require("../../src/cache");
const openai_2 = require("../../src/providers/openai");
jest.mock('../../src/cache');
jest.mock('../../src/globalConfig/globalConfig');
jest.mock('../../src/logger');
const mockFetchWithCache = jest.mocked(cache_1.fetchWithCache);
describe('OpenAI Provider', () => {
    beforeEach(() => {
        jest.resetAllMocks();
        (0, cache_1.disableCache)();
    });
    afterEach(() => {
        (0, cache_1.enableCache)();
    });
    describe('failApiCall', () => {
        it('should format OpenAI API errors', () => {
            const error = new openai_1.default.APIError(400, {
                type: 'invalid_request_error',
                message: 'Bad request',
            }, 'Bad request', {});
            const result = (0, openai_2.failApiCall)(error);
            expect(result).toEqual({
                error: `API error: invalid_request_error 400 Bad request`,
            });
        });
        it('should format generic errors', () => {
            const error = new Error('Network error');
            const result = (0, openai_2.failApiCall)(error);
            expect(result).toEqual({
                error: `API error: Error: Network error`,
            });
        });
    });
    describe('getTokenUsage', () => {
        it('should return token usage for non-cached response', () => {
            const data = {
                usage: {
                    total_tokens: 100,
                    prompt_tokens: 40,
                    completion_tokens: 60,
                },
            };
            const result = (0, openai_2.getTokenUsage)(data, false);
            expect(result).toEqual({
                total: 100,
                prompt: 40,
                completion: 60,
            });
        });
        it('should return cached token usage', () => {
            const data = {
                usage: {
                    total_tokens: 100,
                },
            };
            const result = (0, openai_2.getTokenUsage)(data, true);
            expect(result).toEqual({
                cached: 100,
                total: 100,
            });
        });
        it('should handle missing usage data', () => {
            const data = {};
            const result = (0, openai_2.getTokenUsage)(data, false);
            expect(result).toEqual({});
        });
    });
    describe('OpenAiGenericProvider', () => {
        const provider = new openai_2.OpenAiGenericProvider('test-model', {
            config: {
                apiKey: 'test-key',
                organization: 'test-org',
            },
        });
        it('should generate correct API URL', () => {
            expect(provider.getApiUrl()).toBe('https://api.openai.com/v1');
        });
        it('should use custom API host', () => {
            const customProvider = new openai_2.OpenAiGenericProvider('test-model', {
                config: { apiHost: 'custom.openai.com' },
            });
            expect(customProvider.getApiUrl()).toBe('https://custom.openai.com/v1');
        });
        it('should get organization', () => {
            expect(provider.getOrganization()).toBe('test-org');
        });
        it('should get API key', () => {
            expect(provider.getApiKey()).toBe('test-key');
        });
    });
    describe('OpenAiEmbeddingProvider', () => {
        const provider = new openai_2.OpenAiEmbeddingProvider('text-embedding-3-large', {
            config: {
                apiKey: 'test-key',
            },
        });
        it('should call embedding API successfully', async () => {
            const mockResponse = {
                data: [
                    {
                        embedding: [0.1, 0.2, 0.3],
                    },
                ],
                usage: {
                    total_tokens: 10,
                    prompt_tokens: 0,
                    completion_tokens: 0,
                },
            };
            jest.mocked(cache_1.fetchWithCache).mockResolvedValue({
                data: mockResponse,
                cached: false,
                status: 200,
                statusText: 'OK',
            });
            const result = await provider.callEmbeddingApi('test text');
            expect(result.embedding).toEqual([0.1, 0.2, 0.3]);
            expect(result.tokenUsage).toEqual({
                total: 10,
                prompt: 0,
                completion: 0,
            });
        });
        it('should handle API errors', async () => {
            jest.mocked(cache_1.fetchWithCache).mockRejectedValue(new Error('API error'));
            await expect(provider.callEmbeddingApi('test text')).rejects.toThrow('API error');
        });
    });
    describe('calculateOpenAICost', () => {
        it('should calculate cost correctly', () => {
            const cost = (0, openai_2.calculateOpenAICost)('gpt-4', {}, 100, 50);
            expect(cost).toBeDefined();
            expect(typeof cost).toBe('number');
        });
        it('should handle unknown models', () => {
            const cost = (0, openai_2.calculateOpenAICost)('unknown-model', {}, 100, 50);
            expect(cost).toBeUndefined();
        });
    });
    describe('OpenAiChatCompletionProvider', () => {
        it('should call API successfully', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
        it('should handle caching correctly', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output 2' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output 2');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            const cachedResponse = {
                ...mockResponse,
                cached: true,
            };
            mockFetchWithCache.mockResolvedValue(cachedResponse);
            const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(2);
            expect(result2.output).toBe('Test output 2');
            expect(result2.tokenUsage).toEqual({ total: 10, cached: 10 });
        });
        it('should handle disabled cache correctly', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            (0, cache_1.disableCache)();
            const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(2);
            expect(result2.output).toBe('Test output');
            expect(result2.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            (0, cache_1.enableCache)();
        });
        it('constructor should handle config correctly', async () => {
            const config = {
                temperature: 3.1415926,
                max_tokens: 201,
            };
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', { config });
            const prompt = 'Test prompt';
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            await provider.callApi(prompt);
            const call = mockFetchWithCache.mock.calls[0];
            const requestBody = JSON.parse(call[1].body);
            expect(requestBody).toMatchObject({
                temperature: 3.1415926,
                max_tokens: 201,
            });
            expect(provider.config.temperature).toBe(config.temperature);
            expect(provider.config.max_tokens).toBe(config.max_tokens);
        });
        it('should handle structured output correctly', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: '{"name": "John", "age": 30}' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    response_format: {
                        type: 'json_schema',
                        json_schema: {
                            name: 'person',
                            strict: true,
                            schema: {
                                type: 'object',
                                properties: {
                                    name: { type: 'string' },
                                    age: { type: 'number' },
                                },
                                required: ['name', 'age'],
                                additionalProperties: false,
                            },
                        },
                    },
                },
            });
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Get me a person' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toEqual({ name: 'John', age: 30 });
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
        it('should handle model refusals correctly', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { refusal: 'Content policy violation' } }],
                    usage: { total_tokens: 5, prompt_tokens: 5, completion_tokens: 0 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Generate inappropriate content' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Content policy violation');
            expect(result.tokenUsage).toEqual({ total: 5, prompt: 5, completion: 0 });
            expect(result.isRefusal).toBe(true);
        });
        it('should handle function tool callbacks correctly', async () => {
            const mockResponse = {
                data: {
                    choices: [
                        {
                            message: {
                                content: null,
                                tool_calls: [
                                    {
                                        function: {
                                            name: 'get_weather',
                                            arguments: '{"location":"New York"}',
                                        },
                                    },
                                ],
                            },
                        },
                    ],
                    usage: { total_tokens: 15, prompt_tokens: 10, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const mockWeatherFunction = jest.fn().mockResolvedValue('Sunny, 25°C');
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    tools: [
                        {
                            type: 'function',
                            function: {
                                name: 'get_weather',
                                description: 'Get the weather for a location',
                                parameters: {
                                    type: 'object',
                                    properties: {
                                        location: { type: 'string' },
                                    },
                                    required: ['location'],
                                },
                            },
                        },
                    ],
                    functionToolCallbacks: {
                        get_weather: mockWeatherFunction,
                    },
                },
            });
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: "What's the weather in New York?" }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(mockWeatherFunction).toHaveBeenCalledWith('{"location":"New York"}');
            expect(result.output).toBe('Sunny, 25°C');
            expect(result.tokenUsage).toEqual({ total: 15, prompt: 10, completion: 5 });
        });
        it('should handle multiple function tool calls', async () => {
            const mockResponse = {
                data: {
                    choices: [
                        {
                            message: {
                                content: null,
                                tool_calls: [
                                    {
                                        function: {
                                            name: 'addNumbers',
                                            arguments: '{"a":5,"b":6}',
                                        },
                                    },
                                    {
                                        function: {
                                            name: 'multiplyNumbers',
                                            arguments: '{"x":2,"y":3}',
                                        },
                                    },
                                ],
                            },
                        },
                    ],
                    usage: { total_tokens: 15, prompt_tokens: 7, completion_tokens: 8 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    tools: [
                        {
                            type: 'function',
                            function: {
                                name: 'addNumbers',
                                description: 'Add two numbers together',
                                parameters: {
                                    type: 'object',
                                    properties: {
                                        a: { type: 'number' },
                                        b: { type: 'number' },
                                    },
                                    required: ['a', 'b'],
                                },
                            },
                        },
                        {
                            type: 'function',
                            function: {
                                name: 'multiplyNumbers',
                                description: 'Multiply two numbers',
                                parameters: {
                                    type: 'object',
                                    properties: {
                                        x: { type: 'number' },
                                        y: { type: 'number' },
                                    },
                                    required: ['x', 'y'],
                                },
                            },
                        },
                    ],
                    functionToolCallbacks: {
                        addNumbers: (parametersJsonString) => {
                            const { a, b } = JSON.parse(parametersJsonString);
                            return Promise.resolve(JSON.stringify(a + b));
                        },
                        multiplyNumbers: (parametersJsonString) => {
                            const { x, y } = JSON.parse(parametersJsonString);
                            return Promise.resolve(JSON.stringify(x * y));
                        },
                    },
                },
            });
            const result = await provider.callApi('Add 5 and 6, then multiply 2 and 3');
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('11\n6');
            expect(result.tokenUsage).toEqual({ total: 15, prompt: 7, completion: 8 });
        });
        it('should handle errors in function tool callbacks', async () => {
            const mockResponse = {
                data: {
                    choices: [
                        {
                            message: {
                                content: null,
                                function_call: {
                                    name: 'errorFunction',
                                    arguments: '{}',
                                },
                            },
                        },
                    ],
                    usage: { total_tokens: 5, prompt_tokens: 2, completion_tokens: 3 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    tools: [
                        {
                            type: 'function',
                            function: {
                                name: 'errorFunction',
                                description: 'A function that always throws an error',
                                parameters: {
                                    type: 'object',
                                    properties: {},
                                },
                            },
                        },
                    ],
                    functionToolCallbacks: {
                        errorFunction: () => {
                            throw new Error('Test error');
                        },
                    },
                },
            });
            const result = await provider.callApi('Call the error function');
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toEqual({ arguments: '{}', name: 'errorFunction' });
            expect(result.tokenUsage).toEqual({ total: 5, prompt: 2, completion: 3 });
        });
        it('should prioritize response_format from prompt config over provider config', async () => {
            const providerResponseFormat = {
                type: 'json_object',
            };
            const promptResponseFormat = {
                type: 'json_schema',
                json_schema: {
                    name: 'test_schema',
                    strict: true,
                    schema: {
                        type: 'object',
                        properties: { key2: { type: 'string' } },
                        additionalProperties: false,
                    },
                },
            };
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    response_format: providerResponseFormat,
                },
            });
            const mockResponse = {
                data: {
                    choices: [{ message: { content: '{"key2": "value2"}' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const result = await provider.callApi('Test prompt', {
                vars: {},
                prompt: {
                    raw: 'Test prompt',
                    label: 'Test prompt',
                    config: {
                        response_format: promptResponseFormat,
                    },
                },
            });
            const call = mockFetchWithCache.mock.calls[0];
            const requestBody = JSON.parse(call[1].body);
            expect(requestBody.response_format).toEqual(promptResponseFormat);
            expect(result.output).toEqual({ key2: 'value2' });
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
        it('should use provider config response_format when prompt config is not provided', async () => {
            const providerResponseFormat = {
                type: 'json_object',
            };
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini', {
                config: {
                    response_format: providerResponseFormat,
                },
            });
            const mockResponse = {
                data: {
                    choices: [{ message: { content: '{"key1": "value1"}' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const result = await provider.callApi('Test prompt', {
                vars: {},
                prompt: {
                    raw: 'Test prompt',
                    label: 'Test prompt',
                },
            });
            const call = mockFetchWithCache.mock.calls[0];
            const requestBody = JSON.parse(call[1].body);
            expect(requestBody.response_format).toEqual(providerResponseFormat);
            expect(result.output).toBe('{"key1": "value1"}');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
        it('should call API with basic chat completion', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
                severity: 'info',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
        it('should handle caching correctly with multiple chat calls', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output 2' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
                severity: 'info',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output 2');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            const cachedResponse = {
                ...mockResponse,
                cached: true,
                status: 200,
                statusText: 'OK',
                severity: 'info',
            };
            mockFetchWithCache.mockResolvedValue(cachedResponse);
            const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt 2' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(2);
            expect(result2.output).toBe('Test output 2');
            expect(result2.tokenUsage).toEqual({ total: 10, cached: 10 });
        });
        it('should handle disabled cache correctly for chat completion', async () => {
            const mockResponse = {
                data: {
                    choices: [{ message: { content: 'Test output' } }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
                severity: 'info',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiChatCompletionProvider('gpt-4o-mini');
            const result = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            (0, cache_1.disableCache)();
            const result2 = await provider.callApi(JSON.stringify([{ role: 'user', content: 'Test prompt' }]));
            expect(mockFetchWithCache).toHaveBeenCalledTimes(2);
            expect(result2.output).toBe('Test output');
            expect(result2.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
            (0, cache_1.enableCache)();
        });
    });
    describe('OpenAiCompletionProvider', () => {
        it('should call API successfully with text completion', async () => {
            const mockResponse = {
                data: {
                    choices: [{ text: 'Test output' }],
                    usage: { total_tokens: 10, prompt_tokens: 5, completion_tokens: 5 },
                },
                cached: false,
                status: 200,
                statusText: 'OK',
                severity: 'info',
            };
            mockFetchWithCache.mockResolvedValue(mockResponse);
            const provider = new openai_2.OpenAiCompletionProvider('text-davinci-003');
            const result = await provider.callApi('Test prompt');
            expect(mockFetchWithCache).toHaveBeenCalledTimes(1);
            expect(result.output).toBe('Test output');
            expect(result.tokenUsage).toEqual({ total: 10, prompt: 5, completion: 5 });
        });
    });
});
//# sourceMappingURL=openai.test.js.map