/**
 * Shared helpers for the LangChain callback integration.
 */
export function nanoTimestamp() {
    return (BigInt(Date.now()) * 1000000n).toString();
}
export function extractModelName(serialized, extraParams) {
    const serKwargs = serialized["kwargs"];
    if (serKwargs != null && typeof serKwargs === "object") {
        const kw = serKwargs;
        if (typeof kw["model_name"] === "string")
            return kw["model_name"];
        if (typeof kw["model"] === "string")
            return kw["model"];
    }
    if (typeof serialized["name"] === "string")
        return serialized["name"];
    const inv = extraParams?.["invocation_params"];
    if (inv != null && typeof inv === "object") {
        const ip = inv;
        if (typeof ip["model_name"] === "string")
            return ip["model_name"];
        if (typeof ip["model"] === "string")
            return ip["model"];
    }
    return null;
}
export function extractModelParameters(extraParams) {
    const inv = extraParams?.["invocation_params"];
    if (inv == null || typeof inv !== "object")
        return {};
    const ip = inv;
    const params = {};
    const keys = [
        "temperature",
        "max_tokens",
        "top_p",
        "top_k",
        "frequency_penalty",
        "presence_penalty",
        "seed",
    ];
    for (const key of keys) {
        const value = ip[key];
        if (value != null) {
            params[key] = value;
        }
    }
    return params;
}
export function normalizeMessages(messages) {
    const result = [];
    for (const group of messages) {
        for (const msg of group) {
            const type = msg._getType();
            switch (type) {
                case "human":
                    result.push({ role: "user", content: msg.content });
                    break;
                case "system":
                    result.push({ role: "system", content: msg.content });
                    break;
                case "ai": {
                    const entry = {
                        role: "assistant",
                        content: msg.content,
                    };
                    const toolCalls = msg["tool_calls"] ??
                        msg.additional_kwargs?.["tool_calls"];
                    if (toolCalls) {
                        entry["tool_calls"] = toolCalls;
                    }
                    result.push(entry);
                    break;
                }
                case "tool":
                    result.push({
                        role: "tool",
                        content: msg.content,
                        tool_call_id: msg["tool_call_id"],
                    });
                    break;
                case "function":
                    result.push({
                        role: "function",
                        content: msg.content,
                        name: msg["name"],
                    });
                    break;
                case "chat":
                    result.push({
                        role: msg["role"],
                        content: msg.content,
                    });
                    break;
                default:
                    result.push({ role: "unknown", content: String(msg.content) });
            }
        }
    }
    return result;
}
function isBaseMessage(value) {
    return (value != null &&
        typeof value === "object" &&
        typeof value._getType === "function");
}
function normalizeChainMessageItem(item) {
    if (isBaseMessage(item)) {
        return normalizeMessages([[item]])[0];
    }
    if (Array.isArray(item) &&
        item.length === 2 &&
        typeof item[0] === "string") {
        const aliases = { human: "user", ai: "assistant" };
        const raw = item[0].toLowerCase();
        return { role: aliases[raw] ?? raw, content: item[1] };
    }
    return item;
}
function isNormalizableArray(value) {
    if (!Array.isArray(value) || value.length === 0)
        return false;
    return value.some((m) => isBaseMessage(m) ||
        (Array.isArray(m) && m.length === 2 && typeof m[0] === "string"));
}
/**
 * Normalize a chain payload's message arrays to role-typed dicts.
 *
 * Handles raw BaseMessage objects (from LangGraph state) and
 * `[role, content]` tuples (the form accepted by agent.invoke). Scans every
 * top-level key — common variants are `messages`, `output`, `outputs`,
 * `input`, `inputs` — so prompt template / RunnableSequence outputs render
 * with structured roles instead of the lc-serialized blob.
 */
