"use strict";
/**
 * Helpers for embedding providers when input exceeds model token limits.
 * Token counts use a chars/4 heuristic (conservative margin) — not exact tiktoken counts.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_EMBEDDING_TOKEN_MARGIN = exports.DEFAULT_EMBEDDING_MAX_TOKENS = void 0;
exports.estimateTokenCount = estimateTokenCount;
exports.maxCharsForTokens = maxCharsForTokens;
exports.getEffectiveMaxTokens = getEffectiveMaxTokens;
exports.getOverflowStrategy = getOverflowStrategy;
exports.splitTextForEmbedding = splitTextForEmbedding;
exports.meanPoolEmbeddings = meanPoolEmbeddings;
exports.prepareEmbeddingInput = prepareEmbeddingInput;
exports.aggregateEmbeddingsFromResponse = aggregateEmbeddingsFromResponse;
exports.buildEmbeddingOverflowMetadata = buildEmbeddingOverflowMetadata;
exports.DEFAULT_EMBEDDING_MAX_TOKENS = 8192;
exports.DEFAULT_EMBEDDING_TOKEN_MARGIN = 128;
function estimateTokenCount(text) {
    return Math.ceil(text.length / 4);
}
function maxCharsForTokens(maxTokens) {
    const effectiveTokens = Math.max(1, maxTokens - exports.DEFAULT_EMBEDDING_TOKEN_MARGIN);
    return effectiveTokens * 4;
}
function getEffectiveMaxTokens(config) {
    return config?.embeddingMaxTokens ?? exports.DEFAULT_EMBEDDING_MAX_TOKENS;
}
function getOverflowStrategy(config) {
    return config?.embeddingOverflowStrategy ?? 'chunk-mean';
}
function splitTextForEmbedding(text, maxTokens) {
    const maxChars = maxCharsForTokens(maxTokens);
    if (text.length <= maxChars) {
        return [text];
    }
    const chunks = [];
    let remaining = text;
    while (remaining.length > 0) {
        if (remaining.length <= maxChars) {
            chunks.push(remaining);
            break;
        }
        const slice = remaining.slice(0, maxChars);
        const breakIndex = findBestBreakIndex(slice);
        const chunk = remaining.slice(0, breakIndex);
        chunks.push(chunk);
        remaining = remaining.slice(breakIndex);
    }
    return chunks.filter((c) => c.length > 0);
}
function findBestBreakIndex(slice) {
    const minBreak = Math.floor(slice.length * 0.5);
    const paragraphBreak = slice.lastIndexOf('\n\n');
    if (paragraphBreak >= minBreak) {
        return paragraphBreak + 2;
    }
    const lineBreak = slice.lastIndexOf('\n');
    if (lineBreak >= minBreak) {
        return lineBreak + 1;
    }
    const sentenceBreak = slice.lastIndexOf('. ');
    if (sentenceBreak >= minBreak) {
        return sentenceBreak + 2;
    }
    return slice.length;
}
function meanPoolEmbeddings(embeddings, weights) {
    if (embeddings.length === 0) {
        throw new Error('Cannot mean-pool empty embeddings array');
    }
    const dim = embeddings[0].length;
    const effectiveWeights = weights && weights.length === embeddings.length
        ? weights
        : embeddings.map(() => 1);
    const totalWeight = effectiveWeights.reduce((sum, w) => sum + w, 0);
    if (totalWeight === 0) {
        throw new Error('Cannot mean-pool embeddings with zero total weight');
    }
    const pooled = new Array(dim).fill(0);
    for (let i = 0; i < embeddings.length; i++) {
        const weight = effectiveWeights[i] / totalWeight;
        for (let j = 0; j < dim; j++) {
            pooled[j] += embeddings[i][j] * weight;
        }
    }
    return pooled;
}
function prepareEmbeddingInput(text, config) {
    const maxTokens = getEffectiveMaxTokens(config);
    const strategy = getOverflowStrategy(config);
    const estimatedTokens = estimateTokenCount(text);
    const chunks = splitTextForEmbedding(text, maxTokens);
    const overflow = estimatedTokens > maxTokens || chunks.length > 1;
    if (!overflow) {
        return {
            input: text,
            overflow: false,
            chunkCount: 1,
            strategy,
            estimatedTokens,
            chunks: [text],
        };
    }
    if (strategy === 'truncate') {
        return {
            input: chunks[0],
            overflow: true,
            chunkCount: chunks.length,
            strategy,
            estimatedTokens,
            chunks,
        };
    }
    return {
        input: chunks,
        overflow: true,
        chunkCount: chunks.length,
        strategy,
        estimatedTokens,
        chunks,
    };
}
function aggregateEmbeddingsFromResponse(data, expectedCount, strategy) {
    const items = data?.data;
    if (!Array.isArray(items) || items.length === 0) {
        return { error: 'No embedding returned' };
    }
    if (items.length < expectedCount) {
        return {
            error: `Expected ${expectedCount} embeddings but received ${items.length}`,
        };
    }
    const embeddings = [];
    for (let i = 0; i < expectedCount; i++) {
        const embedding = items[i]?.embedding;
        if (!embedding || !Array.isArray(embedding)) {
            return { error: `No embedding returned for chunk ${i}` };
        }
        embeddings.push(embedding);
    }
    if (expectedCount === 1) {
        return { embedding: embeddings[0] };
    }
    if (strategy === 'truncate') {
        return { embedding: embeddings[0] };
    }
    try {
        return { embedding: meanPoolEmbeddings(embeddings) };
    }
    catch (err) {
        return { error: String(err) };
    }
}
function buildEmbeddingOverflowMetadata(prepared) {
    if (!prepared.overflow) {
        return undefined;
    }
    return {
        embeddingOverflow: true,
        strategy: prepared.strategy,
        chunkCount: prepared.chunkCount,
        estimatedTokens: prepared.estimatedTokens,
    };
}
//# sourceMappingURL=textOverflow.js.map