"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChartUtils = exports.FrameworkUtils = void 0;
exports.buildCanonicalTool = buildCanonicalTool;
exports.buildCanonicalAgent = buildCanonicalAgent;
exports.buildCanonicalExtendedModel = buildCanonicalExtendedModel;
/**
 * Build a canonical Tool so all frameworks return the same shape.
 */
function buildCanonicalTool(p) {
    return {
        id: p.id,
        name: p.name,
        ...(p.description != null && { description: p.description }),
        input: p.input,
        output: p.output,
        ...(p.argument !== undefined && { argument: p.argument }),
        ...(p.argument_defination !== undefined && { argument_defination: p.argument_defination }),
        duration: Number(p.duration) || 0,
        timestamp: p.timestamp,
        spanId: p.spanId,
        executionType: p.executionType ?? 'actual',
        ...(p.tokenUsage != null && { tokenUsage: p.tokenUsage }),
        ...(p.metadata != null && Object.keys(p.metadata).length > 0 && { metadata: p.metadata }),
    };
}
/**
 * Build a canonical Agent so all frameworks return the same shape.
 */
function buildCanonicalAgent(p) {
    return {
        id: p.id,
        name: p.name,
        type: p.type,
        ...(p.description != null && { description: p.description }),
        ...(p.role != null && { role: p.role }),
        ...(p.goal != null && { goal: p.goal }),
        ...(p.backstory != null && { backstory: p.backstory }),
        ...(p.toolDefinitions != null &&
            p.toolDefinitions.length > 0 && { toolDefinitions: p.toolDefinitions }),
        ...(p.metadata != null && Object.keys(p.metadata).length > 0 && { metadata: p.metadata }),
    };
}
/**
 * Build a canonical ExtendedModel so all frameworks return the same shape.
 */
function buildCanonicalExtendedModel(p) {
    const tokenUsage = {
        inputTokens: p.tokenUsage.inputTokens ?? 0,
        outputTokens: p.tokenUsage.outputTokens ?? 0,
        totalTokens: p.tokenUsage.totalTokens ?? (p.tokenUsage.inputTokens ?? 0) + (p.tokenUsage.outputTokens ?? 0),
        ...(p.tokenUsage.cacheRead != null && { cacheRead: p.tokenUsage.cacheRead }),
        ...(p.tokenUsage.cacheWrite != null && { cacheWrite: p.tokenUsage.cacheWrite }),
        ...(p.tokenUsage.cachedTokens != null && { cachedTokens: p.tokenUsage.cachedTokens }),
        ...(p.tokenUsage.reasoningTokens != null && { reasoningTokens: p.tokenUsage.reasoningTokens }),
    };
    return {
        id: p.id,
        provider: p.provider,
        modelName: p.modelName,
        tokenUsage,
        spanId: p.spanId,
        timestamp: p.timestamp,
        ...(p.temperature != null && { temperature: p.temperature }),
        cost: p.cost ?? 0,
        ...(p.ttft != null && { ttft: p.ttft }),
        ...(p.serverTimePerOutputToken != null && { serverTimePerOutputToken: p.serverTimePerOutputToken }),
        ...(p.prompt != null && { prompt: p.prompt }),
        ...(p.completion != null && { completion: p.completion }),
        duration: p.duration ?? undefined,
    };
}
/**
 * Utility: Extract attribute with fallback between gen_ai.* and genai.* formats
 */
function getAttr(attrs, ...keys) {
    for (const key of keys) {
        if (attrs[key] !== undefined && attrs[key] !== null) {
            return attrs[key];
        }
    }
    return undefined;
}
const RESPONSE_MODEL_KEYS = ['gen_ai.response.model', 'genai.response.model'];
const REQUEST_MODEL_KEYS = ['gen_ai.request.model', 'genai.request.model'];
/**
 * Get model attribute (request/response model), treating "unknown" as absent.
 * Use this for model detection and model name extraction so gen_ai.request.model = "unknown" is ignored.
 */
function getModelAttr(attrs, ...keys) {
    const v = getAttr(attrs, ...keys);
    if (v === undefined || v === null)
        return undefined;
    const s = String(v).trim();
    if (!s || s.toLowerCase() === 'unknown')
        return undefined;
    return s;
}
/**
 * Prefer genai.response.model (and gen_ai.response.model), and fall back to genai.request.model
 * when the response model is missing or clearly just copied from the request (response === request).
 */