export function maybeNormalizeChainMessages(payload) {
    if (payload == null || typeof payload !== "object" || Array.isArray(payload)) {
        return payload;
    }
    const obj = payload;
    let changed = false;
    const next = { ...obj };
    for (const [key, value] of Object.entries(obj)) {
        if (isNormalizableArray(value)) {
            next[key] = value.map(normalizeChainMessageItem);
            changed = true;
        }
        else if (isBaseMessage(value)) {
            next[key] = normalizeChainMessageItem(value);
            changed = true;
        }
    }
    return changed ? next : payload;
}
/**
 * Wrap chain inputs/outputs into a dict suitable for the span normalizer.
 *
 * LangChain hands callbacks raw BaseMessage objects (or lists thereof) for
 * chains like RunnableSequence wrapping a chat model. Without coercion they
 * get stored as `{outputs: <AIMessage>}` and serialized via the default
 * stringifier, producing an unreadable repr in the trace. Wrapping them as
 * `{messages: [...]}` routes through the normal role/tool_calls path.
 */
export function coerceChainPayload(payload) {
    // Check BaseMessage first — a BaseMessage IS an object, so the plain-dict
    // branch below would otherwise swallow it and ship the raw lc-serialized
    // form (via toJSON) instead of normalized {role, content, tool_calls}.
    if (isBaseMessage(payload)) {
        return { messages: [payload] };
    }
    if (Array.isArray(payload) && payload.length > 0 && payload.every(isBaseMessage)) {
        return { messages: payload };
    }
    if (payload != null && typeof payload === "object" && !Array.isArray(payload)) {
        return payload;
    }
    return { outputs: payload };
}
function messageId(msg) {
    if (msg == null || typeof msg !== "object")
        return undefined;
    const m = msg;
    const id = m["id"];
    return typeof id === "string" ? id : undefined;
}
/**
 * Return outputs with input messages stripped from outputs.messages.
 *
 * LangGraph's root on_chain_end receives the full merged state, so the
 * input turns reappear in outputs.messages. Use message ids when every
 * input has one (handles RemoveMessage correctly); otherwise fall back to
 * positional trimming, which is safe under the append-only add_messages
 * reducer LangGraph uses by default.
 */
