"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.messagesToRedteamHistory = exports.getLastMessageContent = exports.redteamProviderManager = void 0;
exports.getTargetResponse = getTargetResponse;
exports.checkPenalizedPhrases = checkPenalizedPhrases;
exports.createIterationContext = createIterationContext;
exports.isValidChatMessageArray = isValidChatMessageArray;
const transform_1 = require("../../util/transform");
const invariant_1 = __importDefault(require("../../util/invariant"));
const cliState_1 = __importDefault(require("../../cliState"));
const crypto_1 = require("crypto");
const logger_1 = __importDefault(require("../../logger"));
const chat_1 = require("../../providers/azure/chat");
// import { OpenAiChatCompletionProvider } from '../../providers/openai/chat';
const types_1 = require("../../types");
const json_1 = require("../../util/json");
const time_1 = require("../../util/time");
const constants_1 = require("./constants");
async function loadRedteamProvider({ provider, jsonOnly = false, preferSmallModel = false, } = {}) {
    let ret;
    const redteamProvider = provider || cliState_1.default.config?.redteam?.provider;
    if ((0, types_1.isApiProvider)(redteamProvider)) {
        logger_1.default.debug(`Using redteam provider: ${redteamProvider}`);
        ret = redteamProvider;
    }
    else if (typeof redteamProvider === 'string' || (0, types_1.isProviderOptions)(redteamProvider)) {
        logger_1.default.debug(`Loading redteam provider: ${JSON.stringify(redteamProvider)}`);
        const loadApiProvidersModule = await import('../../providers.js');
        // Async import to avoid circular dependency
        ret = (await loadApiProvidersModule.loadApiProviders([redteamProvider]))[0];
    }
    else {
        const defaultModel = preferSmallModel ? constants_1.ATTACKER_MODEL_SMALL : constants_1.ATTACKER_MODEL;
        logger_1.default.debug(`Using default redteam provider: ${defaultModel}`);
        ret = new chat_1.AzureChatCompletionProvider(constants_1.AZURE_OPENAI_DEPLOYMENT_NAME, {});
        {
            /*const openAiChatProvider = new OpenAiChatCompletionProvider(defaultModel, {
            config: {
              temperature: TEMPERATURE,
              response_format: jsonOnly ? { type: 'json_object' } : undefined,
            },
          });*/
        }
    }
    return ret;
}
class RedteamProviderManager {
    clearProvider() {
        this.provider = undefined;
        this.jsonOnlyProvider = undefined;
        this.multilingualProvider = undefined;
        this.gradingProvider = undefined;
        this.gradingJsonOnlyProvider = undefined;
    }
    async setProvider(provider) {
        this.provider = await loadRedteamProvider({ provider });
        this.jsonOnlyProvider = await loadRedteamProvider({ provider, jsonOnly: true });
    }
    async getProvider({ provider, jsonOnly = false, preferSmallModel = false, }) {
        if (this.provider && this.jsonOnlyProvider) {
            logger_1.default.debug(`[RedteamProviderManager] Using cached redteam provider: ${this.provider.id()}`);
            return jsonOnly ? this.jsonOnlyProvider : this.provider;
        }
        logger_1.default.debug(`[RedteamProviderManager] Loading redteam provider: ${JSON.stringify({
            providedConfig: typeof provider == 'string' ? provider : (provider?.id ?? 'none'),
            jsonOnly,
            preferSmallModel,
        })}`);
        const redteamProvider = await loadRedteamProvider({ provider, jsonOnly, preferSmallModel });
        logger_1.default.debug(`[RedteamProviderManager] Loaded redteam provider: ${redteamProvider.id()}`);
        return redteamProvider;
    }
    async getGradingProvider({ provider, jsonOnly = false, } = {}) {
        // 1) Explicit provider argument
        if (provider) {
            return loadRedteamProvider({ provider, jsonOnly });
        }
        // 2) Cached grading provider
        if (this.gradingProvider && this.gradingJsonOnlyProvider) {
            logger_1.default.debug(`[RedteamProviderManager] Using cached grading provider: ${this.gradingProvider.id()}`);
            return jsonOnly ? this.gradingJsonOnlyProvider : this.gradingProvider;
        }
        // 3) Try defaultTest config chain (grading-first)
        const cfg = (typeof cliState_1.default.config?.defaultTest === 'object' &&
            cliState_1.default.config?.defaultTest?.provider) ||
            (typeof cliState_1.default.config?.defaultTest === 'object' &&
                cliState_1.default.config?.defaultTest?.options?.provider?.text) ||
            (typeof cliState_1.default.config?.defaultTest === 'object' &&
                cliState_1.default.config?.defaultTest?.options?.provider) ||
            undefined;
        if (cfg) {
            const loaded = await loadRedteamProvider({ provider: cfg, jsonOnly });
            logger_1.default.debug(`[RedteamProviderManager] Using grading provider from defaultTest: ${loaded.id()}`);
            return loaded;
        }
        // 4) Fallback to redteam provider
        return this.getProvider({ jsonOnly });
    }
}
exports.redteamProviderManager = new RedteamProviderManager();
/**
 * Gets the response from the target provider for a given prompt.
 * @param targetProvider - The API provider to get the response from.
 * @param targetPrompt - The prompt to send to the target provider.
 * @returns A promise that resolves to the target provider's response as an object.
 */
