var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { ToolMessage } from "@langchain/core/messages";
import { isGraphInterrupt } from "@langchain/langgraph";
import { createMiddleware } from "langchain";
import { logger } from "adminforth";
import { formatApiBasedToolCall, } from "../../tools/apiBasedTools.js";
import { createToolCallTracker, } from "../../domain/toolCallEvents.js";
import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "../../tools/index.js";
import { createApiTool } from "../../tools/apiTool.js";
import { isAbortError } from "../../shared/errors.js";
function getEnabledApiToolNames(messages) {
    const enabledToolNames = new Set();
    for (const message of messages) {
        if (!ToolMessage.isInstance(message) || message.name !== "fetch_tool_schema") {
            continue;
        }
        const content = typeof message.content === "string"
            ? message.content
            : Array.isArray(message.content)
                ? message.content
                    .map((block) => typeof block === "string"
                    ? block
                    : "text" in block
                        ? block.text
                        : "")
                    .join("")
                : "";
        try {
            const parsed = JSON.parse(content);
            if (parsed.status === 200 && parsed.name) {
                enabledToolNames.add(parsed.name);
            }
        }
        catch (_a) { }
    }
    return enabledToolNames;
}
export function createApiBasedToolsMiddleware(apiBasedTools, adminforth) {
    const alwaysAvailableApiToolNames = new Set(ALWAYS_AVAILABLE_API_TOOL_NAMES);
    const dynamicTools = Object.fromEntries(Object.entries(apiBasedTools).map(([toolName, apiBasedTool]) => [
        toolName,
        createApiTool(toolName, apiBasedTool),
    ]));
    return createMiddleware({
        name: "ApiBasedToolsMiddleware",
        wrapModelCall(request, handler) {
            return __awaiter(this, void 0, void 0, function* () {
                const enabledApiToolNames = getEnabledApiToolNames(request.state.messages);
                const tools = [...enabledApiToolNames]
                    .filter((toolName) => !alwaysAvailableApiToolNames.has(toolName))
                    .map((toolName) => dynamicTools[toolName]);
                const availableTools = [...request.tools, ...tools];
                return handler(Object.assign(Object.assign({}, request), { tools: availableTools }));
            });
        },
        wrapToolCall(request, handler) {
            return __awaiter(this, void 0, void 0, function* () {
                var _a, _b;
                const startedAt = Date.now();
                if (!request.toolCall.id) {
                    throw new Error(`Tool call "${request.toolCall.name}" has no id.`);
                }
                const toolCallId = request.toolCall.id;
                const { adminUser, abortSignal, emit, sequenceDebugSink, userTimeZone } = request.runtime.context;
                const emitToolCall = (event) => {
                    sequenceDebugSink.handleToolCallEvent(event);
                    void (emit === null || emit === void 0 ? void 0 : emit({
                        type: "tool-call",
                        data: event,
                    }));
                };
                const toolArgs = ((_a = request.toolCall.args) !== null && _a !== void 0 ? _a : {});
                let toolInfo;
                if (request.toolCall.name === "fetch_skill") {
                    toolInfo = `Load ${toolArgs.skillName.split("_").join(" ")} skill`;
                }
                else if (request.toolCall.name === "fetch_tool_schema") {
                    toolInfo = `Load ${toolArgs.toolName.split("_").join(" ")} tool `;
                }
                else {
                    toolInfo = yield formatApiBasedToolCall({
                        adminforth,
                        adminUser,
                        inputs: toolArgs,
                        toolName: request.toolCall.name,
                        userTimeZone,
                    });
                }
                const toolCallTracker = createToolCallTracker({
                    emit: emitToolCall,
                    toolCallId,
                    toolName: request.toolCall.name,
                    toolInfo,
                    input: toolArgs,
                    startedAt,
                });
                toolCallTracker.start();
                try {
                    const result = getEnabledApiToolNames(request.state.messages).has(request.toolCall.name)
                        ? yield handler(Object.assign(Object.assign({}, request), { tool: dynamicTools[request.toolCall.name] }))
                        : yield handler(request);
                    toolCallTracker.finishSuccess(result);
                    return result;
                }
                catch (error) {
                    if (isGraphInterrupt(error)
                        || (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted)
                        || isAbortError(error)) {
                        throw error;
                    }
                    const message = error instanceof Error ? error.message : String(error);
                    logger.error(`Error calling tool "${request.toolCall.name}": ${error instanceof Error ? (_b = error.stack) !== null && _b !== void 0 ? _b : error.message : String(error)}`);
                    toolCallTracker.finishError(`Error: ${message}`);
                    return new ToolMessage({
                        name: request.toolCall.name,
                        tool_call_id: toolCallId,
                        status: "error",
                        content: `Error: ${message}`,
                    });
                }
            });
        },
    });
}
