"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleCombinedAudio = exports.getAudioTranscript = void 0;
exports.sttContentTypeForAudioFormat = sttContentTypeForAudioFormat;
const dedent_1 = __importDefault(require("dedent"));
const lodash_1 = __importDefault(require("lodash"));
const logger_1 = __importDefault(require("../../logger"));
const defaults_1 = require("../../providers/defaults");
const deepgram_1 = require("../../providers/stt/deepgram");
const language_1 = require("./language");
/** Coerce provider output to a string safe for .replace / JSON parsing. */
function normalizeAudioGradingModelOutput(raw) {
    if (raw == null) {
        return null;
    }
    if (typeof raw === 'string') {
        return raw;
    }
    if (typeof raw === 'object') {
        return JSON.stringify(raw);
    }
    return String(raw);
}
/** Deepgram listen API Content-Type for encoded audio bytes. */
function sttContentTypeForAudioFormat(format) {
    const f = (format || 'wav').toLowerCase().replace(/^\./, '');
    if (f === 'mp3' || f === 'mpeg') {
        return 'audio/mpeg';
    }
    if (f === 'opus') {
        return 'audio/opus';
    }
    return 'audio/wav';
}
const getAudioTranscript = async (outputAudio, language = '', workspaceId) => {
    let languageTokenUsage;
    let sttProvider = new deepgram_1.DeepGramSttProvider('enhanced', {
        config: {
            diarize: false,
            punctuate: true,
            language: language || 'en',
            utterances: true,
            detect_language: false,
        },
    });
    if (!language) {
        // Detect audio language (and capture its token usage)
        const langResult = await (0, language_1.getAudioLanguage)(outputAudio);
        // If langResult.language contains more than 2 words, set language to 'en'
        if (langResult.language === 'none' || langResult.language.trim().split(/\s+/).length > 2) {
            sttProvider = new deepgram_1.DeepGramSttProvider();
        }
        else {
            sttProvider.config.language = langResult.language;
        }
        languageTokenUsage = langResult.tokenUsage;
    }
    const sttReponse = await sttProvider.transcribeBase64Audio(outputAudio.data || '', sttContentTypeForAudioFormat(outputAudio?.format));
    if (sttReponse.error) {
        throw new Error(`STT failed: ${sttReponse.error}`);
    }
    return {
        transcript: sttReponse.output || '',
        languageTokenUsage,
        sttTokenUsage: sttReponse.tokenUsage,
    };
};
exports.getAudioTranscript = getAudioTranscript;
const handleCombinedAudio = async ({ assertion, renderedValue, prompt, output, outputAudio, test, workspaceId, }) => {
    const textProvider = (await (0, defaults_1.getDefaultProviders)(undefined, workspaceId)).audioProvider; //TODO: Hardcoded for now
    const providerConfig = textProvider.config;
    console.log('handleCombinedAudio audio provider used', {
        providerId: textProvider.id(),
        providerType: textProvider.constructor.name,
        providerLabel: textProvider.label,
        providerString: String(textProvider),
        deploymentName: 'deploymentName' in textProvider
            ? textProvider.deploymentName
            : undefined,
        modelName: 'modelName' in textProvider ? textProvider.modelName : undefined,
        config: providerConfig
            ? {
                max_tokens: providerConfig.max_tokens,
                max_completion_tokens: providerConfig.max_completion_tokens,
                isReasoningModel: providerConfig.isReasoningModel ?? providerConfig.o1,
                passthroughKeys: providerConfig.passthrough && typeof providerConfig.passthrough === 'object'
                    ? Object.keys(providerConfig.passthrough)
                    : undefined,
                passthroughMaxTokens: providerConfig.passthrough
                    ?.max_tokens,
            }
            : undefined,
    });
    const allPrompts = JSON.parse(prompt);
    if (lodash_1.default.isEmpty(outputAudio)) {
        return {
            pass: false,
            score: 0,
            reason: 'No audio provided',
            tokensUsed: {
                total: 0,
                prompt: 0,
                completion: 0,
                model: defaults_1.AUDIO_MODEL_NAME,
            },
        };
    }
    let instructions = '';
    let metrics = '';
    let i = 0;
    for (i = 0; i < allPrompts.length; i++) {
        const singlePrompt = allPrompts[i];
        if (singlePrompt.prompt) {
            instructions = `${instructions}${i + 1}. ${singlePrompt.prompt}\n`;
        }
        if (singlePrompt.metric) {
            metrics = `${metrics}${singlePrompt.metric}\n`;
        }
    }
    let reference_transcript = output;
    let languageTokenUsage;
    let sttTokenUsage;
    if (lodash_1.default.isEmpty(reference_transcript) || reference_transcript === 'no-transcript') {
        const language = test?.metadata?.language;
        logger_1.default.debug(`Audio language from test metadata: ${language}`);
        try {
            const transcriptResult = await (0, exports.getAudioTranscript)(outputAudio, language, workspaceId);
            reference_transcript = transcriptResult.transcript;
            languageTokenUsage = transcriptResult.languageTokenUsage;
            sttTokenUsage = transcriptResult.sttTokenUsage;
        }
        catch (err) {
            return {
                pass: false,
                score: 0,
                reason: err instanceof Error ? err.message : String(err),
                tokensUsed: {
                    total: 0,
                    prompt: 0,
                    completion: 0,
                    model: defaults_1.AUDIO_MODEL_NAME,
                },
            };
        }
    }
    const systemPrompt = (0, dedent_1.default) `
        You are a helpful assistant that checks for the quality of the attached audio between a AI Bot agent and a customer. If the audio only has one participant, then treat that as the response from AI Bot agent.
        You will be provided with the audio and instructions to analyze the audio.
        You will also be provided with the metrics to be included in the final JSON output.
        Transcribe the audio first using the below instructions:
        - Transcribe strictly based on what is heard in the audio and do not be creative.
        - While transcribing the audio, always diarize the speakers and their corresponding sentences.
        - If there is background noise or someone else is speaking in the background, ignore those sentences and do not transcribe those.
        - You will be provided with the transcription of the audio from another tool called reference_transcription which you can use as a good reference while creating your own transcript. The reference_transcription may not have diarization.
        - If the reference_transcription is not empty, then only consider the sentences in the audio which are also present in the reference_transcription while transcribing. Do not add any additional sentences which are not present in the reference_transcription.
        - If any sentences are repeated in the audio, transcribe them also repeatedly. 
        - It is not always necessary that the customer would have responded to the AI Bot agent, so do not assume that the customer would have responded to every question asked by the AI Bot agent. It is Ok to have multiple sentences in the transcript spoken by AI Bot agent without any response from customer.
        - If the audio is in a language other than English, transcribe the audio in the same written language script as the audio.
        - You will also be provided with the original transcript coming from AI Bot - called original_transcription. This original_transcription may contain issues like incorrect transcription, missing transcription or even additional ghost words or sentences. If the original_transcription is present, it will give you a good idea of what the speakers are trying to say. Use this original_transcription as a secondary reference while creating your own transcript, but do not take any additional ghost words or sentences which cannot be heard in the audio or present in the reference_transcription.
        Transcribe the audio and provide it in a JSON field called "transcript".
        
        <reference_transcription>
          ${reference_transcript}
        </reference_transcription>
        <original_transcription>
          ${outputAudio?.transcript || ''}
        </original_transcription>
        
        Please follow the instructions and generate the metrics carefully as valid JSON. DO NOT surround the JSON with any code block or any other text.
        Generate the transcript first and then follow the additional instructions as below:
        `;
    const userPrompt = (0, dedent_1.default) `
        <additional_instructions>
        ${instructions}
        </additional_instructions>

        Example JSON Output Structure  
        json
        {
        "transcript": [
            {
            "customer support": "Good afternoon, thank you for calling. This is Jenny speaking. How may I assist you today?"
            },
            {
            "customer1": "Hi, um, yeah, I received my order for [Product Name], and there seems to be an issue with one of the items."
            }
          ],
          ${metrics}
        }
        When you generate your final JSON, please follow the above structure and fill in the correct values based on your analysis of the audio.
        `;
    logger_1.default.debug(`systemPrompt, ${systemPrompt}\npromptSteps, ${userPrompt}`);
    const llmPrompt = [
        {
            role: 'system',
            content: [{ type: 'text', text: systemPrompt }],
        },
        {
            role: 'user',
            content: [
                { type: 'text', text: userPrompt },
                {
                    type: 'input_audio',
                    input_audio: { data: outputAudio?.data, format: outputAudio?.format },
                },
            ],
        },
    ];
    const promptText = JSON.stringify(llmPrompt);
    const resp = await textProvider.callApi(promptText);
    console.log(`response from audio provider, ${JSON.stringify(resp)}`);
    if (resp.error) {
        return {
            pass: false,
            score: 0,
            reason: resp.error,
            tokensUsed: {
                total: resp.tokenUsage?.total ?? 0,
                prompt: resp.tokenUsage?.prompt ?? 0,
                completion: resp.tokenUsage?.completion ?? 0,
                model: defaults_1.AUDIO_MODEL_NAME,
            },
        };
    }
    const normalizedOutput = normalizeAudioGradingModelOutput(resp.output);
    if (normalizedOutput == null) {
        return {
            pass: false,
            score: 0,
            reason: 'Audio grading model returned no output',
            tokensUsed: {
                total: resp.tokenUsage?.total ?? 0,
                prompt: resp.tokenUsage?.prompt ?? 0,
                completion: resp.tokenUsage?.completion ?? 0,
                model: defaults_1.AUDIO_MODEL_NAME,
            },
        };
    }
    let result = normalizedOutput;
    // Strip json code block if present
    result = result
        .replace(/```json/g, '')
        .replace(/```/g, '')
        .trim();
    if (result.length === 0) {
        return {
            pass: false,
            score: 0,
            reason: 'Audio grading model returned empty output',
            tokensUsed: {
                total: resp.tokenUsage?.total ?? 0,
                prompt: resp.tokenUsage?.prompt ?? 0,
                completion: resp.tokenUsage?.completion ?? 0,
                model: defaults_1.AUDIO_MODEL_NAME,
            },
        };
    }
    // Aggregate token usage from main grading call, language detection, and STT (if present)
    const baseTokens = resp.tokenUsage || {};
    const langTokens = languageTokenUsage || {};
    const sttTokens = sttTokenUsage || {};
    const aggregatedTokens = { ...baseTokens };
    const mergeTokens = (extra) => {
        Object.entries(extra || {}).forEach(([key, value]) => {
            if (typeof value === 'number') {
                const current = typeof aggregatedTokens[key] === 'number' ? aggregatedTokens[key] : 0;
                aggregatedTokens[key] = current + value;
            }
            else if (aggregatedTokens[key] === undefined) {
                // Preserve non-numeric metadata keys if grading call didn't set them
                aggregatedTokens[key] = value;
            }
        });
    };
    mergeTokens(langTokens);
    mergeTokens(sttTokens);
    return {
        pass: true,
        score: 1,
        reason: '',
        metadata: {
            result,
        },
        tokensUsed: { ...aggregatedTokens, model: defaults_1.AUDIO_MODEL_NAME },
    };
};
exports.handleCombinedAudio = handleCombinedAudio;
//# sourceMappingURL=combinedAudio.js.map