var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
import URL from "../../../Types/API/URL";
import API from "../../../Utils/API";
import LlmType from "../../../Types/LLM/LlmType";
import BadDataException from "../../../Types/Exception/BadDataException";
import logger from "../Logger";
import CaptureSpan from "../Telemetry/CaptureSpan";
class LLMService {
    static async getCompletion(request) {
        const config = request.llmProviderConfig;
        switch (config.llmType) {
            case LlmType.OpenAI:
            case LlmType.Groq:
            case LlmType.Mistral:
                return await this.getOpenAICompatibleCompletion(config, request);
            case LlmType.AzureOpenAI:
                return await this.getAzureOpenAICompletion(config, request);
            case LlmType.Anthropic:
                return await this.getAnthropicCompletion(config, request);
            case LlmType.Ollama:
                return await this.getOllamaCompletion(config, request);
            default:
                throw new BadDataException(`Unsupported LLM type: ${config.llmType}`);
        }
    }
    static async getOpenAICompatibleCompletion(config, request) {
        var _a;
        if (!config.apiKey) {
            throw new BadDataException(`${config.llmType} API key is required`);
        }
        const defaultBaseUrls = {
            [LlmType.OpenAI]: "https://api.openai.com/v1",
            [LlmType.Groq]: "https://api.groq.com/openai/v1",
            [LlmType.Mistral]: "https://api.mistral.ai/v1",
        };
        const defaultModels = {
            [LlmType.OpenAI]: "gpt-4o",
            [LlmType.Groq]: "llama-3.3-70b-versatile",
            [LlmType.Mistral]: "mistral-large-latest",
        };
        const baseUrl = config.baseUrl ||
            defaultBaseUrls[config.llmType] ||
            "https://api.openai.com/v1";
        const modelName = config.modelName || defaultModels[config.llmType] || "gpt-4o";
        const response = await API.post({
            url: URL.fromString(`${baseUrl}/chat/completions`),
            data: {
                model: modelName,
                messages: request.messages.map((msg) => {
                    return {
                        role: msg.role,
                        content: msg.content,
                    };
                }),
                temperature: (_a = request.temperature) !== null && _a !== void 0 ? _a : 0.7,
            },
            headers: {
                Authorization: `Bearer ${config.apiKey}`,
                "Content-Type": "application/json",
            },
            options: {
                retries: 2,
                exponentialBackoff: true,
                timeout: 120000,
            },
        });
        const logAttributes = {
            llmType: config.llmType,
            modelName: modelName,
        };
        if (response instanceof HTTPErrorResponse) {
            logger.error(`Error from ${config.llmType} API:`, logAttributes);
            logger.error(response, logAttributes);
            throw new BadDataException(`${config.llmType} API error: ${JSON.stringify(response.jsonData)}`);
        }
        const jsonData = response.jsonData;
        const choices = jsonData["choices"];
        if (!choices || choices.length === 0) {
            throw new BadDataException(`No response from ${config.llmType}`);
        }
        const message = choices[0]["message"];
        const usage = jsonData["usage"];
        return {
            content: message["content"],
            usage: usage
                ? {
                    promptTokens: usage["prompt_tokens"],
                    completionTokens: usage["completion_tokens"],
                    totalTokens: usage["total_tokens"],
                }
                : undefined,
        };
    }
    static buildAzureOpenAIChatCompletionsUrl(baseUrl) {
        const trimmed = baseUrl.replace(/\/+$/, "");
        const queryIndex = trimmed.indexOf("?");
        const pathPart = queryIndex >= 0 ? trimmed.substring(0, queryIndex) : trimmed;
        const queryPart = queryIndex >= 0 ? trimmed.substring(queryIndex + 1) : "";
        const params = new URLSearchParams(queryPart);
        if (!params.has("api-version")) {
            params.set("api-version", LLMService.AZURE_OPENAI_DEFAULT_API_VERSION);
        }
        return `${pathPart}/chat/completions?${params.toString()}`;
    }
    static async getAzureOpenAICompletion(config, request) {
        var _a;
        if (!config.apiKey) {
            throw new BadDataException("Azure OpenAI API key is required");
        }
        if (!config.baseUrl) {
            throw new BadDataException("Azure OpenAI Base URL is required (e.g. https://<resource>.openai.azure.com/openai/deployments/<deployment>)");
        }
        const modelName = config.modelName || "gpt-4o";
        const requestUrl = LLMService.buildAzureOpenAIChatCompletionsUrl(config.baseUrl);
        const response = await API.post({
            url: URL.fromString(requestUrl),
            data: {
                model: modelName,
                messages: request.messages.map((msg) => {
                    return {
                        role: msg.role,
                        content: msg.content,
                    };
                }),
                temperature: (_a = request.temperature) !== null && _a !== void 0 ? _a : 0.7,
            },
            headers: {
                "api-key": config.apiKey,
                "Content-Type": "application/json",
            },
            options: {
                retries: 2,
                exponentialBackoff: true,
                timeout: 120000,
            },
        });
        const logAttributes = {
            llmType: config.llmType,
            modelName: modelName,
        };
        if (response instanceof HTTPErrorResponse) {
            logger.error("Error from Azure OpenAI API:", logAttributes);
            logger.error(response, logAttributes);
            throw new BadDataException(`Azure OpenAI API error: ${JSON.stringify(response.jsonData)}`);
        }
        const jsonData = response.jsonData;
        const choices = jsonData["choices"];
        if (!choices || choices.length === 0) {
            throw new BadDataException("No response from Azure OpenAI");
        }
        const message = choices[0]["message"];
        const usage = jsonData["usage"];
        return {
            content: message["content"],
            usage: usage
                ? {
                    promptTokens: usage["prompt_tokens"],
                    completionTokens: usage["completion_tokens"],
                    totalTokens: usage["total_tokens"],
                }
                : undefined,
        };
    }
    static async getAnthropicCompletion(config, request) {
        var _a;
        if (!config.apiKey) {
            throw new BadDataException("Anthropic API key is required");
        }
        const baseUrl = config.baseUrl || "https://api.anthropic.com/v1";
        const modelName = config.modelName || "claude-sonnet-4-20250514";
        // Anthropic requires system message to be separate
        let systemMessage = "";
        const userMessages = [];
        for (const msg of request.messages) {
            if (msg.role === "system") {
                systemMessage = msg.content;
            }
            else {
                userMessages.push({
                    role: msg.role,
                    content: msg.content,
                });
            }
        }
        const requestData = {
            model: modelName,
            messages: userMessages,
            temperature: (_a = request.temperature) !== null && _a !== void 0 ? _a : 0.7,
        };
        if (systemMessage) {
            requestData["system"] = systemMessage;
        }
        const response = await API.post({
            url: URL.fromString(`${baseUrl}/messages`),
            data: requestData,
            headers: {
                "x-api-key": config.apiKey,
                "anthropic-version": "2023-06-01",
                "Content-Type": "application/json",
            },
            options: {
                retries: 2,
                exponentialBackoff: true,
                timeout: 120000,
            },
        });
        const anthropicLogAttributes = {
            llmType: config.llmType,
            modelName: modelName,
        };
        if (response instanceof HTTPErrorResponse) {
            logger.error("Error from Anthropic API:", anthropicLogAttributes);
            logger.error(response, anthropicLogAttributes);
            throw new BadDataException(`Anthropic API error: ${JSON.stringify(response.jsonData)}`);
        }
        const jsonData = response.jsonData;
        const content = jsonData["content"];
        if (!content || content.length === 0) {
            throw new BadDataException("No response from Anthropic");
        }
        const textContent = content.find((c) => {
            return c["type"] === "text";
        });
        if (!textContent) {
            throw new BadDataException("No text content in Anthropic response");
        }
        const usage = jsonData["usage"];
        return {
            content: textContent["text"],
            usage: usage
                ? {
                    promptTokens: usage["input_tokens"],
                    completionTokens: usage["output_tokens"],
                    totalTokens: (usage["input_tokens"] || 0) +
                        (usage["output_tokens"] || 0),
                }
                : undefined,
        };
    }
    static async getOllamaCompletion(config, request) {
        var _a;
        if (!config.baseUrl) {
            throw new BadDataException("Ollama base URL is required");
        }
        const modelName = config.modelName || "llama2";
        const response = await API.post({
            url: URL.fromString(`${config.baseUrl}/api/chat`),
            data: {
                model: modelName,
                messages: request.messages.map((msg) => {
                    return {
                        role: msg.role,
                        content: msg.content,
                    };
                }),
                stream: false,
                options: {
                    temperature: (_a = request.temperature) !== null && _a !== void 0 ? _a : 0.7,
                },
            },
            headers: {
                "Content-Type": "application/json",
            },
            options: {
                retries: 2,
                exponentialBackoff: true,
                timeout: 300000, // 5 minutes for Ollama as it may be slower
            },
        });
        const ollamaLogAttributes = {
            llmType: config.llmType,
            modelName: modelName,
        };
        if (response instanceof HTTPErrorResponse) {
            logger.error("Error from Ollama API:", ollamaLogAttributes);
            logger.error(response, ollamaLogAttributes);
            throw new BadDataException(`Ollama API error: ${JSON.stringify(response.jsonData)}`);
        }
        const jsonData = response.jsonData;
        const message = jsonData["message"];
        if (!message) {
            throw new BadDataException("No response from Ollama");
        }
        return {
            content: message["content"],
            usage: undefined, // Ollama doesn't provide token usage in the same way
        };
    }
}
/*
 * Default Azure OpenAI API version. Users can override by including
 * ?api-version=... in their configured base URL.
 */
LLMService.AZURE_OPENAI_DEFAULT_API_VERSION = "2024-10-21";
export default LLMService;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], LLMService, "getCompletion", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], LLMService, "getOpenAICompatibleCompletion", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], LLMService, "getAzureOpenAICompletion", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], LLMService, "getAnthropicCompletion", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], LLMService, "getOllamaCompletion", null);
//# sourceMappingURL=LLMService.js.map