"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LangGraphParser = void 0;
const spanDeduplicator_1 = require("../utils/spanDeduplicator");
const toolArgumentFields_1 = require("../utils/toolArgumentFields");
const utilts_1 = require("../utils/utilts");
/**
 * LangGraph Parser
 */
class LangGraphParser {
    constructor() {
        this.name = 'langgraph';
        this.deduplicator = new spanDeduplicator_1.SpanDeduplicator();
    }
    canParse(spans) {
        return spans.some((s) => {
            const wfType = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.type', 'genai.workflow.type');
            const wfName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.name', 'genai.workflow.name');
            const provider = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.provider.name', 'genai.provider.name');
            const opName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.operation.name', 'genai.operation.name');
            if (wfType === 'LangGraph')
                return true;
            if (wfName === 'LangGraph')
                return true;
            if (typeof provider === 'string' && provider.toLowerCase() === 'langgraph')
                return true;
            if (typeof s.name === 'string' && s.name.includes('workflow LangGraph'))
                return true;
            if (opName === 'graph_execution' && typeof s.name === 'string' && s.name.includes('LangGraph'))
                return true;
            if (opName === 'invoke_workflow' && typeof s.name === 'string' && s.name.includes('LangGraph'))
                return true;
            return typeof s.name === 'string' && s.name.includes('graph_execution LangGraph');
        });
    }
    getTools(spans) {
        this.deduplicator.reset();
        const tools = [];
        // 1) Prefer real tool spans if they exist
        for (const s of spans) {
            if (this.deduplicator.isDuplicate(s))
                continue;
            if (!this.isToolSpan(s))
                continue;
            const fields = (0, toolArgumentFields_1.extractOpenLitStyleToolFieldsFromSpan)(s);
            tools.push((0, utilts_1.buildCanonicalTool)({
                id: s.id,
                name: fields.name,
                ...(fields.description != null && { description: fields.description }),
                input: fields.input,
                output: fields.output,
                argument: fields.argument,
                argument_defination: fields.argument_defination,
                duration: s.duration,
                timestamp: s.timestamp,
                spanId: s.id,
                executionType: 'actual',
                metadata: { source: 'tool_span' },
            }));
        }
        const existingSpanIds = new Set(tools.map((t) => t.spanId));
        // If we already have real tool spans, don't synthesize "message tools" (e.g. DataCollector).
        // In LangGraph traces like langgraph_5.json, tools are explicit spans and should nest under agents.
        if (tools.length > 0)
            return tools;
        // 2) LangGraph message-tools embedded in the LangGraph root workflow.output.messages[]
        const root = this.findLangGraphRoot(spans);
        if (!root)
            return tools;
        const rootOutput = utilts_1.FrameworkUtils.getAttr(root.attributes, 'gen_ai.workflow.output', 'genai.workflow.output');
        if (typeof rootOutput !== 'string')
            return tools;
        let messages = [];
        const parsed = this.safeParseJson(rootOutput);
        if (Array.isArray(parsed?.messages)) {
            messages = parsed.messages;
        }
        else {
            // Fallback: workflow.output may be truncated/invalid JSON. Extract serialized messages directly.
            messages = this.extractSerializedMessagesFromRawOutput(rootOutput);
        }
        if (messages.length === 0)
            return tools;
        const directChildren = spans.filter((s) => s.parentSpanId === root.id);
        for (const msg of messages) {
            const m = this.parseSerializedMessage(msg);
            if (!m?.name || !m?.content)
                continue;
            const name = m.name.trim();
            const content = m.content.trim();
            if (!this.looksLikeToolMessage(name, content))
                continue;
            const attachSpan = this.bestAttachSpanForTool(name, directChildren) ?? root;
            if (existingSpanIds.has(attachSpan.id))
                continue; // simple: 1 tool per span
            const wfInput = utilts_1.FrameworkUtils.getAttr(attachSpan.attributes, 'gen_ai.workflow.input', 'genai.workflow.input');
            const argumentDef = (0, toolArgumentFields_1.getToolArgumentDefinitionFromAttrs)(attachSpan.attributes) ?? (0, toolArgumentFields_1.inferArgumentDefinationFromInput)(wfInput ?? content);
            tools.push((0, utilts_1.buildCanonicalTool)({
                id: attachSpan.id,
                name,
                ...((0, toolArgumentFields_1.getToolDescriptionFromAttrs)(attachSpan.attributes) != null && {
                    description: (0, toolArgumentFields_1.getToolDescriptionFromAttrs)(attachSpan.attributes),
                }),
                input: wfInput,
                output: content,
                argument: wfInput,
                argument_defination: argumentDef,
                duration: attachSpan.duration,
                timestamp: attachSpan.timestamp,
                spanId: attachSpan.id,
                executionType: 'actual',
                metadata: { source: 'workflow_messages', messageId: m.id, rootSpanId: root.id },
            }));
            existingSpanIds.add(attachSpan.id);
        }
        return tools;
    }
    getAgents(spans) {
        this.deduplicator.reset();
        const root = this.findLangGraphRoot(spans);
        if (!root)
            return [];
        const agents = [];
        // LangGraph root is the framework/workflow container, not an agent. Do not add it to agents.
        // Only child workflow nodes (e.g. investigation, collect_data, news_risk) are exposed as agents.
        const children = spans
            .filter((s) => s.parentSpanId === root.id)
            .filter((s) => this.isAgentSpan(s));
        const spanById = new Map(spans.map((sp) => [sp.id, sp]));
        for (const s of children) {
            if (this.deduplicator.isDuplicate(s))
                continue;
            const wfType = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.type', 'genai.workflow.type');
            const nodeName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'langgraph.node.name');
            const agentName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.agent.name', 'genai.agent.name');
            const agentId = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.agent.id', 'genai.agent.id');
            const nodeKey = (typeof nodeName === 'string' && nodeName.trim())
                ? nodeName.trim()
                : (typeof agentName === 'string' && agentName.trim())
                    ? agentName.trim()
                    : (typeof agentId === 'string' && agentId.trim())
                        ? agentId.trim()
                        : (typeof wfType === 'string' && wfType.trim())
                            ? wfType
                            : typeof s.name === 'string' && s.name.startsWith('invoke_agent ')
                                ? s.name.replace(/^invoke_agent\s+/i, '').trim()
                                : (s.name ?? '').toString().replace(/^workflow\s+/i, '').trim();
            // IMPORTANT (prod-safe): agent names must come from trace-only stable identifiers.
            // Use langgraph.node.name, gen_ai.workflow.type (e.g. "news_risk", "collect_data"), or span name.
            const name = nodeKey || (typeof s.name === 'string' ? s.name : String(s.id));
            const toolDefinitions = (0, toolArgumentFields_1.collectAgentToolDefinitions)(s.id, spans, spanById, (sp) => this.isToolSpan(sp));
            agents.push((0, utilts_1.buildCanonicalAgent)({
                id: s.id,
                name,
                type: (typeof wfType === 'string' && wfType) ? wfType : 'langgraph',
                ...((0, toolArgumentFields_1.getAgentDescriptionFromAttrs)(s.attributes) != null && {
                    description: (0, toolArgumentFields_1.getAgentDescriptionFromAttrs)(s.attributes),
                }),
                role: this.inferRoleFromAgentName(name) ?? utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.agent.role', 'genai.agent.role'),
                goal: utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.agent.goal', 'genai.agent.goal'),
                ...(toolDefinitions.length > 0 && { toolDefinitions }),
                metadata: { spanId: s.id, timestamp: s.timestamp, duration: s.duration },
            }));
        }
        return agents;
    }
    getModels(spans) {
        // Reuse the same model detection/extraction as LangChain
        this.deduplicator.reset();
        const models = [];
        const seen = new Set();
        for (const s of spans) {
            if (this.deduplicator.isDuplicate(s))
                continue;
            if (!this.isModelSpan(s))
                continue;
            const m = this.extractModelLikeLangChain(s);
            if (!m)
                continue;
            if (seen.has(m.id))
                continue;
            models.push(m);
            seen.add(m.id);
        }
        return models;
    }
    generateDag(spans) {
        // Keep it simple: same as LangChain parser (span-level DAG)
        this.deduplicator.reset();
        const uniqueSpans = spans.filter((s) => !this.deduplicator.isDuplicate(s));
        const nodes = [];
        const nodeMap = new Map();
        for (const span of uniqueSpans) {
            const node = {
                id: span.id,
                type: this.getNodeType(span),
                name: span.name,
                children: [],
                metadata: {
                    timestamp: span.timestamp,
                    duration: span.duration,
                    parentSpanId: span.parentSpanId,
                    attributes: span.attributes,
                },
            };
            nodes.push(node);
            nodeMap.set(span.id, node);
        }
        for (const span of uniqueSpans) {
            if (!span.parentSpanId)
                continue;
            const parentNode = nodeMap.get(span.parentSpanId);
            if (parentNode && !parentNode.children.includes(span.id)) {
                parentNode.children.push(span.id);
            }
        }
        const byTime = [...uniqueSpans].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
        // First entity span: any agent/tool/llm so prompt wires to the call DAG
        const entityTypes = new Set(['agent', 'tool', 'llm']);
        const firstEntitySpan = byTime.find((s) => nodeMap.has(s.id) && entityTypes.has(this.getNodeType(s)));
        // Last entity span: prefer agent/tool (always in call DAG); llm can be missing if model was filtered (e.g. "unknown")
        const agentOrTool = new Set(['agent', 'tool']);
        const lastEntitySpan = [...byTime].reverse().find((s) => nodeMap.has(s.id) && agentOrTool.has(this.getNodeType(s))) ??
            [...byTime].reverse().find((s) => nodeMap.has(s.id) && entityTypes.has(this.getNodeType(s)));
        const firstSpanId = firstEntitySpan?.id ?? (byTime.length > 0 && nodeMap.has(byTime[0].id) ? byTime[0].id : undefined);
        const lastSpanId = lastEntitySpan?.id ??
            (byTime.length > 0 && nodeMap.has(byTime[byTime.length - 1].id) ? byTime[byTime.length - 1].id : undefined);
        if (firstSpanId != null) {
            nodes.push({
                id: 'prompt',
                type: 'prompt',
                name: 'Prompt',
                children: [firstSpanId],
                metadata: {}
            });
        }
        if (lastSpanId != null) {
            const lastNode = nodeMap.get(lastSpanId);
            if (lastNode)
                lastNode.children = [...lastNode.children, 'response'];
            nodes.push({
                id: 'response',
                type: 'response',
                name: 'Response',
                children: [],
                metadata: {}
            });
        }
        return nodes;
    }
    generateLinearFlow(spans) {
        const tools = this.getTools(spans);
        const agents = this.getAgents(spans);
        const models = this.getModels(spans);
        const toolSpanIds = new Set(tools.map((t) => t.spanId));
        const agentSpanIds = new Set(agents.map((a) => a.id));
        const modelSpanIds = new Set(models.map((m) => m.spanId));
        const entitySpanIds = new Set([...toolSpanIds, ...agentSpanIds, ...modelSpanIds]);
        const spanMap = new Map(spans.map((s) => [s.id, s]));
        // 2) Build ordered list of entity spans by timestamp (prompt → first entity … last entity → response)
        const entitySpans = [...entitySpanIds]
            .map((id) => spanMap.get(id))
            .filter((s) => s != null)
            .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
        // Prefer agent over tool when a span is both (e.g. message-derived tool attached to agent span)
        const getStepType = (span) => {
            if (agentSpanIds.has(span.id))
                return 'agent';
            if (toolSpanIds.has(span.id))
                return 'tool';
            return 'llm';
        };
        return entitySpans.map((span, index) => ({
            stepNumber: index + 1,
            spanId: span.id,
            type: getStepType(span),
            name: span.name,
            timestamp: span.timestamp,
            duration: span.duration,
            input: utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.workflow.input', 'genai.workflow.input', 'gen_ai.prompt', 'genai.prompt', 'gen_ai.tool.input', 'genai.tool.input'),
            output: utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.workflow.output', 'genai.workflow.output', 'gen_ai.completion', 'genai.completion', 'gen_ai.tool.output', 'genai.tool.output'),
            metadata: {
                parentSpanId: span.parentSpanId,
                operationName: utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.operation.name', 'genai.operation.name'),
                workflowType: utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.workflow.type', 'genai.workflow.type'),
            },
        }));
    }
    // ---------- small helpers (kept local to keep the flow simple) ----------
    findLangGraphRoot(spans) {
        // Tier 1: spans with wfType === 'LangGraph', wfName === 'LangGraph',
        // or name 'workflow LangGraph'/'invoke_workflow LangGraph'.
        // These are the actual graph entry points whose direct children are the agent nodes
        // (e.g. workflow supervisor, invoke_agent cio). Prefer these over container spans.
        const tier1 = spans.filter((s) => {
            const wfType = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.type', 'genai.workflow.type');
            const wfName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.name', 'genai.workflow.name');
            const opName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.operation.name', 'genai.operation.name');
            if (wfType === 'LangGraph')
                return true;
            if (wfName === 'LangGraph' && (opName === 'invoke_workflow' || opName === 'workflow' || opName === 'graph_execution'))
                return true;
            if (typeof s.name === 'string' && (s.name === 'workflow LangGraph' || s.name === 'invoke_workflow LangGraph'))
                return true;
            return false;
        });
        // Tier 2: graph_execution/invoke_workflow container spans (outer wrappers that may not have agent children directly).
        // Fall back to these only when no tier-1 span exists.
        const tier2 = spans.filter((s) => {
            const opName = utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.operation.name', 'genai.operation.name');
            return ((opName === 'graph_execution' && typeof s.name === 'string' && s.name.includes('LangGraph')) ||
                (opName === 'invoke_workflow' && typeof s.name === 'string' && s.name.includes('LangGraph')) ||
                (typeof s.name === 'string' && s.name.includes('graph_execution LangGraph')) ||
                (typeof s.name === 'string' && s.name.includes('invoke_workflow LangGraph')));
        });
        const candidates = tier1.length > 0 ? tier1 : tier2;
        if (candidates.length === 0)
            return undefined;
        // Prefer root span (no parent); otherwise earliest by timestamp
        const withNoParent = candidates.filter((s) => !s.parentSpanId || s.parentSpanId === '');
        const pool = withNoParent.length > 0 ? withNoParent : candidates;
        return pool.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime())[0];
    }
    safeParseJson(value) {
        if (typeof value !== 'string')
            return undefined;
        const s = value.trim();
        if (!s)
            return undefined;
        try {
            return JSON.parse(s);
        }
        catch {
            return undefined;
        }
    }
    extractSerializedMessagesFromRawOutput(raw) {
        // Matches the common OpenLit/LangGraph message serialization:
        // content='...' additional_kwargs=... response_metadata=... name='X' id='...'
        // This works even when the surrounding JSON is incomplete.
        const results = [];
        const re = /content='([\s\S]*?)'\s+additional_kwargs=[\s\S]*?\sname='([^']+)'(?:\s+id='([^']+)')?/g;
        let m;
        while ((m = re.exec(raw)) !== null) {
            const content = m[1];
            const name = m[2];
            const id = m[3];
            const parts = [
                `content='${content}'`,
                `additional_kwargs={}`,
                `response_metadata={}`,
                `name='${name}'`,
            ];
            if (id)
                parts.push(`id='${id}'`);
            results.push(parts.join(' '));
            if (results.length >= 50)
                break; // keep it bounded
        }
        return results;
    }
    parseSerializedMessage(s) {
        if (typeof s !== 'string')
            return null;
        const contentMatch = s.match(/content='([\s\S]*?)'\s+/) ??
            s.match(/content=\\?"([\s\S]*?)\\?"\s+/) ??
            s.match(/content="([\s\S]*?)"\s+/);
        const nameMatch = s.match(/name='([^']+)'/);
        const idMatch = s.match(/id='([^']+)'/);
        if (!contentMatch && !nameMatch)
            return null;
        return { content: contentMatch?.[1], name: nameMatch?.[1], id: idMatch?.[1] };
    }
    looksLikeToolMessage(name, content) {
        const n = name.toLowerCase();
        const c = content.trim();
        if (!name || n === 'user' || n === 'system')
            return false;
        // Avoid misclassifying semantic agent message names like DataCollector/CIO/Critic.
        if (n.includes('analyst') || n.includes('collector') || n === 'cio' || n.includes('critic'))
            return false;
        // Tool names in LangGraph are typically snake_case function-like identifiers.
        if (/^[a-z0-9_]+$/.test(n) && n.includes('_'))
            return true;
        // Last-resort: very short status output with a non-agent name
        if (c.length > 0 && c.length <= 160)
            return true;
        return false;
    }
    tokenizeName(s) {
        const cleaned = String(s ?? '').trim();
        if (!cleaned)
            return [];
        return cleaned
            .replace(/([a-z])([A-Z])/g, '$1 $2')
            .replace(/[_\-\s]+/g, ' ')
            .toLowerCase()
            .split(' ')
            .map((t) => t.trim())
            .filter(Boolean);
    }
    overlapScore(a, b) {
        const A = new Set(this.tokenizeName(a));
        const B = new Set(this.tokenizeName(b));
        if (A.size === 0 || B.size === 0)
            return 0;
        let overlap = 0;
        for (const t of A)
            if (B.has(t))
                overlap++;
        return overlap;
    }
    bestAttachSpanForTool(toolName, candidates) {
        let best;
        let bestScore = 0;
        for (const s of candidates) {
            const wfType = String(utilts_1.FrameworkUtils.getAttr(s.attributes, 'gen_ai.workflow.type', 'genai.workflow.type') ?? '');
            const score = Math.max(this.overlapScore(toolName, wfType), this.overlapScore(toolName, s.name ?? ''));
            if (score > bestScore) {
                bestScore = score;
                best = s;
            }
        }
        return bestScore > 0 ? best : undefined;
    }
    // NOTE: We intentionally do NOT infer agent names from messages (e.g. HumanMessage(name="DataCollector"))
    // because those are application-defined and can cause incorrect collapsing/labeling across nodes.
    inferRoleFromAgentName(name) {
        const n = (name || '').toLowerCase();
        if (n.includes('fundamentals'))
            return 'fundamental_analysis';
        if (n.includes('technical'))
            return 'technical_analysis';
        if (n.includes('news') || n.includes('risk'))
            return 'news_risk';
        if (n.includes('peers'))
            return 'peer_analysis';
        if (n === 'cio')
            return 'synthesis';
        if (n.includes('critic'))
            return 'review';
        if (n.includes('collector'))
            return 'data_collection';
        return undefined;
    }
    isInfrastructureWorkflow(span) {
        const wfType = String(utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.workflow.type', 'genai.workflow.type') ?? '');
        const name = String(span.name ?? '');
        const infra = [
            'RunnableLambda',
            'RunnableSequence',
            'RunnableParallel',
            'ChatPromptTemplate',
            'StrOutputParser',
            'should_continue',
            'route_decision',
            'route_after_supervisor', // LangGraph internal routing after supervisor
        ];
        return infra.some((p) => wfType.includes(p) || name.includes(p));
    }
    isToolSpan(span) {
        if (typeof span.name === 'string' && span.name.startsWith('tool '))
            return true;
        const opName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.operation.name', 'genai.operation.name');
        const toolName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.tool.name', 'genai.tool.name');
        return opName === 'execute_tool' && !!toolName;
    }
    isAgentSpan(span) {
        const opName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.operation.name', 'genai.operation.name');
        // OpenLit LangGraph: invoke_agent (e.g. "invoke_agent research", langgraph.node.name)
        if (opName === 'invoke_agent')
            return !this.isInfrastructureWorkflow(span);
        if (opName !== 'workflow')
            return false;
        const wfType = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.workflow.type', 'genai.workflow.type');
        // LangGraph root is the framework container, not an agent.
        if (wfType === 'LangGraph')
            return false;
        return !this.isInfrastructureWorkflow(span);
    }
    isModelSpan(span) {
        const opName = utilts_1.FrameworkUtils.getAttr(span.attributes, 'gen_ai.operation.name', 'genai.operation.name');
        // Container spans (graph_execution, invoke_agent) may have model attrs from inner LLM calls but are not LLM spans.
        if (opName === 'graph_execution' || opName === 'invoke_agent')
            return false;
        if (typeof span.name === 'string' && span.name.startsWith('chat '))
            return true;
        if (opName === 'chat')
            return true;
        if (utilts_1.FrameworkUtils.getModelAttr(span.attributes, 'gen_ai.request.model', 'genai.request.model'))
            return true;
        if (utilts_1.FrameworkUtils.getModelAttr(span.attributes, 'gen_ai.response.model', 'genai.response.model'))
            return true;
        return false;
    }
    extractModelLikeLangChain(span) {
        const attrs = span.attributes;
        const modelName = utilts_1.FrameworkUtils.getModelAttrResponseFirst(attrs);
        const finalModelName = modelName || (typeof span.name === 'string' ? span.name.replace(/^chat\s+/i, '') : undefined);
        if (!finalModelName)
            return null;
        // token usage (simple)
        const inputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.input_tokens', 'genai.usage.inputtokens'));
        const outputTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.output_tokens', 'genai.usage.outputtokens'));
        let totalTokens = utilts_1.FrameworkUtils.safeParseInt(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.total_tokens', 'genai.usage.totaltokens'));
        if (!totalTokens && (inputTokens || outputTokens))
            totalTokens = inputTokens + outputTokens;
        const { cacheRead, cacheWrite } = utilts_1.FrameworkUtils.getCacheTokens(attrs);
        const tokenUsage = {
            inputTokens,
            outputTokens,
            totalTokens,
            ...(cacheRead != null && { cacheRead }),
            ...(cacheWrite != null && { cacheWrite }),
        };
        return (0, utilts_1.buildCanonicalExtendedModel)({
            id: span.id,
            provider: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.system', 'genai.system') || 'openai',
            modelName: String(finalModelName),
            tokenUsage,
            spanId: span.id,
            timestamp: span.timestamp,
            temperature: utilts_1.FrameworkUtils.safeParseFloat(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.request.temperature', 'genai.request.temperature')),
            cost: utilts_1.FrameworkUtils.safeParseFloat(utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.usage.cost', 'genai.usage.cost')) || 0,
            ttft: utilts_1.FrameworkUtils.safeParseFloat(attrs['gen_ai.server.time_to_first_token']),
            serverTimePerOutputToken: utilts_1.FrameworkUtils.safeParseFloat(attrs['gen_ai.server.time_per_output_token']),
            prompt: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.prompt', 'genai.prompt'),
            completion: utilts_1.FrameworkUtils.getAttr(attrs, 'gen_ai.completion', 'genai.completion'),
            duration: utilts_1.FrameworkUtils.safeParseFloat(span.duration ?? 0) ?? 0,
        });
    }
    getNodeType(span) {
        if (this.isModelSpan(span))
            return 'llm';
        if (this.isToolSpan(span))
            return 'tool';
        if (this.isAgentSpan(span))
            return 'agent';
        return 'workflow';
    }
}
exports.LangGraphParser = LangGraphParser;
//# sourceMappingURL=langgraph.js.map