import { createHash } from "node:crypto";
const RESPONSES_FUNCTION_CALL_ID = /^fc_[A-Za-z0-9_-]{1,61}$/;
const RESPONSES_FUNCTION_CALL_ID_PREFIX = "fc_";
const MAX_RESPONSES_FUNCTION_CALL_ID_LENGTH = 64;
export const MIN_RESPONSES_MAX_OUTPUT_TOKENS = 16;
function isPlainObject(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
export function isOpenAIResponsesModel(model) {
    return model.api === "openai-responses";
}
export function isValidResponsesFunctionCallId(id) {
    return typeof id === "string" && RESPONSES_FUNCTION_CALL_ID.test(id);
}
function sha256Base64Url(value) {
    return createHash("sha256").update(value).digest("base64url");
}
function sanitizedCallIdFragment(callId) {
    return callId.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
}
export function responsesFunctionCallIdForCallId(callId) {
    if (typeof callId !== "string" || callId.length === 0)
        return undefined;
    const direct = `${RESPONSES_FUNCTION_CALL_ID_PREFIX}${callId}`;
    if (isValidResponsesFunctionCallId(direct))
        return direct;
    const sanitized = sanitizedCallIdFragment(callId);
    const hash = sha256Base64Url(callId).slice(0, 16);
    const suffixBudget = MAX_RESPONSES_FUNCTION_CALL_ID_LENGTH - RESPONSES_FUNCTION_CALL_ID_PREFIX.length;
    const suffix = sanitized.length > 0 ? `${sanitized.slice(0, suffixBudget - hash.length - 1)}_${hash}` : hash;
    return `${RESPONSES_FUNCTION_CALL_ID_PREFIX}${suffix}`;
}
function sanitizeResponsesFunctionCall(item) {
    if (item.type !== "function_call")
        return false;
    if (isValidResponsesFunctionCallId(item.id))
        return false;
    const synthesized = responsesFunctionCallIdForCallId(item.call_id);
    if (synthesized) {
        item.id = synthesized;
    }
    else {
        delete item.id;
    }
    return true;
}
export function sanitizeOpenAIResponsesPayload(payload, model) {
    if (!isOpenAIResponsesModel(model) || !isPlainObject(payload))
        return payload;
    let changed = false;
    let sanitizedPayload = payload;
    if (typeof payload.max_output_tokens === "number" &&
        Number.isFinite(payload.max_output_tokens) &&
        payload.max_output_tokens < MIN_RESPONSES_MAX_OUTPUT_TOKENS) {
        changed = true;
        sanitizedPayload = { ...sanitizedPayload, max_output_tokens: MIN_RESPONSES_MAX_OUTPUT_TOKENS };
    }
    if (Array.isArray(payload.input)) {
        let inputChanged = false;
        const input = payload.input.map((item) => {
            if (!isPlainObject(item))
                return item;
            const cloned = { ...item };
            inputChanged = sanitizeResponsesFunctionCall(cloned) || inputChanged;
            return cloned;
        });
        if (inputChanged) {
            changed = true;
            sanitizedPayload = { ...sanitizedPayload, input };
        }
    }
    return changed ? sanitizedPayload : payload;
}
//# sourceMappingURL=openai-responses-payload-sanitizer.js.map