"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LLMonitorHandler = exports.convertToLLMonitorMessages = void 0;
const llmonitor_1 = __importDefault(require("llmonitor"));
const env_1 = require("@langchain/core/utils/env");
const base_1 = require("@langchain/core/callbacks/base");
// Langchain Helpers
// Input can be either a single message, an array of message, or an array of array of messages (batch requests)
const parseRole = (id) => {
    const roleHint = id[id.length - 1];
    if (roleHint.includes("Human"))
        return "user";
    if (roleHint.includes("System"))
        return "system";
    if (roleHint.includes("AI"))
        return "ai";
    if (roleHint.includes("Function"))
        return "function";
    if (roleHint.includes("Tool"))
        return "tool";
    return "ai";
};
const PARAMS_TO_CAPTURE = [
    "stop",
    "stop_sequences",
    "function_call",
    "functions",
    "tools",
    "tool_choice",
    "response_format",
];
const convertToLLMonitorMessages = (input) => {
    const parseMessage = (raw) => {
        if (typeof raw === "string")
            return raw;
        // sometimes the message is nested in a "message" property
        if ("message" in raw)
            return parseMessage(raw.message);
        // Serialize
        const message = JSON.parse(JSON.stringify(raw));
        try {
            // "id" contains an array describing the constructor, with last item actual schema type
            const role = parseRole(message.id);
            const obj = message.kwargs;
            const text = message.text ?? obj.content;
            return {
                role,
                text,
                ...(obj.additional_kwargs ?? {}),
            };
        }
        catch (e) {
            // if parsing fails, return the original message
            return message.text ?? message;
        }
    };
    if (Array.isArray(input)) {
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore Confuses the compiler
        return input.length === 1
            ? (0, exports.convertToLLMonitorMessages)(input[0])
            : input.map(exports.convertToLLMonitorMessages);
    }
    return parseMessage(input);
};
exports.convertToLLMonitorMessages = convertToLLMonitorMessages;
const parseInput = (rawInput) => {
    if (!rawInput)
        return null;
    const { input, inputs, question } = rawInput;
    if (input)
        return input;
    if (inputs)
        return inputs;
    if (question)
        return question;
    return rawInput;
};
const parseOutput = (rawOutput) => {
    if (!rawOutput)
        return null;
    const { text, output, answer, result } = rawOutput;
    if (text)
        return text;
    if (answer)
        return answer;
    if (output)
        return output;
    if (result)
        return result;
    return rawOutput;
};
const parseExtraAndName = (llm, extraParams, metadata) => {
    const params = {
        ...(extraParams?.invocation_params ?? {}),
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore this is a valid property
        ...(llm?.kwargs ?? {}),
        ...(metadata || {}),
    };
    const { model, model_name, modelName, model_id, userId, userProps, ...rest } = params;
    const name = model || modelName || model_name || model_id || llm.id.at(-1);
    // Filter rest to only include params we want to capture
    const extra = Object.fromEntries(Object.entries(rest).filter(([key]) => PARAMS_TO_CAPTURE.includes(key) ||
        ["string", "number", "boolean"].includes(typeof rest[key])));
    return { name, extra, userId, userProps };
};
/**
 * @deprecated Please use LunaryHandler instead:
 * ```
 * import { LunaryHandler } from "@langchain/community/callbacks/handlers/lunary";
 * ```
 */
class LLMonitorHandler extends base_1.BaseCallbackHandler {
    constructor(fields = {}) {
        super(fields);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "llmonitor_handler"
        });
        Object.defineProperty(this, "monitor", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.monitor = llmonitor_1.default;
        if (fields) {
            const { appId, apiUrl, verbose } = fields;
            this.monitor.init({
                verbose,
                appId: appId ?? (0, env_1.getEnvironmentVariable)("LLMONITOR_APP_ID"),
                apiUrl: apiUrl ?? (0, env_1.getEnvironmentVariable)("LLMONITOR_API_URL"),
            });
        }
    }
    async handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata) {
        const { name, extra, userId, userProps } = parseExtraAndName(llm, extraParams, metadata);
        await this.monitor.trackEvent("llm", "start", {
            runId,
            parentRunId,
            name,
            input: (0, exports.convertToLLMonitorMessages)(prompts),
            extra,
            userId,
            userProps,
            tags,
            runtime: "langchain-js",
        });
    }
    async handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata) {
        const { name, extra, userId, userProps } = parseExtraAndName(llm, extraParams, metadata);
        await this.monitor.trackEvent("llm", "start", {
            runId,
            parentRunId,
            name,
            input: (0, exports.convertToLLMonitorMessages)(messages),
            extra,
            userId,
            userProps,
            tags,
            runtime: "langchain-js",
        });
    }
    async handleLLMEnd(output, runId) {
        const { generations, llmOutput } = output;
        await this.monitor.trackEvent("llm", "end", {
            runId,
            output: (0, exports.convertToLLMonitorMessages)(generations),
            tokensUsage: {
                completion: llmOutput?.tokenUsage?.completionTokens,
                prompt: llmOutput?.tokenUsage?.promptTokens,
            },
        });
    }
    async handleLLMError(error, runId) {
        await this.monitor.trackEvent("llm", "error", {
            runId,
            error,
        });
    }
    async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata) {
        const { agentName, userId, userProps, ...rest } = metadata || {};
        // allow the user to specify an agent name
        const name = agentName || chain.id.at(-1);
        // Attempt to automatically detect if this is an agent or chain
        const runType = agentName || ["AgentExecutor", "PlanAndExecute"].includes(name)
            ? "agent"
            : "chain";
        await this.monitor.trackEvent(runType, "start", {
            runId,
            parentRunId,
            name,
            userId,
            userProps,
            input: parseInput(inputs),
            extra: rest,
            tags,
            runtime: "langchain-js",
        });
    }
    async handleChainEnd(outputs, runId) {
        await this.monitor.trackEvent("chain", "end", {
            runId,
            output: parseOutput(outputs),
        });
    }
    async handleChainError(error, runId) {
        await this.monitor.trackEvent("chain", "error", {
            runId,
            error,
        });
    }
    async handleToolStart(tool, input, runId, parentRunId, tags, metadata) {
        const { toolName, userId, userProps, ...rest } = metadata || {};
        const name = toolName || tool.id.at(-1);
        await this.monitor.trackEvent("tool", "start", {
            runId,
            parentRunId,
            name,
            userId,
            userProps,
            input,
            extra: rest,
            tags,
            runtime: "langchain-js",
        });
    }
    async handleToolEnd(output, runId) {
        await this.monitor.trackEvent("tool", "end", {
            runId,
            output,
        });
    }
    async handleToolError(error, runId) {
        await this.monitor.trackEvent("tool", "error", {
            runId,
            error,
        });
    }
}
exports.LLMonitorHandler = LLMonitorHandler;