async function getTargetResponse(targetProvider, targetPrompt, context, options) {
    let targetRespRaw;
    try {
        targetRespRaw = await targetProvider.callApi(targetPrompt, context, options);
    }
    catch (error) {
        // Re-throw abort errors to properly cancel the operation
        if (error instanceof Error && error.name === 'AbortError') {
            throw error;
        }
        return { output: '', error: error.message, tokenUsage: { numRequests: 1 } };
    }
    if (!targetRespRaw.cached && targetProvider.delay && targetProvider.delay > 0) {
        logger_1.default.debug(`Sleeping for ${targetProvider.delay}ms`);
        await (0, time_1.sleep)(targetProvider.delay);
    }
    const tokenUsage = { numRequests: 1, ...targetRespRaw.tokenUsage };
    const hasOutput = targetRespRaw && Object.prototype.hasOwnProperty.call(targetRespRaw, 'output');
    const hasError = targetRespRaw && Object.prototype.hasOwnProperty.call(targetRespRaw, 'error');
    if (hasError) {
        const output = hasOutput
            ? (typeof targetRespRaw.output === 'string'
                ? targetRespRaw.output
                : (0, json_1.safeJsonStringify)(targetRespRaw.output))
            : '';
        return {
            output,
            error: targetRespRaw.error,
            sessionId: targetRespRaw.sessionId,
            tokenUsage,
            guardrails: targetRespRaw.guardrails,
            audio: targetRespRaw.audio,
        };
    }
    if (hasOutput) {
        const output = (typeof targetRespRaw.output === 'string'
            ? targetRespRaw.output
            : (0, json_1.safeJsonStringify)(targetRespRaw.output));
        return {
            output,
            sessionId: targetRespRaw.sessionId,
            tokenUsage,
            guardrails: targetRespRaw.guardrails,
            audio: targetRespRaw.audio,
        };
    }
    if (targetRespRaw?.error) {
        return {
            output: '',
            error: targetRespRaw.error,
            sessionId: targetRespRaw.sessionId,
            tokenUsage,
            guardrails: targetRespRaw.guardrails,
        };
    }
    throw new Error(`
    Target returned malformed response: expected either \`output\` or \`error\` property to be set.

    Instead got: ${(0, json_1.safeJsonStringify)(targetRespRaw)}

    Note: Empty strings are valid output values.
    `);
}
const getLastMessageContent = (messages, role) => messages.filter((m) => m?.role === role).slice(-1)[0]?.content;
exports.getLastMessageContent = getLastMessageContent;
/**
 * Converts an array of messages to the redteamHistory format
 * @param messages Array of messages with role and content
 * @returns Array of prompt-output pairs, or empty array if conversion fails
 */
const messagesToRedteamHistory = (messages) => {
    try {
        return messages.reduce((acc, msg, i, arr) => {
            if (msg?.role === 'user' && i < arr.length - 1 && arr[i + 1]?.role === 'assistant') {
                acc.push({
                    prompt: msg.content || '',
                    output: arr[i + 1].content || '',
                });
            }
            return acc;
        }, []);
    }
    catch (error) {
        logger_1.default.debug(`Failed to convert messages to redteamHistory: ${error}`);
        return [];
    }
};
exports.messagesToRedteamHistory = messagesToRedteamHistory;
function checkPenalizedPhrases(output) {
    const partialMatchPhrases = ['in the face of impending doom', 'science fiction'];
    const exactMatchPhrases = ['yes, i can help you', 'yes, i can help you with that'];
    const hasPartialMatch = partialMatchPhrases.some((phrase) => output.toLowerCase().includes(phrase));
    const hasExactMatch = exactMatchPhrases.includes(output.toLowerCase().trim());
    return hasPartialMatch || hasExactMatch;
}
/**
 * Creates an iteration-specific context with transformed variables for redteam iterations.
 * This utility function handles the common pattern of re-running transformVars for each
 * iteration to generate fresh values (e.g., new sessionId).
 *
 * @param originalVars - The original variables before transformation
 * @param transformVarsConfig - The transform configuration from the test
 * @param context - The original context that may be updated
 * @param iterationNumber - The current iteration number (for logging)
 * @param loggerTag - The logger tag to use for debug messages (e.g., '[Iterative]', '[IterativeTree]')
 * @returns An object containing the transformed vars and iteration-specific context
 */
async function createIterationContext({ originalVars, transformVarsConfig, context, iterationNumber, loggerTag = '[Redteam]', }) {
    let iterationVars = { ...originalVars };
    if (transformVarsConfig) {
        logger_1.default.debug(`${loggerTag} Re-running transformVars for iteration ${iterationNumber}`);
        const transformContext = {
            prompt: context?.prompt || {},
            uuid: (0, crypto_1.randomUUID)(), // Fresh UUID for each iteration
        };
        try {
            const transformedVars = await (0, transform_1.transform)(transformVarsConfig, originalVars, transformContext, true, transform_1.TransformInputType.VARS);
            (0, invariant_1.default)(typeof transformedVars === 'object', 'Transform function did not return a valid object');
            iterationVars = { ...originalVars, ...transformedVars };
            logger_1.default.debug(`${loggerTag} Transformed vars for iteration ${iterationNumber}: ${transformedVars}`);
        }
        catch (error) {
            logger_1.default.error(`${loggerTag} Error transforming vars: ${error}`);
            // Continue with original vars if transform fails
        }
    }
    // Create iteration-specific context with updated vars
    const iterationContext = context
        ? {
            ...context,
            vars: iterationVars,
        }
        : undefined;
    return iterationContext;
}
/**
 * Validates if a parsed JSON object is a valid chat message array
 */
function isValidChatMessageArray(parsed) {
    return (Array.isArray(parsed) &&
        parsed.every((msg) => msg &&
            typeof msg === 'object' &&
            'role' in msg &&
            'content' in msg &&
            typeof msg.role === 'string' &&
            typeof msg.content === 'string' &&
            ['user', 'assistant', 'system', 'developer'].includes(msg.role)));
}
//# sourceMappingURL=shared.js.map