const CONTEXT_WINDOW_UNITS = {
    k: 1_000,
    m: 1_000_000,
};
function isPositiveInteger(value) {
    return Number.isFinite(value) && Number.isInteger(value) && value > 0;
}
export function validateContextWindowValue(value) {
    return isPositiveInteger(value) ? undefined : "Context window must be a positive integer token count";
}
export function parseContextWindowValue(input) {
    const trimmed = input.trim();
    if (!trimmed) {
        return { error: "Context window requires a value" };
    }
    const match = /^(\d+(?:\.\d+)?)([kKmM])?$/.exec(trimmed);
    if (!match) {
        return { error: `Invalid context window "${input}". Use a positive number, or a compact value like 400k or 1m.` };
    }
    const numericValue = Number(match[1]);
    const unit = match[2]?.toLowerCase();
    const multiplier = unit ? CONTEXT_WINDOW_UNITS[unit] : 1;
    const tokens = numericValue * multiplier;
    const validationError = validateContextWindowValue(tokens);
    if (validationError) {
        return { error: `Invalid context window "${input}". ${validationError}.` };
    }
    return { value: tokens };
}
export function formatContextWindow(value) {
    if (value >= 1_000_000) {
        const millions = value / 1_000_000;
        return millions % 1 === 0 ? `${millions}m` : `${millions.toFixed(1)}m`;
    }
    if (value >= 1_000) {
        const thousands = value / 1_000;
        return thousands % 1 === 0 ? `${thousands}k` : `${thousands.toFixed(1)}k`;
    }
    return String(value);
}
export function normalizeContextWindowOptions(values) {
    const seen = new Set();
    const normalized = [];
    for (const value of values ?? []) {
        if (!isPositiveInteger(value) || seen.has(value))
            continue;
        seen.add(value);
        normalized.push(value);
    }
    return normalized.sort((a, b) => a - b);
}
export function getModelDefaultContextWindow(model) {
    return isPositiveInteger(model.defaultContextWindow ?? 0) ? model.defaultContextWindow : model.contextWindow;
}
/**
 * Effective input-token budget for compaction/overflow decisions. Equals the displayed
 * `contextWindow` unless a smaller hard input cap (`maxInputTokens`, e.g. GitHub Copilot's
 * `max_prompt_tokens`) is advertised, in which case the lower of the two is used. This lets a model
 * display its full/branded window while compaction and overflow recovery respect the real,
 * server-enforced input limit.
 */
export function getEffectiveInputBudget(model) {
    const cap = model.maxInputTokens;
    return isPositiveInteger(cap ?? 0) ? Math.min(model.contextWindow, cap) : model.contextWindow;
}
export function getSupportedContextWindows(model) {
    return normalizeContextWindowOptions([getModelDefaultContextWindow(model), ...(model.contextWindowOptions ?? [])]);
}
export function withContextWindowOptions(model, contextWindowOptions) {
    return {
        ...model,
        defaultContextWindow: getModelDefaultContextWindow(model),
        contextWindowOptions: normalizeContextWindowOptions(contextWindowOptions),
    };
}
function resolveSelectableContextWindow(model, requestedContextWindow, supported, options) {
    if (supported.includes(requestedContextWindow)) {
        return requestedContextWindow;
    }
    if (options.allowCopilotLongContextFallback !== true || model.provider !== "github-copilot") {
        return undefined;
    }
    const defaultContextWindow = getModelDefaultContextWindow(model);
    const candidates = supported.filter((contextWindow) => contextWindow <= requestedContextWindow && contextWindow > defaultContextWindow);
    return candidates.length > 0 ? Math.max(...candidates) : undefined;
}
export function selectContextWindow(model, contextWindow, options = {}) {
    const validationError = validateContextWindowValue(contextWindow);
    if (validationError) {
        return { error: validationError };
    }
    const apiModel = model;
    const supported = getSupportedContextWindows(apiModel);
    const selectedContextWindow = resolveSelectableContextWindow(apiModel, contextWindow, supported, options);
    if (selectedContextWindow === undefined) {
        return {
            error: `Context window ${formatContextWindow(contextWindow)} is not supported by ${model.provider}/${model.id}. Supported values: ${supported.map(formatContextWindow).join(", ")}.`,
        };
    }
    return {
        model: {
            ...model,
            defaultContextWindow: getModelDefaultContextWindow(apiModel),
            contextWindow: selectedContextWindow,
            contextWindowOptions: supported,
        },
        contextWindow: selectedContextWindow,
    };
}
//# sourceMappingURL=context-window.js.map