"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractImageInfo = extractImageInfo;
exports.convertToConverseMessages = convertToConverseMessages;
exports.isBedrockTool = isBedrockTool;
exports.convertToConverseTools = convertToConverseTools;
exports.convertToBedrockToolChoice = convertToBedrockToolChoice;
exports.convertConverseMessageToLangChainMessage = convertConverseMessageToLangChainMessage;
exports.handleConverseStreamContentBlockDelta = handleConverseStreamContentBlockDelta;
exports.handleConverseStreamContentBlockStart = handleConverseStreamContentBlockStart;
exports.handleConverseStreamMetadata = handleConverseStreamMetadata;
exports.bedrockReasoningDeltaToLangchainPartialReasoningBlock = bedrockReasoningDeltaToLangchainPartialReasoningBlock;
exports.bedrockReasoningBlockToLangchainReasoningBlock = bedrockReasoningBlockToLangchainReasoningBlock;
exports.langchainReasoningBlockToBedrockReasoningBlock = langchainReasoningBlockToBedrockReasoningBlock;
exports.concatenateLangchainReasoningBlocks = concatenateLangchainReasoningBlocks;
exports.supportedToolChoiceValuesForModel = supportedToolChoiceValuesForModel;
const messages_1 = require("@langchain/core/messages");
const base_1 = require("@langchain/core/language_models/base");
const function_calling_1 = require("@langchain/core/utils/function_calling");
const outputs_1 = require("@langchain/core/outputs");
const types_1 = require("@langchain/core/utils/types");
const json_schema_1 = require("@langchain/core/utils/json_schema");
function isDefaultCachePoint(block) {
    return Boolean(typeof block === "object" &&
        block !== null &&
        "cachePoint" in block &&
        block.cachePoint &&
        typeof block.cachePoint === "object" &&
        block.cachePoint !== null &&
        "type" in block.cachePoint &&
        block.cachePoint.type === "default");
}
const standardContentBlockConverter = {
    providerName: "ChatBedrockConverse",
    fromStandardTextBlock(block) {
        return {
            text: block.text,
        };
    },
    fromStandardImageBlock(block) {
        let format;
        if (block.source_type === "url") {
            const parsedData = (0, messages_1.parseBase64DataUrl)({
                dataUrl: block.url,
                asTypedArray: true,
            });
            if (parsedData) {
                const parsedMimeType = (0, messages_1.parseMimeType)(parsedData.mime_type);
                format = parsedMimeType.type;
                return {
                    image: {
                        format,
                        source: {
                            bytes: parsedData.data,
                        },
                    },
                };
            }
            else {
                throw new Error("Only base64 data URLs are supported for image blocks with source type 'url' with ChatBedrockConverse.");
            }
        }
        else if (block.source_type === "base64") {
            if (block.mime_type) {
                const parsedMimeType = (0, messages_1.parseMimeType)(block.mime_type);
                format = parsedMimeType.subtype;
            }
            if (format && !["gif", "jpeg", "png", "webp"].includes(format)) {
                throw new Error(`Unsupported image mime type: "${block.mime_type}" ChatBedrockConverse only supports "image/gif", "image/jpeg", "image/png", and "image/webp" formats.`);
            }
            return {
                image: {
                    format,
                    source: {
                        bytes: Uint8Array.from(atob(block.data), (c) => c.charCodeAt(0)),
                    },
                },
            };
        }
        else if (block.source_type === "id") {
            throw new Error("Image source type 'id' not supported with ChatBedrockConverse.");
        }
        else {
            throw new Error(`Unsupported image source type: "${block.source_type}" with ChatBedrockConverse.`);
        }
    },
    fromStandardFileBlock(block) {
        const mimeTypeToDocumentFormat = {
            "text/csv": "csv",
            "application/msword": "doc",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx",
            "text/html": "html",
            "text/markdown": "md",
            "application/pdf": "pdf",
            "text/plain": "txt",
            "application/vnd.ms-excel": "xls",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "xlsx",
        };
        const name = (block.metadata?.name ??
            block.metadata?.filename ??
            block.metadata?.title);
        if (block.source_type === "text") {
            return {
                document: {
                    name,
                    format: "txt",
                    source: {
                        bytes: new TextEncoder().encode(block.text),
                    },
                },
            };
        }
        if (block.source_type === "url") {
            const parsedData = (0, messages_1.parseBase64DataUrl)({
                dataUrl: block.url,
                asTypedArray: true,
            });
            if (parsedData) {
                const parsedMimeType = (0, messages_1.parseMimeType)(parsedData.mime_type ?? block.mime_type);
                const mimeType = `${parsedMimeType.type}/${parsedMimeType.subtype}`;
                const format = mimeTypeToDocumentFormat[mimeType];
                return {
                    document: {
                        name,
                        format,
                        source: {
                            bytes: parsedData.data,
                        },
                    },
                };
            }
            throw new Error("Only base64 data URLs are supported for file blocks with source type 'url' with ChatBedrockConverse.");
        }
        if (block.source_type === "base64") {
            let format;
            if (block.mime_type) {
                const parsedMimeType = (0, messages_1.parseMimeType)(block.mime_type);
                const mimeType = `${parsedMimeType.type}/${parsedMimeType.subtype}`;
                format = mimeTypeToDocumentFormat[mimeType];
                if (format === undefined) {
                    throw new Error(`Unsupported file mime type: "${block.mime_type}" ChatBedrockConverse only supports ${Object.keys(mimeTypeToDocumentFormat).join(", ")} formats.`);
                }
            }
            return {
                document: {
                    name,
                    format,
                    source: {
                        bytes: Uint8Array.from(atob(block.data), (c) => c.charCodeAt(0)),
                    },
                },
            };
        }
        if (block.source_type === "id") {
            throw new Error("File source type 'id' not supported with ChatBedrockConverse.");
        }
        throw new Error(`Unsupported file source type: "${block.source_type}" with ChatBedrockConverse.`);
    },
};
function extractImageInfo(base64) {
    // Extract the format from the base64 string
    const formatMatch = base64.match(/^data:image\/(\w+);base64,/);
    let format;
    if (formatMatch) {
        const extractedFormat = formatMatch[1].toLowerCase();
        if (["gif", "jpeg", "png", "webp"].includes(extractedFormat)) {
            format = extractedFormat;
        }
    }
    // Remove the data URL prefix if present
    const base64Data = base64.replace(/^data:image\/\w+;base64,/, "");
    // Convert base64 to Uint8Array
    const binaryString = atob(base64Data);
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i += 1) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return {
        image: {
            format,
            source: {
                bytes,
            },
        },
    };
}
function convertLangChainContentBlockToConverseContentBlock({ block, onUnknown = "throw", }) {
    if (typeof block === "string") {
        return { text: block };
    }
    if ((0, messages_1.isDataContentBlock)(block)) {
        return (0, messages_1.convertToProviderContentBlock)(block, standardContentBlockConverter);
    }
    if (block.type === "text") {
        return { text: block.text };
    }
    if (block.type === "image_url") {
        return extractImageInfo(typeof block.image_url === "string"
            ? block.image_url
            : block.image_url.url);
    }
    if (block.type === "document" && block.document !== undefined) {
        return {
            document: block.document,
        };
    }
    if (block.type === "image" && block.image !== undefined) {
        return {
            image: block.image,
        };
    }
    if (isDefaultCachePoint(block)) {
        return {
            cachePoint: {
                type: "default",
            },
        };
    }
    if (onUnknown === "throw") {
        throw new Error(`Unsupported content block type: ${block.type}`);
    }
    else {
        return block;
    }
}
function convertSystemMessageToConverseMessage(msg) {
    if (typeof msg.content === "string") {
        return [{ text: msg.content }];
    }
    else if (Array.isArray(msg.content) && msg.content.length > 0) {
        const contentBlocks = [];
        for (const block of msg.content) {
            if (block.type === "text" && typeof block.text === "string") {
                contentBlocks.push({
                    text: block.text,
                });
            }
            else if (isDefaultCachePoint(block)) {
                contentBlocks.push({
                    cachePoint: {
                        type: "default",
                    },
                });
            }
            else
                break;
        }
        if (msg.content.length === contentBlocks.length)
            return contentBlocks;
    }
    throw new Error("System message content must be either a string, or an array of text blocks, optionally including a cache point.");
}
function convertAIMessageToConverseMessage(msg) {
    const assistantMsg = {
        role: "assistant",
        content: [],
    };
    if (typeof msg.content === "string" && msg.content !== "") {
        assistantMsg.content?.push({
            text: msg.content,
        });
    }
    else if (Array.isArray(msg.content)) {
        const concatenatedBlocks = concatenateLangchainReasoningBlocks(msg.content);
        const contentBlocks = [];
        concatenatedBlocks.forEach((block) => {
            if (block.type === "text" && block.text !== "") {
                // Merge whitespace/newlines with previous text blocks to avoid validation errors.
                const cleanedText = block.text?.replace(/\n/g, "").trim();
                if (cleanedText === "") {
                    if (contentBlocks.length > 0) {
                        const mergedTextContent = `${contentBlocks[contentBlocks.length - 1].text}${block.text}`;
                        contentBlocks[contentBlocks.length - 1].text = mergedTextContent;
                    }
                }
                else {
                    contentBlocks.push({
                        text: block.text,
                    });
                }
            }
            else if (block.type === "reasoning_content") {
                contentBlocks.push({
                    reasoningContent: langchainReasoningBlockToBedrockReasoningBlock(block),
                });
            }
            else if (isDefaultCachePoint(block)) {
                contentBlocks.push({
                    cachePoint: {
                        type: "default",
                    },
                });
            }
            else {
                const blockValues = Object.fromEntries(Object.entries(block).filter(([key]) => key !== "type"));
                throw new Error(`Unsupported content block type: ${block.type} with content of ${JSON.stringify(blockValues, null, 2)}`);
            }
        });
        assistantMsg.content = [
            ...(assistantMsg.content ? assistantMsg.content : []),
            ...contentBlocks,
        ];
    }
    // Important: this must be placed after any reasoning content blocks, else claude models will return an error.
    if (msg.tool_calls && msg.tool_calls.length) {
        assistantMsg.content = [
            ...(assistantMsg.content ? assistantMsg.content : []),
            ...msg.tool_calls.map((tc) => ({
                toolUse: {
                    toolUseId: tc.id,
                    name: tc.name,
                    input: tc.args,
                },
            })),
        ];
    }
    return assistantMsg;
}
function convertHumanMessageToConverseMessage(msg) {
    if (msg.content === "") {
        throw new Error(`Invalid message content: empty string. '${msg.getType()}' must contain non-empty content.`);
    }
    const content = Array.isArray(msg.content)
        ? msg.content.map((c) => convertLangChainContentBlockToConverseContentBlock({
            block: c,
            onUnknown: "throw",
        }))
        : [
            convertLangChainContentBlockToConverseContentBlock({
                block: msg.content,
                onUnknown: "throw",
            }),
        ];
    return {
        role: "user",
        content,
    };
}
function convertToolMessageToConverseMessage(msg) {
    const castMsg = msg;
    if (typeof castMsg.content === "string") {
        return {
            // Tool use messages are always from the user
            role: "user",
            content: [
                {
                    toolResult: {
                        toolUseId: castMsg.tool_call_id,
                        content: [
                            {
                                text: castMsg.content,
                            },
                        ],
                    },
                },
            ],
        };
    }
    else {
        return {
            // Tool use messages are always from the user
            role: "user",
            content: [
                {
                    toolResult: {
                        toolUseId: castMsg.tool_call_id,
                        content: msg.content.map((c) => {
                            const converted = convertLangChainContentBlockToConverseContentBlock({
                                block: c,
                                onUnknown: "returnUnmodified",
                            });
                            if (converted !== c) {
                                return converted;
                            }
                            return { json: c };
                        }),
                    },
                },
            ],
        };
    }
}
function convertToConverseMessages(messages) {
    const converseSystem = messages
        .filter((msg) => msg.getType() === "system")
        .flatMap((msg) => convertSystemMessageToConverseMessage(msg));
    const converseMessages = messages
        .filter((msg) => msg.getType() !== "system")
        .map((msg) => {
        if (msg.getType() === "ai") {
            return convertAIMessageToConverseMessage(msg);
        }
        else if (msg.getType() === "human" || msg.getType() === "generic") {
            return convertHumanMessageToConverseMessage(msg);
        }
        else if (msg.getType() === "tool") {
            return convertToolMessageToConverseMessage(msg);
        }
        else {
            throw new Error(`Unsupported message type: ${msg.getType()}`);
        }
    });
    // Combine consecutive user tool result messages into a single message
    const combinedConverseMessages = converseMessages.reduce((acc, curr) => {
        const lastMessage = acc[acc.length - 1];
        if (lastMessage &&
            lastMessage.role === "user" &&
            lastMessage.content?.some((c) => "toolResult" in c) &&
            curr.role === "user" &&
            curr.content?.some((c) => "toolResult" in c)) {
            lastMessage.content = lastMessage.content.concat(curr.content);
        }
        else {
            acc.push(curr);
        }
        return acc;
    }, []);
    return { converseMessages: combinedConverseMessages, converseSystem };
}
function isBedrockTool(tool) {
    if (typeof tool === "object" && tool && "toolSpec" in tool) {
        return true;
    }
    return false;
}
function convertToConverseTools(tools) {
    if (tools.every(base_1.isOpenAITool)) {
        return tools.map((tool) => ({
            toolSpec: {
                name: tool.function.name,
                description: tool.function.description,
                inputSchema: {
                    json: tool.function.parameters,
                },
            },
        }));
    }
    else if (tools.every(function_calling_1.isLangChainTool)) {
        return tools.map((tool) => ({
            toolSpec: {
                name: tool.name,
                description: tool.description,
                inputSchema: {
                    json: ((0, types_1.isInteropZodSchema)(tool.schema)
                        ? (0, json_schema_1.toJsonSchema)(tool.schema)
                        : tool.schema),
                },
            },
        }));
    }
    else if (tools.every(isBedrockTool)) {
        return tools;
    }
    throw new Error("Invalid tools passed. Must be an array of StructuredToolInterface, ToolDefinition, or BedrockTool.");
}
function convertToBedrockToolChoice(toolChoice, tools, fields) {
    const supportsToolChoiceValues = fields.supportsToolChoiceValues ?? [];
    let bedrockToolChoice;
    if (typeof toolChoice === "string") {
        switch (toolChoice) {
            case "any":
                bedrockToolChoice = {
                    any: {},
                };
                break;
            case "auto":
                bedrockToolChoice = {
                    auto: {},
                };
                break;
            default: {
                const foundTool = tools.find((tool) => tool.toolSpec?.name === toolChoice);
                if (!foundTool) {
                    throw new Error(`Tool with name ${toolChoice} not found in tools list.`);
                }
                bedrockToolChoice = {
                    tool: {
                        name: toolChoice,
                    },
                };
            }
        }
    }
    else {
        bedrockToolChoice = toolChoice;
    }
    const toolChoiceType = Object.keys(bedrockToolChoice)[0];
    if (!supportsToolChoiceValues.includes(toolChoiceType)) {
        let supportedTxt = "";
        if (supportsToolChoiceValues.length) {
            supportedTxt =
                `Model ${fields.model} does not currently support 'tool_choice' ` +
                    `of type ${toolChoiceType}. The following 'tool_choice' types ` +
                    `are supported: ${supportsToolChoiceValues.join(", ")}.`;
        }
        else {
            supportedTxt = `Model ${fields.model} does not currently support 'tool_choice'.`;
        }
        throw new Error(`${supportedTxt} Please see` +
            "https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolChoice.html" +
            "for the latest documentation on models that support tool choice.");
    }
    return bedrockToolChoice;
}
function convertConverseMessageToLangChainMessage(message, responseMetadata) {
    if (!message.content) {
        throw new Error("No message content found in response.");
    }
    if (message.role !== "assistant") {
        throw new Error(`Unsupported message role received in ChatBedrockConverse response: ${message.role}`);
    }
    let requestId;
    if ("$metadata" in responseMetadata &&
        responseMetadata.$metadata &&
        typeof responseMetadata.$metadata === "object" &&
        "requestId" in responseMetadata.$metadata) {
        requestId = responseMetadata.$metadata.requestId;
    }
    let tokenUsage;
    if (responseMetadata.usage) {
        const input_tokens = responseMetadata.usage.inputTokens ?? 0;
        const output_tokens = responseMetadata.usage.outputTokens ?? 0;
        tokenUsage = {
            input_tokens,
            output_tokens,
            total_tokens: responseMetadata.usage.totalTokens ?? input_tokens + output_tokens,
        };
    }
    if (message.content?.length === 1 &&
        "text" in message.content[0] &&
        typeof message.content[0].text === "string") {
        return new messages_1.AIMessage({
            content: message.content[0].text,
            response_metadata: responseMetadata,
            usage_metadata: tokenUsage,
            id: requestId,
        });
    }
    else {
        const toolCalls = [];
        const content = [];
        message.content.forEach((c) => {
            if ("toolUse" in c &&
                c.toolUse &&
                c.toolUse.name &&
                c.toolUse.input &&
                typeof c.toolUse.input === "object") {
                toolCalls.push({
                    id: c.toolUse.toolUseId,
                    name: c.toolUse.name,
                    args: c.toolUse.input,
                    type: "tool_call",
                });
            }
            else if ("text" in c && typeof c.text === "string") {
                content.push({ type: "text", text: c.text });
            }
            else if ("reasoningContent" in c) {
                content.push(bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent));
            }
            else {
                content.push(c);
            }
        });
        return new messages_1.AIMessage({
            content: content.length ? content : "",
            tool_calls: toolCalls.length ? toolCalls : undefined,
            response_metadata: responseMetadata,
            usage_metadata: tokenUsage,
            id: requestId,
        });
    }
}
function handleConverseStreamContentBlockDelta(contentBlockDelta) {
    if (!contentBlockDelta.delta) {
        throw new Error("No delta found in content block.");
    }
    if (typeof contentBlockDelta.delta.text === "string") {
        return new outputs_1.ChatGenerationChunk({
            text: contentBlockDelta.delta.text,
            message: new messages_1.AIMessageChunk({
                content: contentBlockDelta.delta.text,
            }),
        });
    }
    else if (contentBlockDelta.delta.toolUse) {
        const index = contentBlockDelta.contentBlockIndex;
        return new outputs_1.ChatGenerationChunk({
            text: "",
            message: new messages_1.AIMessageChunk({
                content: "",
                tool_call_chunks: [
                    {
                        args: contentBlockDelta.delta.toolUse.input,
                        index,
                        type: "tool_call_chunk",
                    },
                ],
            }),
        });
    }
    else if (contentBlockDelta.delta.reasoningContent) {
        return new outputs_1.ChatGenerationChunk({
            text: "",
            message: new messages_1.AIMessageChunk({
                content: [
                    bedrockReasoningDeltaToLangchainPartialReasoningBlock(contentBlockDelta.delta.reasoningContent),
                ],
            }),
        });
    }
    else {
        throw new Error(`Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`);
    }
}
function handleConverseStreamContentBlockStart(contentBlockStart) {
    const index = contentBlockStart.contentBlockIndex;
    if (contentBlockStart.start?.toolUse) {
        return new outputs_1.ChatGenerationChunk({
            text: "",
            message: new messages_1.AIMessageChunk({
                content: "",
                tool_call_chunks: [
                    {
                        name: contentBlockStart.start.toolUse.name,
                        id: contentBlockStart.start.toolUse.toolUseId,
                        index,
                        type: "tool_call_chunk",
                    },
                ],
            }),
        });
    }
    throw new Error("Unsupported content block start event.");
}
function handleConverseStreamMetadata(metadata, extra) {
    const inputTokens = metadata.usage?.inputTokens ?? 0;
    const outputTokens = metadata.usage?.outputTokens ?? 0;
    const usage_metadata = {
        input_tokens: inputTokens,
        output_tokens: outputTokens,
        total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,
    };
    return new outputs_1.ChatGenerationChunk({
        text: "",
        message: new messages_1.AIMessageChunk({
            content: "",
            usage_metadata: extra.streamUsage ? usage_metadata : undefined,
            response_metadata: {
                // Use the same key as returned from the Converse API
                metadata,
            },
        }),
    });
}
function bedrockReasoningDeltaToLangchainPartialReasoningBlock(reasoningContent) {
    const { text, redactedContent, signature } = reasoningContent;
    if (typeof text === "string") {
        return {
            type: "reasoning_content",
            reasoningText: { text },
        };
    }
    if (signature) {
        return {
            type: "reasoning_content",
            reasoningText: { signature },
        };
    }
    if (redactedContent) {
        return {
            type: "reasoning_content",
            redactedContent: Buffer.from(redactedContent).toString("base64"),
        };
    }
    throw new Error("Invalid reasoning content");
}
function bedrockReasoningBlockToLangchainReasoningBlock(reasoningContent) {
    const { reasoningText, redactedContent } = reasoningContent;
    if (reasoningText) {
        return {
            type: "reasoning_content",
            reasoningText: reasoningText,
        };
    }
    if (redactedContent) {
        return {
            type: "reasoning_content",
            redactedContent: Buffer.from(redactedContent).toString("base64"),
        };
    }
    throw new Error("Invalid reasoning content");
}
function langchainReasoningBlockToBedrockReasoningBlock(content) {
    if (content.type !== "reasoning_content") {
        throw new Error("Invalid reasoning content");
    }
    if ("reasoningText" in content) {
        return {
            reasoningText: content.reasoningText,
        };
    }
    if ("redactedContent" in content) {
        return {
            redactedContent: Buffer.from(content.redactedContent, "base64"),
        };
    }
    throw new Error("Invalid reasoning content");
}
function concatenateLangchainReasoningBlocks(content) {
    const concatenatedBlocks = [];
    let concatenatedBlock = {};
    for (const block of content) {
        if (block.type !== "reasoning_content") {
            // if it's some other block type, end the current block, but keep it so we preserve order
            if (Object.keys(concatenatedBlock).length > 0) {
                concatenatedBlocks.push(concatenatedBlock);
                concatenatedBlock = {};
            }
            concatenatedBlocks.push(block);
            continue;
        }
        // non-redacted block
        if ("reasoningText" in block && typeof block.reasoningText === "object") {
            if ("redactedContent" in concatenatedBlock) {
                // new type of block, so end the previous one
                concatenatedBlocks.push(concatenatedBlock);
                concatenatedBlock = {};
            }
            const { text, signature } = block.reasoningText;
            const { text: prevText, signature: prevSignature } = ("reasoningText" in concatenatedBlock
                ? concatenatedBlock.reasoningText
                : {});
            concatenatedBlock = {
                type: "reasoning_content",
                reasoningText: {
                    ...(concatenatedBlock
                        .reasoningText ?? {}),
                    ...(prevText !== undefined || text !== undefined
                        ? { text: (prevText ?? "") + (text ?? "") }
                        : {}),
                    ...(prevSignature !== undefined || signature !== undefined
                        ? { signature: (prevSignature ?? "") + (signature ?? "") }
                        : {}),
                },
            };
            // if a partial block chunk has a signature, the next one will begin a new reasoning block.
            // full blocks always have signatures, so we start one now, anyway
            if ("signature" in block.reasoningText) {
                concatenatedBlocks.push(concatenatedBlock);
                concatenatedBlock = {};
            }
        }
        if ("redactedContent" in block) {
            if ("reasoningText" in concatenatedBlock) {
                // New type of block, so end the previous one. We should't really hit
                // this, as we'll have created a new block upon encountering the
                // signature above, but better safe than sorry.
                concatenatedBlocks.push(concatenatedBlock);
                concatenatedBlock = {};
            }
            const { redactedContent } = block;
            const prevRedactedContent = ("redactedContent" in concatenatedBlock
                ? concatenatedBlock.redactedContent
                : "");
            concatenatedBlock = {
                type: "reasoning_content",
                redactedContent: prevRedactedContent + redactedContent,
            };
        }
    }
    if (Object.keys(concatenatedBlock).length > 0) {
        concatenatedBlocks.push(concatenatedBlock);
    }
    return concatenatedBlocks;
}
function supportedToolChoiceValuesForModel(model) {
    if (model.includes("claude-3") ||
        model.includes("claude-4") ||
        model.includes("claude-opus-4") ||
        model.includes("claude-sonnet-4")) {
        return ["auto", "any", "tool"];
    }
    if (model.includes("mistral-large")) {
        return ["auto", "any"];
    }
    return undefined;
}