export function rootOutputDelta(inputs, outputs) {
    if (inputs == null || typeof inputs !== "object" || Array.isArray(inputs)) {
        return outputs;
    }
    const inMsgs = inputs["messages"];
    const outMsgs = outputs["messages"];
    if (!Array.isArray(inMsgs) || !Array.isArray(outMsgs) || outMsgs.length === 0) {
        return outputs;
    }
    const inIds = inMsgs.map(messageId);
    if (inIds.length > 0 && inIds.every((id) => id != null)) {
        const inputIdSet = new Set(inIds);
        const delta = outMsgs.filter((m) => {
            const id = messageId(m);
            return id == null || !inputIdSet.has(id);
        });
        return { ...outputs, messages: delta };
    }
    if (outMsgs.length >= inMsgs.length) {
        return { ...outputs, messages: outMsgs.slice(inMsgs.length) };
    }
    return outputs;
}
export function extractAssistantToolCalls(message) {
    if (message == null || typeof message !== "object")
        return null;
    const msg = message;
    const additionalKwargs = msg["additional_kwargs"];
    if (additionalKwargs != null && typeof additionalKwargs === "object") {
        const fromKwargs = additionalKwargs["tool_calls"];
        if (Array.isArray(fromKwargs) && fromKwargs.length > 0) {
            return fromKwargs;
        }
    }
    const fromMessage = msg["tool_calls"];
    if (Array.isArray(fromMessage) && fromMessage.length > 0) {
        return fromMessage;
    }
    return null;
}
function readUsage(usage) {
    if (usage == null)
        return null;
    const promptTokens = usage["prompt_tokens"] ??
        usage["promptTokens"] ??
        usage["input_tokens"] ??
        usage["inputTokens"] ??
        0;
    const completionTokens = usage["completion_tokens"] ??
        usage["completionTokens"] ??
        usage["output_tokens"] ??
        usage["outputTokens"] ??
        0;
    const totalTokens = usage["total_tokens"] ??
        usage["totalTokens"] ??
        promptTokens + completionTokens;
    if (!promptTokens && !completionTokens && !totalTokens)
        return null;
    const result = {
        prompt_tokens: promptTokens,
        completion_tokens: completionTokens,
        total_tokens: totalTokens,
    };
    const promptDetails = usage["prompt_tokens_details"] ?? usage["input_token_details"];
    if (promptDetails != null && typeof promptDetails === "object") {
        const cached = promptDetails["cached_tokens"];
        if (cached != null) {
            result["cached_tokens"] = cached;
        }
    }
    const completionDetails = usage["completion_tokens_details"] ?? usage["output_token_details"];
    if (completionDetails != null && typeof completionDetails === "object") {
        const reasoning = completionDetails["reasoning_tokens"];
        if (reasoning != null) {
            result["reasoning_tokens"] = reasoning;
        }
    }
    return result;
}
export function extractTokenUsage(response) {
    const resp = response;
    if (resp == null)
        return null;
    const llmOutput = (resp["llmOutput"] ?? resp["llm_output"]);
    if (llmOutput != null && typeof llmOutput === "object") {
        const usage = (llmOutput["tokenUsage"] ??
            llmOutput["token_usage"] ??
            llmOutput["usage"] ??
            llmOutput["estimatedTokenUsage"]);
        const fromLlmOutput = readUsage(usage);
        if (fromLlmOutput)
            return fromLlmOutput;
    }
    const generations = resp["generations"];
    if (Array.isArray(generations)) {
        for (const group of generations) {
            if (!Array.isArray(group))
                continue;
            for (const gen of group) {
                const g = gen;
                if (g == null)
                    continue;
                const message = g["message"];
                const usageMeta = message?.["usage_metadata"] ??
                    message?.["usageMetadata"] ??
                    message?.["response_metadata"]?.["tokenUsage"] ??
                    message?.["response_metadata"]?.["usage"];
                const fromMessage = readUsage(usageMeta);
                if (fromMessage)
                    return fromMessage;
            }
        }
    }
    return null;
}
export function extractToolName(serialized) {
    if (!serialized)
        return null;
    const kwargs = serialized["kwargs"];
    if (kwargs != null && typeof kwargs === "object") {
        const kw = kwargs;
        if (typeof kw["name"] === "string" && kw["name"])
            return kw["name"];
    }
    if (typeof serialized["name"] === "string" && serialized["name"]) {
        return serialized["name"];
    }
    const idList = serialized["id"];
    if (Array.isArray(idList) && idList.length > 0) {
        const last = idList[idList.length - 1];
        if (typeof last === "string")
            return last;
    }
    return null;
}
export function stringifyToolOutput(output) {
    if (output == null)
        return "";
    if (typeof output === "string")
        return output;
    if (typeof output === "object") {
        const obj = output;
        if (typeof obj["content"] === "string")
            return obj["content"];
        if (obj["content"] != null) {
            try {
                return JSON.stringify(obj["content"]);
            }
            catch {
                return String(obj["content"]);
            }
        }
        try {
            return JSON.stringify(output);
        }
        catch {
            return String(output);
        }
    }
    return String(output);
}
export function resolveSpanName(name, metadata, serialized, fallback = "LangGraph") {
    if (name)
        return name;
    if (serialized) {
        if (typeof serialized["name"] === "string")
            return serialized["name"];
        const idList = serialized["id"];
        if (idList && idList.length > 0) {
            const last = idList[idList.length - 1];
            if (last != null)
                return last;
        }
    }
    if (metadata && typeof metadata["langgraph_node"] === "string") {
        return metadata["langgraph_node"];
    }
    return fallback;
}
export function formatError(error) {
    return {
        message: error.message,
        traceback: error.stack ?? "",
    };
}
//# sourceMappingURL=utils.mjs.map