function getModelAttrResponseFirst(attrs) {
    const responseModel = getModelAttr(attrs, ...RESPONSE_MODEL_KEYS);
    const requestModel = getModelAttr(attrs, ...REQUEST_MODEL_KEYS);
    if (requestModel != null) {
        return requestModel;
    }
    return responseModel;
}
/**
 * Utility: Safe parseInt
 */
function safeParseInt(value) {
    if (value === undefined || value === null)
        return 0;
    const parsed = parseInt(String(value), 10);
    return isNaN(parsed) ? 0 : parsed;
}
/**
 * Utility: Safe parseFloat
 */
function safeParseFloat(value) {
    if (value === undefined || value === null)
        return undefined;
    const parsed = parseFloat(String(value));
    return isNaN(parsed) ? undefined : parsed;
}
const CACHE_READ_RE = /['"]cache_read['"]\s*:\s*(\d+)/;
const CACHE_WRITE_RE = /['"]cache_write['"]\s*:\s*(\d+)/;
function getCacheTokensFromEmbeddedString(str) {
    if (typeof str !== 'string' || !str.length)
        return {};
    const cacheReadMatch = str.match(CACHE_READ_RE);
    const cacheWriteMatch = str.match(CACHE_WRITE_RE);
    return {
        cacheRead: cacheReadMatch ? safeParseInt(cacheReadMatch[1]) : undefined,
        cacheWrite: cacheWriteMatch ? safeParseInt(cacheWriteMatch[1]) : undefined,
    };
}
function getCacheTokens(attrs) {
    let cacheRead;
    let cacheWrite;
    cacheRead = safeParseInt(getAttr(attrs, 'gen_ai.usage.cache_read_tokens', 'genai.usage.cache_read_tokens'));
    cacheWrite = safeParseInt(getAttr(attrs, 'gen_ai.usage.cache_write_tokens', 'genai.usage.cache_write_tokens'));
    const usageMeta = attrs?.usage_metadata;
    if (usageMeta && typeof usageMeta === 'object') {
        const inputDetails = usageMeta.input_token_details;
        const outputDetails = usageMeta.output_token_details;
        if (inputDetails != null && typeof inputDetails === 'object') {
            const v = safeParseInt(inputDetails.cache_read);
            if (v > 0)
                cacheRead = v;
        }
        if (outputDetails != null && typeof outputDetails === 'object') {
            const v = safeParseInt(outputDetails.cache_write);
            if (v > 0)
                cacheWrite = v;
        }
    }
    if ((cacheRead == null || cacheRead === 0) || (cacheWrite == null || cacheWrite === 0)) {
        const workflowInput = getAttr(attrs, 'gen_ai.workflow.input', 'genai.workflow.input');
        const workflowOutput = getAttr(attrs, 'gen_ai.workflow.output', 'genai.workflow.output');
        for (const val of [workflowInput, workflowOutput]) {
            if (typeof val !== 'string')
                continue;
            const embedded = getCacheTokensFromEmbeddedString(val);
            if (embedded.cacheRead != null && embedded.cacheRead > 0 && (cacheRead == null || cacheRead === 0)) {
                cacheRead = embedded.cacheRead;
            }
            if (embedded.cacheWrite != null && embedded.cacheWrite > 0 && (cacheWrite == null || cacheWrite === 0)) {
                cacheWrite = embedded.cacheWrite;
            }
        }
    }
    return {
        cacheRead: cacheRead != null && cacheRead > 0 ? cacheRead : undefined,
        cacheWrite: cacheWrite != null && cacheWrite > 0 ? cacheWrite : undefined,
    };
}
const padHistogramBinsToMin = (points, opts) => {
    const minPoints = Math.max(1, Math.floor(opts?.minPoints ?? 20));
    const binWidth = Number(opts?.binWidth);
    const minStartClamp = Math.max(0, Number(opts?.minStart ?? 0));
    const maxPoints = opts?.maxPoints != null && Number.isFinite(opts.maxPoints) ? Math.floor(Number(opts.maxPoints)) : undefined;
    const parseBin = (binRaw) => {
        const bin = String(binRaw ?? '').trim();
        // Support negative ranges like "-2--1" and tolerate whitespace.
        const m = bin.match(/^\s*(-?\d+(?:\.\d+)?)\s*-\s*(-?\d+(?:\.\d+)?)\s*$/);
        if (!m)
            return null;
        const start = Number.parseFloat(m[1]);
        const end = Number.parseFloat(m[2]);
        if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start)
            return null;
        return { start, end, bin: `${start}-${end}` };
    };
    if (!Array.isArray(points) || points.length === 0) {
        return points;
    }
    if (!Number.isFinite(binWidth) || binWidth <= 0)
        return points;
    const parsed = points
        .map((p) => ({
        parsed: parseBin(p?.bin),
        count: Number(p?.count ?? 0),
    }))
        .filter((p) => !!p.parsed);
    // If we can't confidently parse bins, don't try to fabricate new ones.
    if (parsed.length === 0)
        return points;
    // If original points exceed maxPoints, return as-is: no clamp, no pad.
    if (maxPoints != null && parsed.length > maxPoints) {
        return parsed.map((p) => ({ bin: p.parsed.bin, count: p.count }));
    }
    const clamped = parsed.filter((p) => p.parsed.start >= minStartClamp);
    if (clamped.length === 0)
        return [];
    const toKey = (n) => {
        const v = Number.isInteger(n) ? n : Number(n.toFixed(6));
        return Object.is(v, -0) ? 0 : v;
    };
    const toLabel = (n) => {
        const nn = Object.is(n, -0) ? 0 : n;
        if (Number.isInteger(nn))
            return String(nn);
        const fixed = nn.toFixed(6);
        const trimmed = fixed.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
        return trimmed;
    };
    // Keep each data bin as-is (same bin string, same count); only add zero bins for padding.
    const dataByStart = new Map();
    for (const p of clamped) {
        const key = toKey(p.parsed.start);
        const prev = dataByStart.get(key);
        if (!prev) {
            dataByStart.set(key, { bin: p.parsed.bin, count: p.count, end: p.parsed.end });
        }
        else {
            prev.count += p.count;
        }
    }
    const dataBinsSorted = Array.from(dataByStart.entries())
        .sort((a, b) => a[0] - b[0])
        .map(([start, v]) => ({ start, bin: v.bin, count: v.count, end: v.end }));
    if (dataBinsSorted.length >= minPoints) {
        return dataBinsSorted.map((d) => ({ bin: d.bin, count: d.count }));
    }
    const result = [];
    let totalWithoutLeftRight = dataBinsSorted.length;
    const maxTotal = maxPoints ?? Infinity;
    // Middle padding: zero bins in gaps between consecutive data bins (capped so total does not exceed maxPoints).
    for (let i = 0; i < dataBinsSorted.length; i++) {
        if (i > 0) {
            let s = toKey(dataBinsSorted[i - 1].end);
            while (s < dataBinsSorted[i].start && totalWithoutLeftRight < maxTotal) {
                const end = toKey(s + binWidth);
                result.push({ bin: `${toLabel(s)}-${toLabel(end)}`, count: 0 });
                totalWithoutLeftRight += 1;
                s = toKey(s + binWidth);
            }
        }
        result.push({ bin: dataBinsSorted[i].bin, count: dataBinsSorted[i].count });
    }
    // Padding must not push total length above maxPoints.
    const needPad = Math.max(0, Math.min(minPoints - totalWithoutLeftRight, maxTotal - totalWithoutLeftRight));
    const leftPad = Math.floor(needPad / 2);
    const rightPad = needPad - leftPad;
    // Left padding: zero bins before first data bin (do not generate negative bins).
    const leftBins = [];
    let s = toKey(dataBinsSorted[0].start - binWidth);
    for (let i = 0; i < leftPad; i++) {
        if (s >= 0) {
            leftBins.unshift({
                bin: `${toLabel(s)}-${toLabel(toKey(s + binWidth))}`,
                count: 0,
            });
        }
        s = toKey(s - binWidth);
    }
    // Right padding: zero bins after last data bin.
    const rightBins = [];
    let nextStart = dataBinsSorted[dataBinsSorted.length - 1].end;
    for (let i = 0; i < rightPad; i++) {
        const st = toKey(nextStart);
        const end = toKey(nextStart + binWidth);
        rightBins.push({ bin: `${toLabel(st)}-${toLabel(end)}`, count: 0 });
        nextStart += binWidth;
    }
    return [...leftBins, ...result, ...rightBins];
};
exports.FrameworkUtils = {
    getAttr,
    getModelAttr,
    getModelAttrResponseFirst,
    safeParseInt,
    safeParseFloat,
    getCacheTokens,
};
exports.ChartUtils = {
    padHistogramBinsToMin,
};
//# sourceMappingURL=utilts.js.map