"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericAnthropicClient = void 0;
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
const utils_1 = require("../utils");
const pricing_1 = require("./pricing");
const contextLimits_1 = require("./contextLimits");
const types_1 = require("../types");
class GenericAnthropicClient {
    client;
    apiKey;
    constructor(apiKey) {
        this.setKey(apiKey || process.env.ANTHROPIC_API_KEY || "");
    }
    setKey(apiKey) {
        this.apiKey = apiKey;
        this.client = new sdk_1.default({
            apiKey: apiKey || process.env.ANTHROPIC_API_KEY,
        });
    }
    handleToolCaching(tools, longTtl = false) {
        const lastTool = tools[tools.length - 1];
        if (lastTool) {
            lastTool.cache_control = longTtl ? { type: "ephemeral", ttl: "1h" } : { type: "ephemeral" };
        }
    }
    cleanSchemaForAnthropic(schema) {
        if (!schema || typeof schema !== 'object') {
            return schema;
        }
        if (Array.isArray(schema)) {
            return schema.map(item => this.cleanSchemaForAnthropic(item));
        }
        const cleaned = {};
        for (const key in schema) {
            if (!Object.prototype.hasOwnProperty.call(schema, key)) {
                continue;
            }
            if (key === 'additionalProperties' ||
                key === '$ref' ||
                key === '$defs' ||
                key === 'positional') {
                continue;
            }
            const value = schema[key];
            if (typeof value === 'object' && value !== null) {
                cleaned[key] = this.cleanSchemaForAnthropic(value);
            }
            else {
                cleaned[key] = value;
            }
        }
        return cleaned;
    }
    transformTools(tools, longTtl = false) {
        if (!tools) {
            return [];
        }
        const transformed = tools.map((tool) => ({
            name: tool.function.name || "",
            description: tool.function.description || "",
            input_schema: this.cleanSchemaForAnthropic(tool.function.parameters),
        }));
        this.handleToolCaching(transformed, longTtl);
        return transformed;
    }
    toBlockArray(content) {
        if (typeof content === "string") {
            return [
                {
                    text: content,
                    type: "text",
                },
            ];
        }
        if (Array.isArray(content)) {
            return content.map((c) => {
                if (typeof c === "string") {
                    return {
                        text: c,
                        type: "text",
                    };
                }
                return c;
            });
        }
        return content;
    }
    combineMessages(messages) {
        if (messages.length <= 1) {
            return messages;
        }
        for (let i = 0; i < messages.length; i++) {
            if (i - 1 >= 0) {
                const currentMessage = messages[i];
                const previousMessage = messages[i - 1];
                if (currentMessage?.role === previousMessage?.role) {
                    previousMessage.content = this.toBlockArray(previousMessage.content);
                    previousMessage.content.push(...this.toBlockArray(currentMessage.content));
                    messages.splice(i, 1);
                    i--;
                }
            }
        }
        return messages;
    }
    cacheLastContent(message, longTtl = false) {
        if (Array.isArray(message.content)) {
            const lastMessage = message.content[message.content.length - 1];
            if (lastMessage.type !== "thinking" &&
                lastMessage.type !== "redacted_thinking") {
                lastMessage.cache_control = longTtl
                    ? { type: "ephemeral", ttl: "1h" }
                    : { type: "ephemeral" };
            }
        }
    }
    handleClearingCache(messages) {
        for (const message of messages) {
            if (Array.isArray(message.content)) {
                for (const content of message.content) {
                    if ("cache_control" in content && content.cache_control) {
                        delete content.cache_control;
                    }
                }
            }
        }
    }
    handleMessageCaching(groupedMessages, longTtl = false) {
        this.handleClearingCache(groupedMessages);
        const lastTwoUserMessages = groupedMessages
            .filter((m) => m.role === "user")
            .slice(-2);
        for (const m of lastTwoUserMessages) {
            if (Array.isArray(m.content)) {
                this.cacheLastContent(m, longTtl);
            }
        }
    }
    tryParse(str) {
        try {
            return JSON.parse(str);
        }
        catch (e) {
            console.error("Invalid JSON from tool call", str);
            return {};
        }
    }
    transformMessages(messages, longTtl = false) {
        const toolCalls = messages.flatMap((msg) => msg.tool_calls || []);
        const claudeMessages = messages
            .filter((msg) => msg.role !== "system")
            .filter((msg) => msg.content || msg.role === "tool")
            .map((msg) => {
            if (msg.role === "tool") {
                const toolCall = toolCalls.find((tc) => tc.id === msg.tool_call_id);
                const toolMessages = [];
                if (!toolCall) {
                    console.log("Tool call not found for message", JSON.stringify(msg, null, 2));
                }
                else {
                    toolMessages.push({
                        role: "assistant",
                        content: [
                            {
                                type: "tool_use",
                                id: msg.tool_call_id,
                                name: toolCall.function.name,
                                input: this.tryParse(toolCall.function.arguments),
                            },
                        ],
                    });
                }
                let toolResultContent;
                if (typeof msg.content === "string") {
                    toolResultContent = msg.content;
                }
                else if (Array.isArray(msg.content)) {
                    toolResultContent = msg.content.map((item) => {
                        if (item.type === "image_url") {
                            const url = item.image_url.url;
                            const isDataUrl = url.startsWith("data:");
                            const isHttpUrl = url.startsWith("http");
                            if (isHttpUrl) {
                                return {
                                    type: "image",
                                    source: {
                                        type: "url",
                                        url,
                                    },
                                };
                            }
                            else {
                                const base64Data = isDataUrl ? url.split(",")[1] : url;
                                const mediaType = isDataUrl
                                    ? url.match(/data:([^;]+);/)?.[1] || "image/jpeg"
                                    : "image/jpeg";
                                return {
                                    type: "image",
                                    source: {
                                        type: "base64",
                                        media_type: mediaType,
                                        data: base64Data,
                                    },
                                };
                            }
                        }
                        else if (item.type === "text") {
                            return { type: "text", text: item.text };
                        }
                        return { type: "text", text: String(item) };
                    });
                }
                else {
                    toolResultContent = String(msg.content);
                }
                toolMessages.push({
                    role: "user",
                    content: [
                        {
                            type: "tool_result",
                            content: toolResultContent,
                            tool_use_id: msg.tool_call_id,
                        },
                    ],
                });
                return toolMessages;
            }
            return {
                content: this.transformContent(msg),
                role: msg.role === "system" ? "assistant" : msg.role,
            };
        })
            .flat();
        const groupedMessages = this.combineMessages(claudeMessages);
        this.handleMessageCaching(groupedMessages, longTtl);
        return groupedMessages;
    }
    transformContent(message) {
        if (typeof message.content === "string") {
            return message.content;
        }
        const transformContextElement = (e) => {
            if (typeof e === "object" && e.type === "text") {
                return { type: "text", text: e.text };
            }
            if (typeof e === "object" && e.type === "image_url") {
                const isUrl = e.image_url.url.startsWith("http");
                if (isUrl) {
                    return {
                        type: "image",
                        source: {
                            type: "url",
                            url: e.image_url.url,
                        },
                    };
                }
                else {
                    return {
                        type: "image",
                        source: {
                            type: "base64",
                            media_type: "image/jpeg",
                            data: e.image_url.url,
                        },
                    };
                }
            }
        };
        if (Array.isArray(message.content)) {
            return message.content.map((e) => transformContextElement(e));
        }
    }
    async createChatCompletion(options) {
        const longTtl = !!options.long_ttl_cache;
        const systemMessage = options.messages
            .filter((msg) => msg.role === "system")
            .map((msg) => msg.content || "")
            .join("\n");
        const claudeMessages = this.transformMessages(options.messages, longTtl);
        const tools = this.transformTools(options.tools, longTtl);
        try {
            const response = await this.client.messages.create({
                model: options.model,
                messages: claudeMessages,
                system: systemMessage
                    ? [
                        {
                            text: systemMessage,
                            cache_control: longTtl ? { type: "ephemeral", ttl: "1h" } : { type: "ephemeral" },
                            type: "text",
                        },
                    ]
                    : undefined,
                max_tokens: options.max_tokens || 8000,
                ...(tools.length && {
                    tool_choice: { type: "auto" },
                    tools,
                }),
            }, { signal: options.signal });
            if (!response.content || !response.content.length) {
                console.log("no content in Anthropic response", response);
            }
            return {
                choices: response.content.map((c) => {
                    if (c.type === "tool_use") {
                        return {
                            message: {
                                role: "assistant",
                                content: "",
                                tool_calls: [
                                    {
                                        id: c.id,
                                        type: "function",
                                        function: {
                                            name: c.name,
                                            arguments: JSON.stringify(c.input),
                                        },
                                    },
                                ],
                            },
                        };
                    }
                    else {
                        return {
                            message: {
                                role: "assistant",
                                content: "text" in c ? c.text : c.type,
                                tool_calls: [],
                            },
                        };
                    }
                }),
                model: options.model,
                usage: response.usage ? {
                    input_tokens: response.usage.input_tokens ?? 0,
                    output_tokens: response.usage.output_tokens ?? 0,
                    prompt_tokens: response.usage.input_tokens ?? 0,
                    completion_tokens: response.usage.output_tokens ?? 0,
                    total_tokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0),
                    cache_creation_input_tokens: response.usage.cache_creation_input_tokens ?? 0,
                    cache_read_input_tokens: response.usage.cache_read_input_tokens ?? 0,
                } : undefined,
                usd_cost: this.calculateCost(options.model, response.usage),
            };
        }
        catch (err) {
            if ("headers" in err && err.headers?.["x-should-retry"] === "true") {
                console.warn("Retrying failed request", err);
                await (0, utils_1.wait)(2500);
                return this.createChatCompletion(options);
            }
            else {
                console.error("Error in createChatCompletion", err);
                throw err;
            }
        }
    }
    pricesPerMillion() {
        return pricing_1.AnthropicTextPricing;
    }
    calculateCost(model, usage) {
        const rawP = this.pricesPerMillion()[model];
        const fallback = pricing_1.AnthropicTextPricing[model];
        const p = rawP || fallback || undefined;
        if (!p)
            return undefined;
        const inputTokens = usage.input_tokens ?? 0;
        const cacheWriteTokens = usage.cache_creation_input_tokens ?? 0;
        const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
        const outputTokens = usage.output_tokens ?? 0;
        const totalInputTokens = inputTokens + cacheWriteTokens + cacheReadTokens;
        const useLongContextTier = totalInputTokens > 200_000 && !!p.input_gt_200k;
        const inputRate = useLongContextTier
            ? p.input_gt_200k
            : p.input;
        const outputRate = useLongContextTier && p.output_gt_200k ? p.output_gt_200k : p.output;
        const cacheWriteRate = (p.cache_write / p.input) * inputRate;
        const cacheReadRate = (p.cache_hit / p.input) * inputRate;
        const nonCachedInputCost = (inputTokens * inputRate) / 1e6;
        const cacheWriteCost = (cacheWriteTokens * cacheWriteRate) / 1e6;
        const cacheReadCost = (cacheReadTokens * cacheReadRate) / 1e6;
        const outputCost = (outputTokens * outputRate) / 1e6;
        return nonCachedInputCost + cacheWriteCost + cacheReadCost + outputCost;
    }
    async getModels(modality) {
        if (modality) {
            if (modality === "completion") {
                return Object.values(types_1.Models.anthropic).map((id) => ({ id }));
            }
            return [];
        }
        const models = await this.client.models.list();
        return models.data.map((m) => ({ id: m.id }));
    }
    async createEmbedding(options) {
        throw new Error("Provider does not support embeddings");
    }
    async createAudioTranscription(options) {
        throw new Error("Anthropic does not support audio transcription");
    }
    getContextLimit(model) {
        const contextLimit = contextLimits_1.ContextLimits[model];
        if (contextLimit === undefined)
            return undefined;
        const pricing = pricing_1.AnthropicTextPricing[model];
        const threshold = pricing && "input_gt_200k" in pricing ? 200_000 : contextLimit;
        return { contextLimit, threshold };
    }
    async createAudioGeneration(options) {
        throw new Error("Anthropic does not support audio generation");
    }
    async createImageGeneration(options) {
        throw new Error("Anthropic does not support image generation");
    }
    async createVideoGeneration(options) {
        throw new Error("Video generation is not supported by the Anthropic provider.");
    }
}
exports.GenericAnthropicClient = GenericAnthropicClient;
//# sourceMappingURL=anthropic.js.map