"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadApiProvider = exports.loadApiProviders = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
const js_yaml_1 = __importDefault(require("js-yaml"));
const logger_1 = __importDefault(require("./logger"));
const openai_1 = require("./providers/openai");
const anthropic_1 = require("./providers/anthropic");
const replicate_1 = require("./providers/replicate");
const localai_1 = require("./providers/localai");
const palm_1 = require("./providers/palm");
const llama_1 = require("./providers/llama");
const ollama_1 = require("./providers/ollama");
const vertex_1 = require("./providers/vertex");
const mistral_1 = require("./providers/mistral");
const webhook_1 = require("./providers/webhook");
const scriptCompletion_1 = require("./providers/scriptCompletion");
const azureopenai_1 = require("./providers/azureopenai");
const huggingface_1 = require("./providers/huggingface");
const bedrock_1 = require("./providers/bedrock");
const pythonCompletion_1 = require("./providers/pythonCompletion");
const cohere_1 = require("./providers/cohere");
const CloudflareAiProviders = __importStar(require("./providers/cloudflare-ai"));
const bam_1 = require("./providers/bam");
const portkey_1 = require("./providers/portkey");
const http_1 = require("./providers/http");
const esm_1 = require("./esm");
const voyage_1 = require("./providers/voyage");
async function loadApiProviders(providerPaths, options = {}) {
    const { basePath, env } = options;
    if (typeof providerPaths === 'string') {
        return [await loadApiProvider(providerPaths, { basePath, env })];
    }
    else if (typeof providerPaths === 'function') {
        return [
            {
                id: () => 'custom-function',
                callApi: providerPaths,
            },
        ];
    }
    else if (Array.isArray(providerPaths)) {
        return Promise.all(providerPaths.map((provider, idx) => {
            if (typeof provider === 'string') {
                return loadApiProvider(provider, { basePath, env });
            }
            else if (typeof provider === 'function') {
                return {
                    id: provider.label ? () => provider.label : () => `custom-function-${idx}`,
                    callApi: provider,
                };
            }
            else if (provider.id) {
                // List of ProviderConfig objects
                return loadApiProvider(provider.id, {
                    options: provider,
                    basePath,
                    env,
                });
            }
            else {
                // List of { id: string, config: ProviderConfig } objects
                const id = Object.keys(provider)[0];
                const providerObject = provider[id];
                const context = { ...providerObject, id: providerObject.id || id };
                return loadApiProvider(id, { options: context, basePath, env });
            }
        }));
    }
    throw new Error('Invalid providers list');
}
exports.loadApiProviders = loadApiProviders;
// FIXME(ian): Make loadApiProvider handle all the different provider types (string, ProviderOptions, ApiProvider, etc), rather than the callers.
async function loadApiProvider(providerPath, context = {}) {
    const { options = {}, basePath, env } = context;
    const providerOptions = {
        // Hack(ian): Override id with label. This makes it so that debug and display info, which rely on id, will use the label instead.
        id: options.label || options.id,
        config: {
            ...options.config,
            basePath,
        },
        env,
    };
    let ret;
    if (providerPath.startsWith('file://') &&
        (providerPath.endsWith('.yaml') || providerPath.endsWith('.yml'))) {
        const filePath = providerPath.slice('file://'.length);
        const yamlContent = js_yaml_1.default.load(fs_1.default.readFileSync(filePath, 'utf8'));
        (0, tiny_invariant_1.default)(yamlContent, `Provider config ${filePath} is undefined`);
        (0, tiny_invariant_1.default)(yamlContent.id, `Provider config ${filePath} must have an id`);
        logger_1.default.info(`Loaded provider ${yamlContent.id} from ${filePath}`);
        ret = await loadApiProvider(yamlContent.id, { ...context, options: yamlContent });
    }
    else if (providerPath === 'echo') {
        ret = {
            id: () => 'echo',
            callApi: async (input) => ({ output: input }),
        };
    }
    else if (providerPath.startsWith('exec:')) {
        // Load script module
        const scriptPath = providerPath.split(':')[1];
        ret = new scriptCompletion_1.ScriptCompletionProvider(scriptPath, providerOptions);
    }
    else if (providerPath.startsWith('python:')) {
        const scriptPath = providerPath.split(':')[1];
        ret = new pythonCompletion_1.PythonProvider(scriptPath, providerOptions);
    }
    else if (providerPath.startsWith('openai:')) {
        // Load OpenAI module
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits.slice(2).join(':');
        if (modelType === 'chat') {
            ret = new openai_1.OpenAiChatCompletionProvider(modelName || 'gpt-3.5-turbo', providerOptions);
        }
        else if (modelType === 'embedding' || modelType === 'embeddings') {
            ret = new openai_1.OpenAiEmbeddingProvider(modelName || 'text-embedding-3-large', providerOptions);
        }
        else if (modelType === 'completion') {
            ret = new openai_1.OpenAiCompletionProvider(modelName || 'gpt-3.5-turbo-instruct', providerOptions);
        }
        else if (modelType === 'moderation') {
            ret = new openai_1.OpenAiModerationProvider(modelName || 'text-moderation-latest', providerOptions);
        }
        else if (openai_1.OpenAiChatCompletionProvider.OPENAI_CHAT_MODEL_NAMES.includes(modelType)) {
            ret = new openai_1.OpenAiChatCompletionProvider(modelType, providerOptions);
        }
        else if (openai_1.OpenAiCompletionProvider.OPENAI_COMPLETION_MODEL_NAMES.includes(modelType)) {
            ret = new openai_1.OpenAiCompletionProvider(modelType, providerOptions);
        }
        else if (modelType === 'assistant') {
            ret = new openai_1.OpenAiAssistantProvider(modelName, providerOptions);
        }
        else if (modelType === 'image') {
            ret = new openai_1.OpenAiImageProvider(modelName, providerOptions);
        }
        else {
            throw new Error(`Unknown OpenAI model type: ${modelType}. Use one of the following providers: openai:chat:<model name>, openai:completion:<model name>, openai:embeddings:<model name>, openai:image:<model name>`);
        }
    }
    else if (providerPath.startsWith('azureopenai:')) {
        // Load Azure OpenAI module
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const deploymentName = splits[2];
        if (modelType === 'chat') {
            ret = new azureopenai_1.AzureOpenAiChatCompletionProvider(deploymentName, providerOptions);
        }
        else if (modelType === 'assistant') {
            ret = new azureopenai_1.AzureOpenAiAssistantProvider(deploymentName, providerOptions);
        }
        else if (modelType === 'embedding' || modelType === 'embeddings') {
            ret = new azureopenai_1.AzureOpenAiEmbeddingProvider(deploymentName || 'text-embedding-ada-002', providerOptions);
        }
        else if (modelType === 'completion') {
            ret = new azureopenai_1.AzureOpenAiCompletionProvider(deploymentName, providerOptions);
        }
        else {
            throw new Error(`Unknown Azure OpenAI model type: ${modelType}. Use one of the following providers: azureopenai:chat:<model name>, azureopenai:assistant:<assistant id>, azureopenai:completion:<model name>`);
        }
    }
    else if (providerPath.startsWith('openrouter:')) {
        const splits = providerPath.split(':');
        const modelName = splits.slice(1).join(':');
        ret = new openai_1.OpenAiChatCompletionProvider(modelName, {
            ...providerOptions,
            config: {
                ...providerOptions.config,
                apiBaseUrl: 'https://openrouter.ai/api/v1',
                apiKeyEnvar: 'OPENROUTER_API_KEY',
            },
        });
    }
    else if (providerPath.startsWith('portkey:')) {
        const splits = providerPath.split(':');
        const modelName = splits.slice(1).join(':');
        ret = new portkey_1.PortkeyChatCompletionProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('anthropic:')) {
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits[2];
        if (modelType === 'messages') {
            ret = new anthropic_1.AnthropicMessagesProvider(modelName, providerOptions);
        }
        else if (modelType === 'completion') {
            ret = new anthropic_1.AnthropicCompletionProvider(modelName, providerOptions);
        }
        else if (anthropic_1.AnthropicCompletionProvider.ANTHROPIC_COMPLETION_MODELS.includes(modelType)) {
            ret = new anthropic_1.AnthropicCompletionProvider(modelType, providerOptions);
        }
        else {
            throw new Error(`Unknown Anthropic model type: ${modelType}. Use one of the following providers: anthropic:completion:<model name>`);
        }
    }
    else if (providerPath.startsWith('voyage:')) {
        ret = new voyage_1.VoyageEmbeddingProvider(providerPath.split(':')[1], providerOptions);
    }
    else if (providerPath.startsWith('bedrock:')) {
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits.slice(2).join(':');
        if (modelType === 'completion') {
            // Backwards compatibility: `completion` used to be required
            ret = new bedrock_1.AwsBedrockCompletionProvider(modelName, providerOptions);
        }
        else if (modelType === 'embeddings' || modelType === 'embedding') {
            ret = new bedrock_1.AwsBedrockEmbeddingProvider(modelName, providerOptions);
        }
        else {
            ret = new bedrock_1.AwsBedrockCompletionProvider(`${modelType}${modelName ? `:${modelName}` : ''}`, providerOptions);
        }
    }
    else if (providerPath.startsWith('huggingface:') || providerPath.startsWith('hf:')) {
        const splits = providerPath.split(':');
        if (splits.length < 3) {
            throw new Error(`Invalid Huggingface provider path: ${providerPath}. Use one of the following providers: huggingface:feature-extraction:<model name>, huggingface:text-generation:<model name>, huggingface:text-classification:<model name>, huggingface:token-classification:<model name>`);
        }
        const modelName = splits.slice(2).join(':');
        if (splits[1] === 'feature-extraction') {
            ret = new huggingface_1.HuggingfaceFeatureExtractionProvider(modelName, providerOptions);
        }
        else if (splits[1] === 'sentence-similarity') {
            ret = new huggingface_1.HuggingfaceSentenceSimilarityProvider(modelName, providerOptions);
        }
        else if (splits[1] === 'text-generation') {
            ret = new huggingface_1.HuggingfaceTextGenerationProvider(modelName, providerOptions);
        }
        else if (splits[1] === 'text-classification') {
            ret = new huggingface_1.HuggingfaceTextClassificationProvider(modelName, providerOptions);
        }
        else if (splits[1] === 'token-classification') {
            ret = new huggingface_1.HuggingfaceTokenExtractionProvider(modelName, providerOptions);
        }
        else {
            throw new Error(`Invalid Huggingface provider path: ${providerPath}. Use one of the following providers: huggingface:feature-extraction:<model name>, huggingface:text-generation:<model name>, huggingface:text-classification:<model name>, huggingface:token-classification:<model name>`);
        }
    }
    else if (providerPath.startsWith('replicate:')) {
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits.slice(2).join(':');
        if (modelType === 'moderation') {
            ret = new replicate_1.ReplicateModerationProvider(modelName, providerOptions);
        }
        else {
            // By default, there is no model type.
            ret = new replicate_1.ReplicateProvider(modelType + ':' + modelName, providerOptions);
        }
    }
    else if (providerPath.startsWith('bam:')) {
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits.slice(2).join(':');
        if (modelType === 'chat') {
            ret = new bam_1.BAMChatProvider(modelName || 'ibm/granite-13b-chat-v2', providerOptions);
        }
        else {
            throw new Error(`Invalid BAM provider: ${providerPath}. Use one of the following providers: bam:chat:<model name>`);
        }
    }
    else if (providerPath.startsWith('cloudflare-ai:')) {
        // Load Cloudflare AI
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const deploymentName = splits[2];
        if (modelType === 'chat') {
            ret = new CloudflareAiProviders.CloudflareAiChatCompletionProvider(deploymentName, providerOptions);
        }
        else if (modelType === 'embedding' || modelType === 'embeddings') {
            ret = new CloudflareAiProviders.CloudflareAiEmbeddingProvider(deploymentName, providerOptions);
        }
        else if (modelType === 'completion') {
            ret = new CloudflareAiProviders.CloudflareAiCompletionProvider(deploymentName, providerOptions);
        }
        else {
            throw new Error(`Unknown Cloudflare AI model type: ${modelType}. Use one of the following providers: cloudflare-ai:chat:<model name>, cloudflare-ai:completion:<model name>, cloudflare-ai:embedding:`);
        }
    }
    else if (providerPath.startsWith('webhook:')) {
        const webhookUrl = providerPath.substring('webhook:'.length);
        ret = new webhook_1.WebhookProvider(webhookUrl, providerOptions);
    }
    else if (providerPath === 'llama' || providerPath.startsWith('llama:')) {
        const modelName = providerPath.split(':')[1];
        ret = new llama_1.LlamaProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('ollama:embeddings:') ||
        providerPath.startsWith('ollama:embedding:')) {
        const modelName = providerPath.split(':')[2];
        ret = new ollama_1.OllamaEmbeddingProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('ollama:')) {
        const splits = providerPath.split(':');
        const firstPart = splits[1];
        if (firstPart === 'chat') {
            const modelName = splits.slice(2).join(':');
            ret = new ollama_1.OllamaChatProvider(modelName, providerOptions);
        }
        else if (firstPart === 'completion') {
            const modelName = splits.slice(2).join(':');
            ret = new ollama_1.OllamaCompletionProvider(modelName, providerOptions);
        }
        else {
            // Default to completion provider
            const modelName = splits.slice(1).join(':');
            ret = new ollama_1.OllamaCompletionProvider(modelName, providerOptions);
        }
    }
    else if (providerPath.startsWith('palm:') || providerPath.startsWith('google:')) {
        const modelName = providerPath.split(':')[1];
        ret = new palm_1.PalmChatProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('vertex')) {
        const modelName = providerPath.split(':')[1];
        ret = new vertex_1.VertexChatProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('mistral:')) {
        const modelName = providerPath.split(':')[1];
        ret = new mistral_1.MistralChatCompletionProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('cohere:')) {
        const modelName = providerPath.split(':')[1];
        ret = new cohere_1.CohereChatCompletionProvider(modelName, providerOptions);
    }
    else if (providerPath.startsWith('localai:')) {
        const splits = providerPath.split(':');
        const modelType = splits[1];
        const modelName = splits[2];
        if (modelType === 'chat') {
            ret = new localai_1.LocalAiChatProvider(modelName, providerOptions);
        }
        else if (modelType === 'completion') {
            ret = new localai_1.LocalAiCompletionProvider(modelName, providerOptions);
        }
        else if (modelType === 'embedding' || modelType === 'embeddings') {
            ret = new localai_1.LocalAiEmbeddingProvider(modelName, providerOptions);
        }
        else {
            ret = new localai_1.LocalAiChatProvider(modelType, providerOptions);
        }
    }
    else if (providerPath.startsWith('http:') || providerPath.startsWith('https:')) {
        ret = new http_1.HttpProvider(providerPath, providerOptions);
    }
    else if (providerPath === 'promptfoo:redteam:iterative') {
        const RedteamIterativeProvider = (await Promise.resolve(`${path_1.default.join(__dirname, './redteam/iterative')}`).then(s => __importStar(require(s))))
            .default;
        ret = new RedteamIterativeProvider(providerOptions);
    }
    else {
        if (providerPath.startsWith('file://')) {
            providerPath = providerPath.slice('file://'.length);
        }
        // Load custom module
        const modulePath = path_1.default.isAbsolute(providerPath)
            ? providerPath
            : path_1.default.join(basePath || process.cwd(), providerPath);
        const CustomApiProvider = await (0, esm_1.importModule)(modulePath);
        ret = new CustomApiProvider(options);
        ret.label = ret.label || options.label;
    }
    ret.transform = options.transform;
    ret.delay = options.delay;
    return ret;
}
exports.loadApiProvider = loadApiProvider;
exports.default = {
    OpenAiCompletionProvider: openai_1.OpenAiCompletionProvider,
    OpenAiChatCompletionProvider: openai_1.OpenAiChatCompletionProvider,
    OpenAiAssistantProvider: openai_1.OpenAiAssistantProvider,
    AnthropicCompletionProvider: anthropic_1.AnthropicCompletionProvider,
    AnthropicMessagesProvider: anthropic_1.AnthropicMessagesProvider,
    ReplicateProvider: replicate_1.ReplicateProvider,
    LocalAiCompletionProvider: localai_1.LocalAiCompletionProvider,
    LocalAiChatProvider: localai_1.LocalAiChatProvider,
    BAMChatProvider: bam_1.BAMChatProvider,
    BAMEmbeddingProvider: bam_1.BAMEmbeddingProvider,
    loadApiProvider,
};
//# sourceMappingURL=providers.js.map