"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 __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Clients = exports.AIClient = exports.XaiVideoPricing = exports.XaiImagePricing = exports.XaiTextPricing = exports.GeminiPricing = exports.AnthropicTextPricing = exports.OpenAiTextPricing = void 0;
const openai_1 = require("./openai");
const anthropic_1 = require("./anthropic");
const gemini_1 = require("./gemini");
const xai_1 = require("./xai");
const knowhow_1 = require("./knowhow");
const http_1 = require("./http");
const config_1 = require("../config");
const KnowhowClient_1 = require("../services/KnowhowClient");
const contextLimits_1 = require("./contextLimits");
const openai_2 = require("./pricing/openai");
Object.defineProperty(exports, "OpenAiTextPricing", { enumerable: true, get: function () { return openai_2.OpenAiTextPricing; } });
const anthropic_2 = require("./pricing/anthropic");
Object.defineProperty(exports, "AnthropicTextPricing", { enumerable: true, get: function () { return anthropic_2.AnthropicTextPricing; } });
const google_1 = require("./pricing/google");
Object.defineProperty(exports, "GeminiPricing", { enumerable: true, get: function () { return google_1.GeminiPricing; } });
const withRetry_1 = require("./withRetry");
const xai_2 = require("./pricing/xai");
Object.defineProperty(exports, "XaiTextPricing", { enumerable: true, get: function () { return xai_2.XaiTextPricing; } });
Object.defineProperty(exports, "XaiImagePricing", { enumerable: true, get: function () { return xai_2.XaiImagePricing; } });
Object.defineProperty(exports, "XaiVideoPricing", { enumerable: true, get: function () { return xai_2.XaiVideoPricing; } });
const cerebras_1 = require("./cerebras");
const groq_1 = require("./groq");
const github_1 = require("./github");
const nvidia_1 = require("./nvidia");
const openrouter_1 = require("./openrouter");
const deepseek_1 = require("./deepseek");
const mistral_1 = require("./mistral");
const copilot_1 = require("./copilot");
const llama_1 = require("./llama");
const fireworks_1 = require("./fireworks");
const BUILT_IN_PROVIDER_REGISTRY = {
    openai: { clientClass: openai_1.GenericOpenAiClient },
    anthropic: { clientClass: anthropic_1.GenericAnthropicClient },
    google: { clientClass: gemini_1.GenericGeminiClient },
    xai: { clientClass: xai_1.GenericXAIClient },
    cerebras: {
        clientClass: cerebras_1.GenericCerebrasClient,
    },
    groq: { clientClass: groq_1.GenericGroqClient },
    github: { clientClass: github_1.GenericGitHubModelsClient },
    nvidia: { clientClass: nvidia_1.GenericNvidiaClient },
    openrouter: { clientClass: openrouter_1.GenericOpenRouterClient },
    deepseek: { clientClass: deepseek_1.GenericDeepSeekClient },
    mistral: { clientClass: mistral_1.GenericMistralClient },
    "github-copilot": { clientClass: copilot_1.GitHubCopilotClient },
    llama: { clientClass: llama_1.GenericLlamaClient },
    fireworks: { clientClass: fireworks_1.GenericFireworksClient },
    knowhow: {
        createClient: (entry) => {
            const jwt = (0, KnowhowClient_1.loadKnowhowJwt)();
            if (!jwt)
                return null;
            return new knowhow_1.KnowhowGenericClient(KnowhowClient_1.KNOWHOW_API_URL, jwt);
        },
    },
};
const DEFAULT_PROVIDERS = [
    { provider: "openai", envKey: "OPENAI_API_KEY" },
    { provider: "anthropic", envKey: "ANTHROPIC_API_KEY" },
    { provider: "google", envKey: "GEMINI_API_KEY" },
    { provider: "xai", envKey: "XAI_API_KEY" },
    { provider: "cerebras", envKey: "CEREBRAS_API_KEY" },
    { provider: "knowhow" },
    { provider: "groq", envKey: "GROQ_API_KEY" },
    { provider: "github", envKey: "GITHUB_TOKEN" },
    { provider: "nvidia", envKey: "NVIDIA_API_KEY" },
    { provider: "openrouter", envKey: "OPENROUTER_API_KEY" },
    { provider: "deepseek", envKey: "DEEPSEEK_API_KEY" },
    { provider: "mistral", envKey: "MISTRAL_API_KEY" },
    { provider: "github-copilot", envKey: "GITHUB_COPILOT_TOKEN" },
    { provider: "llama", envKey: "LLAMA_API_KEY" },
    { provider: "fireworks", envKey: "FIREWORKS_API_KEY" },
];
class AIClient {
    clients = {};
    completionModels = {};
    embeddingModels = {};
    clientModels = {};
    imageModels = {};
    audioModels = {};
    videoModels = {};
    providerRegistry = {
        ...BUILT_IN_PROVIDER_REGISTRY,
    };
    constructor() {
        this._initDefaultProviders();
    }
    async _initDefaultProviders() {
        await this.registerModelProviders(DEFAULT_PROVIDERS);
    }
    resolveClient(entry) {
        const reg = this.providerRegistry[entry.provider];
        if (reg?.createClient) {
            return reg.createClient(entry);
        }
        if (reg?.clientClass) {
            const effectiveEnvKey = entry.envKey ??
                DEFAULT_PROVIDERS.find((p) => p.provider === entry.provider)?.envKey;
            if (effectiveEnvKey) {
                const envValue = process.env[effectiveEnvKey];
                if (!envValue)
                    return null;
                const client = new reg.clientClass(envValue);
                if (client instanceof http_1.HttpClient) {
                    client.setOptions({
                        timeout: entry.timeout,
                        headers: entry.headers,
                        extra_body: entry.extra_body,
                    });
                    if (entry.pricing)
                        client.setPrices(entry.pricing);
                }
                return client;
            }
            const client = new reg.clientClass();
            if (client instanceof http_1.HttpClient) {
                client.setOptions({
                    timeout: entry.timeout,
                    headers: entry.headers,
                    extra_body: entry.extra_body,
                });
                if (entry.pricing)
                    client.setPrices(entry.pricing);
            }
            return client;
        }
        if (entry.url) {
            const client = new http_1.HttpClient(entry.url, {
                headers: entry.headers,
                timeout: entry.timeout,
                extra_body: entry.extra_body,
            });
            if (entry.jwtFile) {
                client.loadJwtFile(entry.jwtFile);
            }
            if (entry.pricing)
                client.setPrices(entry.pricing);
            return client;
        }
        return null;
    }
    async _registerClientModalities(provider, client) {
        this.clients[provider] = client;
        const modalities = [
            "completion",
            "embedding",
            "image",
            "audio",
            "video",
        ];
        for (const modality of modalities) {
            try {
                const result = await client.getModels(modality);
                const models = result.map((m) => m.id);
                if (!models.length)
                    continue;
                switch (modality) {
                    case "completion":
                        this._mergeModels(this.completionModels, provider, models);
                        break;
                    case "embedding":
                        this._mergeModels(this.embeddingModels, provider, models);
                        break;
                    case "image":
                        this._mergeModels(this.imageModels, provider, models);
                        break;
                    case "audio":
                        this._mergeModels(this.audioModels, provider, models);
                        break;
                    case "video":
                        this._mergeModels(this.videoModels, provider, models);
                        break;
                }
                this._mergeModels(this.clientModels, provider, models);
            }
            catch {
            }
        }
    }
    _mergeModels(map, provider, models) {
        const current = map[provider] || [];
        map[provider] = Array.from(new Set([...current, ...models]));
    }
    async registerModelProviders(providers) {
        for (const entry of providers) {
            const client = this.resolveClient(entry);
            if (!client) {
                if (entry.provider === "knowhow") {
                    console.warn(`⚠️  Knowhow provider is not logged in. Run 'knowhow login' to enable Knowhow models.`);
                }
                continue;
            }
            const reg = this.providerRegistry[entry.provider];
            if (reg) {
                await this._registerClientModalities(entry.provider, client);
            }
            else {
                this.clients[entry.provider] = client;
                try {
                    await this.loadProviderModels(entry.provider);
                }
                catch (error) {
                    console.error(`Failed to register models for provider ${entry.provider}:`, error.message);
                }
            }
        }
    }
    async registerConfiguredModels() {
        const config = await (0, config_1.getConfig)();
        const modelProviders = config.modelProviders || [];
        if (config.modelProviders !== undefined) {
            const configProviderNames = new Set(modelProviders.map((p) => p.provider));
            for (const defaultEntry of DEFAULT_PROVIDERS) {
                if (!configProviderNames.has(defaultEntry.provider)) {
                    this.unregisterProvider(defaultEntry.provider);
                }
            }
        }
        if (modelProviders.length > 0) {
            await this.registerModelProviders(modelProviders);
        }
    }
    unregisterProvider(provider) {
        delete this.clients[provider];
        delete this.clientModels[provider];
        delete this.completionModels[provider];
        delete this.embeddingModels[provider];
        delete this.imageModels[provider];
        delete this.audioModels[provider];
        delete this.videoModels[provider];
    }
    registerClientClass(provider, entry) {
        this.providerRegistry[provider] = entry;
    }
    getClient(provider, model) {
        if (provider && !model) {
            return { client: this.clients[provider], provider, model: undefined };
        }
        const detected = this.detectProviderModel(provider, model);
        provider = detected.provider;
        model = detected.model;
        if (!this.clients[provider]) {
            throw new Error(`Provider ${provider} for model ${model} not registered. Available providers: ${Object.keys(this.clients)}`);
        }
        const hasModel = this.providerHasModel(provider, model);
        if (!hasModel) {
            throw new Error(`Model ${model} not registered for provider ${provider}.`);
        }
        return { client: this.clients[provider], provider, model };
    }
    registerClient(provider, client) {
        this.clients[provider] = client;
    }
    setKey(provider, apiKey) {
        const { client } = this.getClient(provider);
        client.setKey(apiKey);
        this.clients[provider].setKey(apiKey);
    }
    async loadProviderModels(provider) {
        if (!this.clients[provider]) {
            throw new Error(`Provider ${provider} not registered.`);
        }
        try {
            const models = await this.clients[provider].getModels();
            const ids = models.map((model) => model.id);
            this.registerModels(provider, ids);
        }
        catch (error) {
            console.error(`Failed to load models for provider ${provider}:`, error.message);
        }
    }
    registerModels(provider, models) {
        const currentModels = this.clientModels[provider] || [];
        const currentCompletionModels = this.completionModels[provider] || [];
        this.clientModels[provider] = Array.from(new Set(currentModels.concat(models)));
        const embeddingModels = this.embeddingModels[provider] || [];
        this.embeddingModels[provider] = Array.from(new Set(embeddingModels.concat(models.filter((m) => m.includes("embed")))));
        this.completionModels[provider] = Array.from(new Set(currentCompletionModels.concat(models.filter((m) => !m.includes("embed")))));
    }
    registerClientProviderModels(client, providerModels) {
        for (const [provider, models] of Object.entries(providerModels)) {
            this.registerClient(provider, client);
            this.registerModels(provider, models);
        }
    }
    registerEmbeddingModels(provider, models) {
        const currentModels = this.clientModels[provider] || [];
        this.clientModels[provider] = Array.from(new Set(currentModels.concat(models)));
        this.embeddingModels[provider] = Array.from(new Set(currentModels.concat(models)));
    }
    registerImageModels(provider, models) {
        const currentModels = this.clientModels[provider] || [];
        const currentImageModels = this.imageModels[provider] || [];
        this.clientModels[provider] = Array.from(new Set(currentModels.concat(models)));
        this.imageModels[provider] = Array.from(new Set(currentImageModels.concat(models)));
    }
    registerAudioModels(provider, models) {
        const currentModels = this.clientModels[provider] || [];
        const currentAudioModels = this.audioModels[provider] || [];
        this.clientModels[provider] = Array.from(new Set(currentModels.concat(models)));
        this.audioModels[provider] = Array.from(new Set(currentAudioModels.concat(models)));
    }
    registerVideoModels(provider, models) {
        const currentModels = this.clientModels[provider] || [];
        const currentVideoModels = this.videoModels[provider] || [];
        this.clientModels[provider] = Array.from(new Set(currentModels.concat(models)));
        this.videoModels[provider] = Array.from(new Set(currentVideoModels.concat(models)));
    }
    providerHasModel(provider, model) {
        const models = this.clientModels[provider];
        if (!models)
            return false;
        return models.includes(model);
    }
    findModel(modelPrefix) {
        for (const provider of Object.keys(this.clientModels)) {
            const models = this.clientModels[provider];
            const foundModel = models.find((m) => m.startsWith(modelPrefix));
            if (foundModel) {
                return { provider, model: foundModel };
            }
            const inferredFound = models.find((m) => {
                const split = m.split("/");
                if (split.length < 2)
                    return false;
                const inferredModel = split.slice(1).join("/");
                return (m === modelPrefix ||
                    inferredModel === modelPrefix ||
                    inferredModel.startsWith(modelPrefix));
            });
            if (inferredFound) {
                return { provider, model: inferredFound };
            }
        }
        if (modelPrefix.includes("/")) {
            const split = modelPrefix.split("/");
            return this.findModel(split.slice(1).join("/"));
        }
        return undefined;
    }
    static normalizeModelId(id) {
        return id
            .toLowerCase()
            .replace(/\./g, "-")
            .replace(/:[^:]+$/, "")
            .replace(/-\d{8}$/, "")
            .replace(/-(beta|preview|latest|exp|rc\d*)$/i, "");
    }
    findModelFuzzy(modelQuery, provider) {
        const queryNorm = AIClient.normalizeModelId(modelQuery);
        const providers = provider ? [provider] : Object.keys(this.clientModels);
        for (const p of providers) {
            const models = this.clientModels[p] ?? [];
            for (const m of models) {
                const mNorm = AIClient.normalizeModelId(m);
                if (mNorm === queryNorm || mNorm.startsWith(queryNorm + "-")) {
                    return { provider: p, model: m };
                }
            }
        }
        return undefined;
    }
    detectProviderModel(provider, model) {
        if (this.providerHasModel(provider, model)) {
            return { provider, model };
        }
        if (model?.includes("/")) {
            const split = model.split("/");
            const inferredProvider = split[0];
            const inferredModel = split.slice(1).join("/");
            if (this.providerHasModel(inferredProvider, inferredModel)) {
                return { provider: inferredProvider, model: inferredModel };
            }
            const foundBySplit = this.findModel(inferredModel);
            if (foundBySplit) {
                return foundBySplit;
            }
        }
        const foundByModel = this.findModel(model);
        if (foundByModel) {
            return foundByModel;
        }
        const allModels = this.listAllModels();
        const hasKnowhowModels = allModels.knowhow && allModels.knowhow.length > 0;
        const knowhowIsConfigured = Object.keys(allModels).includes("knowhow");
        console.warn(`⚠️  Unable to find model '${model}' for provider '${provider}'.`);
        console.warn(`   Available providers: ${Object.keys(allModels).join(", ") || "(none)"}`);
        if (!hasKnowhowModels && !knowhowIsConfigured) {
            console.warn(`   Tip: Run 'knowhow login' to enable Knowhow models.`);
        }
        else if (!hasKnowhowModels) {
            console.warn(`   Tip: The Knowhow provider returned no models. Try running 'knowhow login' to re-authenticate.`);
        }
        return { provider, model };
    }
    async createCompletion(provider, options) {
        const { client, model } = this.getClient(provider, options.model);
        if (!model || !client) {
            throw new Error(`provider: ${provider} does not have ${options.model} model registered. Try using ${JSON.stringify(this.listAllModels())}`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createChatCompletion({ ...options, model, signal }), options);
    }
    async createEmbedding(provider, options) {
        const { client, model } = this.getClient(provider, options.model);
        if (!model || !client) {
            throw new Error(`provider: ${provider} does not have ${options.model} model registered. Try using ${JSON.stringify(this.listAllModels())}`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createEmbedding({ ...options, model, signal }), options);
    }
    async createAudioTranscription(provider, options) {
        const { client } = this.getClient(provider, options.model);
        if (!client || !client.createAudioTranscription) {
            throw new Error(`Provider ${provider} does not support audio transcription.`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createAudioTranscription({ ...options, signal }), options);
    }
    async createAudioGeneration(provider, options) {
        const { client, model } = this.getClient(provider, options.model);
        if (!client || !client.createAudioGeneration) {
            throw new Error(`Provider ${provider} does not support audio generation.`);
        }
        if (!model) {
            throw new Error(`Model ${options.model} not registered for provider ${provider}.`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createAudioGeneration({ ...options, model, signal }), options);
    }
    async createImageGeneration(provider, options) {
        const { client, model } = this.getClient(provider, options.model);
        if (!client || !client.createImageGeneration) {
            throw new Error(`Provider ${provider} does not support image generation.`);
        }
        if (!model) {
            throw new Error(`Model ${options.model} not registered for provider ${provider}.`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createImageGeneration({ ...options, model, signal }), options);
    }
    async createVideoGeneration(provider, options) {
        const { client, model } = this.getClient(provider, options.model);
        if (!client || !client.createVideoGeneration) {
            throw new Error(`Provider ${provider} does not support video generation.`);
        }
        if (!model) {
            throw new Error(`Model ${options.model} not registered for provider ${provider}.`);
        }
        return (0, withRetry_1.withRetry)((signal) => client.createVideoGeneration({ ...options, model, signal }), options);
    }
    async getVideoStatus(provider, options) {
        const { client } = this.getClient(provider, options.model);
        if (!client || !client.getVideoStatus) {
            throw new Error(`Provider ${provider} does not support getVideoStatus.`);
        }
        return client.getVideoStatus(options);
    }
    async downloadVideo(provider, options) {
        const { client } = this.getClient(provider);
        if (!client || !client.downloadVideo) {
            throw new Error(`Provider ${provider} does not support downloadVideo.`);
        }
        return client.downloadVideo(options);
    }
    async uploadFile(provider, options) {
        const { client } = this.getClient(provider);
        if (!client || !client.uploadFile) {
            throw new Error(`Provider ${provider} does not support uploadFile.`);
        }
        return client.uploadFile(options);
    }
    async downloadFile(provider, options) {
        const { client } = this.getClient(provider);
        if (!client || !client.downloadFile) {
            throw new Error(`Provider ${provider} does not support downloadFile.`);
        }
        return client.downloadFile(options);
    }
    getRegisteredModels(provider) {
        return this.clientModels[provider] || [];
    }
    listAllModels() {
        return this.clientModels;
    }
    listAllModelsWithProvider() {
        return Object.entries(this.listAllModels())
            .map(([provider, models]) => models.map((m) => ({ id: `${provider}/${m}` })))
            .flat();
    }
    async parseProviderPrefixedModels(client, knownProvider = "") {
        const models = await client.getModels();
        const providerModels = models
            .map((m) => {
            if (knownProvider) {
                return {
                    provider: knownProvider,
                    model: m.id,
                };
            }
            const splitModel = m.id.split("/");
            if (splitModel.length < 2) {
                console.warn(`Cannot parse model format: ${m.id}`);
            }
            const provider = splitModel.length > 1 ? splitModel[0] : "";
            const modelName = splitModel.slice(1).join("/");
            return {
                provider,
                model: modelName,
            };
        })
            .reduce((acc, { provider, model }) => {
            acc[provider] = acc[provider] || [];
            acc[provider].push(model);
            return acc;
        }, {});
        for (const provider in providerModels) {
            if (!providerModels[provider].length) {
                delete providerModels[provider];
            }
        }
        return providerModels;
    }
    listAllEmbeddingModels() {
        return this.embeddingModels;
    }
    listAllCompletionModels() {
        return this.completionModels;
    }
    listAllProviders() {
        return Object.keys(this.clientModels);
    }
    listAllImageModels() {
        return this.imageModels;
    }
    listAllAudioModels() {
        return this.audioModels;
    }
    listAllVideoModels() {
        return this.videoModels;
    }
    getContextLimit(provider, model) {
        const client = this.clients[provider];
        if (client?.getContextLimit) {
            return client.getContextLimit(model);
        }
        const contextLimit = contextLimits_1.ContextLimits[model];
        if (contextLimit === undefined)
            return undefined;
        return { contextLimit, threshold: contextLimit };
    }
    getPrices(modelId) {
        const results = [];
        const addModels = (models, type, pricingMap) => {
            for (const [provider, ids] of Object.entries(models)) {
                for (const id of ids) {
                    if (modelId && id !== modelId)
                        continue;
                    if (!pricingMap[id])
                        continue;
                    const p = pricingMap[id];
                    results.push({
                        id,
                        provider,
                        type,
                        pricing: p,
                    });
                }
            }
        };
        const allTextPricing = {
            ...openai_2.OpenAiTextPricing,
            ...anthropic_2.AnthropicTextPricing,
            ...google_1.GeminiPricing,
            ...xai_2.XaiTextPricing,
        };
        const allImagePricing = {
            ...xai_2.XaiImagePricing,
        };
        const allVideoPricing = {
            ...xai_2.XaiVideoPricing,
        };
        addModels(this.completionModels, "completion", allTextPricing);
        addModels(this.embeddingModels, "embedding", allTextPricing);
        addModels(this.imageModels, "image", {
            ...allTextPricing,
            ...allImagePricing,
        });
        addModels(this.audioModels, "audio", allTextPricing);
        addModels(this.videoModels, "video", {
            ...allTextPricing,
            ...allVideoPricing,
        });
        return results;
    }
}
exports.AIClient = AIClient;
exports.Clients = new AIClient();
__exportStar(require("./types"), exports);
__exportStar(require("./http"), exports);
__exportStar(require("./openai"), exports);
__exportStar(require("./anthropic"), exports);
__exportStar(require("./knowhow"), exports);
__exportStar(require("./gemini"), exports);
__exportStar(require("./contextLimits"), exports);
__exportStar(require("./xai"), exports);
__exportStar(require("./knowhowMcp"), exports);
__exportStar(require("./groq"), exports);
__exportStar(require("./github"), exports);
__exportStar(require("./nvidia"), exports);
__exportStar(require("./openrouter"), exports);
__exportStar(require("./deepseek"), exports);
__exportStar(require("./mistral"), exports);
__exportStar(require("./llama"), exports);
__exportStar(require("./copilot"), exports);
__exportStar(require("./fireworks"), exports);
//# sourceMappingURL=index.js.map