
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { Emitter } from '../../../../base/common/event.js';
import { Iterable } from '../../../../base/common/iterator.js';
import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
import { isFalsyOrWhitespace } from '../../../../base/common/strings.js';
import { localize } from '../../../../nls.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../platform/storage/common/storage.service.js';
import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.service.js';
import { ExtensionsRegistry } from '../../../services/extensions/common/extensionsRegistry.js';
import { ChatContextKeys } from './chatContextKeys.js';

var ChatMessageRole;
(function (ChatMessageRole) {
    ChatMessageRole[ChatMessageRole["System"] = 0] = "System";
    ChatMessageRole[ChatMessageRole["User"] = 1] = "User";
    ChatMessageRole[ChatMessageRole["Assistant"] = 2] = "Assistant";
})(ChatMessageRole || (ChatMessageRole = {}));
var ToolResultAudience;
(function (ToolResultAudience) {
    ToolResultAudience[ToolResultAudience["Assistant"] = 0] = "Assistant";
    ToolResultAudience[ToolResultAudience["User"] = 1] = "User";
})(ToolResultAudience || (ToolResultAudience = {}));
var ChatImageMimeType;
(function (ChatImageMimeType) {
    ChatImageMimeType["PNG"] = "image/png";
    ChatImageMimeType["JPEG"] = "image/jpeg";
    ChatImageMimeType["GIF"] = "image/gif";
    ChatImageMimeType["WEBP"] = "image/webp";
    ChatImageMimeType["BMP"] = "image/bmp";
})(ChatImageMimeType || (ChatImageMimeType = {}));
var ImageDetailLevel;
(function (ImageDetailLevel) {
    ImageDetailLevel["Low"] = "low";
    ImageDetailLevel["High"] = "high";
})(ImageDetailLevel || (ImageDetailLevel = {}));
var ILanguageModelChatMetadata;
(function (ILanguageModelChatMetadata) {
    function suitableForAgentMode(metadata) {
        const supportsToolsAgent = typeof metadata.capabilities?.agentMode === 'undefined' || metadata.capabilities.agentMode;
        return supportsToolsAgent && !!metadata.capabilities?.toolCalling;
    }
    ILanguageModelChatMetadata.suitableForAgentMode = suitableForAgentMode;
    function asQualifiedName(metadata) {
        if (metadata.modelPickerCategory === undefined) {
            return `${metadata.name} (${metadata.family})`;
        }
        return metadata.name;
    }
    ILanguageModelChatMetadata.asQualifiedName = asQualifiedName;
})(ILanguageModelChatMetadata || (ILanguageModelChatMetadata = {}));
const languageModelType = {
    type: 'object',
    properties: {
        vendor: {
            type: 'string',
            description: ( localize(5429, "A globally unique vendor of language models."))
        },
        displayName: {
            type: 'string',
            description: ( localize(5430, "The display name of the language model vendor."))
        },
        managementCommand: {
            type: 'string',
            description: ( localize(
                5431,
                "A command to manage the language model vendor, e.g. 'Manage Copilot models'. This is used in the chat model picker. If not provided, a gear icon is not rendered during vendor selection."
            ))
        }
    }
};
const languageModelExtensionPoint = ExtensionsRegistry.registerExtensionPoint({
    extensionPoint: 'languageModels',
    jsonSchema: {
        description: ( localize(5432, "Contribute language models of a specific vendor.")),
        oneOf: [
            languageModelType,
            {
                type: 'array',
                items: languageModelType
            }
        ]
    },
    activationEventsGenerator: (contribs, result) => {
        for (const contrib of contribs) {
            result.push(`onLanguageModelChat:${contrib.vendor}`);
        }
    }
});
let LanguageModelsService = class LanguageModelsService {
    constructor(_extensionService, _logService, _storageService, _contextKeyService) {
        this._extensionService = _extensionService;
        this._logService = _logService;
        this._storageService = _storageService;
        this._store = ( new DisposableStore());
        this._providers = ( new Map());
        this._modelCache = ( new Map());
        this._vendors = ( new Map());
        this._modelPickerUserPreferences = {};
        this._onLanguageModelChange = this._store.add(( new Emitter()));
        this.onDidChangeLanguageModels = this._onLanguageModelChange.event;
        this._hasUserSelectableModels = ChatContextKeys.languageModelsAreUserSelectable.bindTo(_contextKeyService);
        this._modelPickerUserPreferences = this._storageService.getObject('chatModelPickerPreferences', StorageScope.PROFILE, this._modelPickerUserPreferences);
        this._store.add(this.onDidChangeLanguageModels(() => {
            this._hasUserSelectableModels.set(this._modelCache.size > 0 && ( Array.from(( this._modelCache.values())).some(model => model.isUserSelectable)));
        }));
        this._store.add(languageModelExtensionPoint.setHandler((extensions) => {
            this._vendors.clear();
            for (const extension of extensions) {
                if (!isProposedApiEnabled(extension.description, 'chatProvider')) {
                    extension.collector.error(( localize(5433, "This contribution point requires the 'chatProvider' proposal.")));
                    continue;
                }
                for (const item of Iterable.wrap(extension.value)) {
                    if (( this._vendors.has(item.vendor))) {
                        extension.collector.error(( localize(
                            5434,
                            "The vendor '{0}' is already registered and cannot be registered twice",
                            item.vendor
                        )));
                        continue;
                    }
                    if (isFalsyOrWhitespace(item.vendor)) {
                        extension.collector.error(( localize(5435, "The vendor field cannot be empty.")));
                        continue;
                    }
                    if (item.vendor.trim() !== item.vendor) {
                        extension.collector.error(( localize(5436, "The vendor field cannot start or end with whitespace.")));
                        continue;
                    }
                    this._vendors.set(item.vendor, item);
                }
            }
            for (const [vendor, _] of this._providers) {
                if (!( this._vendors.has(vendor))) {
                    this._providers.delete(vendor);
                }
            }
        }));
    }
    dispose() {
        this._store.dispose();
        this._providers.clear();
    }
    updateModelPickerPreference(modelIdentifier, showInModelPicker) {
        const model = this._modelCache.get(modelIdentifier);
        if (!model) {
            this._logService.warn(`[LM] Cannot update model picker preference for unknown model ${modelIdentifier}`);
            return;
        }
        this._modelPickerUserPreferences[modelIdentifier] = showInModelPicker;
        if (showInModelPicker === model.isUserSelectable) {
            delete this._modelPickerUserPreferences[modelIdentifier];
            this._storageService.store('chatModelPickerPreferences', this._modelPickerUserPreferences, StorageScope.PROFILE, StorageTarget.USER);
        }
        else if (model.isUserSelectable !== showInModelPicker) {
            this._storageService.store('chatModelPickerPreferences', this._modelPickerUserPreferences, StorageScope.PROFILE, StorageTarget.USER);
        }
        this._onLanguageModelChange.fire();
        this._logService.trace(`[LM] Updated model picker preference for ${modelIdentifier} to ${showInModelPicker}`);
    }
    getVendors() {
        return Array.from(( this._vendors.values()));
    }
    getLanguageModelIds() {
        return Array.from(( this._modelCache.keys()));
    }
    lookupLanguageModel(modelIdentifier) {
        const model = this._modelCache.get(modelIdentifier);
        if (model && this._modelPickerUserPreferences[modelIdentifier] !== undefined) {
            return { ...model, isUserSelectable: this._modelPickerUserPreferences[modelIdentifier] };
        }
        return model;
    }
    _clearModelCache(vendors) {
        if (typeof vendors === 'string') {
            vendors = [vendors];
        }
        for (const vendor of vendors) {
            for (const [id, model] of this._modelCache.entries()) {
                if (model.vendor === vendor) {
                    this._modelCache.delete(id);
                }
            }
        }
    }
    async resolveLanguageModels(vendors, silent) {
        if (typeof vendors === 'string') {
            vendors = [vendors];
        }
        this._clearModelCache(vendors);
        for (const vendor of vendors) {
            const provider = this._providers.get(vendor);
            if (!provider) {
                this._logService.warn(`[LM] No provider registered for vendor ${vendor}`);
                continue;
            }
            try {
                const modelsAndIdentifiers = await provider.prepareLanguageModelChat({ silent }, CancellationToken.None);
                for (const modelAndIdentifier of modelsAndIdentifiers) {
                    if (( this._modelCache.has(modelAndIdentifier.identifier))) {
                        this._logService.warn(`[LM] Model ${modelAndIdentifier.identifier} is already registered. Skipping.`);
                        continue;
                    }
                    this._modelCache.set(modelAndIdentifier.identifier, modelAndIdentifier.metadata);
                }
                this._logService.trace(`[LM] Resolved language models for vendor ${vendor}`, modelsAndIdentifiers);
            }
            catch (error) {
                this._logService.error(`[LM] Error resolving language models for vendor ${vendor}:`, error);
            }
        }
        this._onLanguageModelChange.fire();
    }
    async selectLanguageModels(selector, allowPromptingUser) {
        if (selector.vendor) {
            await this._extensionService.activateByEvent(`onLanguageModelChat:${selector.vendor}}`);
            await this.resolveLanguageModels([selector.vendor], !allowPromptingUser);
        }
        else {
            const allVendors = Array.from(( this._vendors.keys()));
            const all = ( allVendors.map(
                vendor => this._extensionService.activateByEvent(`onLanguageModelChat:${vendor}`)
            ));
            await Promise.all(all);
            await this.resolveLanguageModels(allVendors, !allowPromptingUser);
        }
        const result = [];
        for (const [internalModelIdentifier, model] of this._modelCache) {
            if ((selector.vendor === undefined || model.vendor === selector.vendor)
                && (selector.family === undefined || model.family === selector.family)
                && (selector.version === undefined || model.version === selector.version)
                && (selector.id === undefined || model.id === selector.id)) {
                result.push(internalModelIdentifier);
            }
        }
        this._logService.trace('[LM] selected language models', selector, result);
        return result;
    }
    registerLanguageModelProvider(vendor, provider) {
        this._logService.trace('[LM] registering language model provider', vendor, provider);
        if (!( this._vendors.has(vendor))) {
            throw ( new Error(`Chat model provider uses UNKNOWN vendor ${vendor}.`));
        }
        if (( this._providers.has(vendor))) {
            throw ( new Error(`Chat model provider for vendor ${vendor} is already registered.`));
        }
        this._providers.set(vendor, provider);
        this.resolveLanguageModels(vendor, true).then(() => {
            this._onLanguageModelChange.fire();
        });
        return toDisposable(() => {
            this._logService.trace('[LM] UNregistered language model provider', vendor);
            this._clearModelCache(vendor);
            this._providers.delete(vendor);
        });
    }
    async sendChatRequest(modelId, from, messages, options, token) {
        const provider = this._providers.get(this._modelCache.get(modelId)?.vendor || '');
        if (!provider) {
            throw ( new Error(`Chat provider for model ${modelId} is not registered.`));
        }
        return provider.sendChatRequest(modelId, messages, from, options, token);
    }
    computeTokenLength(modelId, message, token) {
        const model = this._modelCache.get(modelId);
        if (!model) {
            throw ( new Error(`Chat model ${modelId} could not be found.`));
        }
        const provider = this._providers.get(model.vendor);
        if (!provider) {
            throw ( new Error(`Chat provider for model ${modelId} is not registered.`));
        }
        return provider.provideTokenCount(modelId, message, token);
    }
};
LanguageModelsService = ( __decorate([
    ( __param(0, IExtensionService)),
    ( __param(1, ILogService)),
    ( __param(2, IStorageService)),
    ( __param(3, IContextKeyService))
], LanguageModelsService));

export { ChatImageMimeType, ChatMessageRole, ILanguageModelChatMetadata, ImageDetailLevel, LanguageModelsService, ToolResultAudience, languageModelExtensionPoint };
