Path: blob/main/src/vs/workbench/contrib/chat/browser/defaultModelContribution.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../../base/common/lifecycle.js';6import { localize } from '../../../../nls.js';7import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js';8import { ILogService } from '../../../../platform/log/common/log.js';9import { Registry } from '../../../../platform/registry/common/platform.js';10import { ILanguageModelChatMetadata, ILanguageModelsService } from '../common/languageModels.js';11import { DEFAULT_MODEL_PICKER_CATEGORY } from '../common/widget/input/modelPickerWidget.js';1213const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);1415export interface DefaultModelArrays {16readonly modelIds: string[];17readonly modelLabels: string[];18readonly modelDescriptions: string[];19}2021export interface DefaultModelContributionOptions {22/** Configuration key for the setting (used in schema notification). */23readonly configKey: string;24/** Configuration section id for `notifyConfigurationSchemaUpdated`, or `undefined` to skip notification. */25readonly configSectionId: string | undefined;26/** Log prefix, e.g. `'[PlanAgentDefaultModel]'`. */27readonly logPrefix: string;28/** Additional filter beyond `isUserSelectable`. Return `true` to include the model. */29readonly filter?: (metadata: ILanguageModelChatMetadata) => boolean;30}3132/**33* Creates the initial static arrays used by configuration registration code.34* The returned arrays are mutated in-place by {@link DefaultModelContribution}.35*/36export function createDefaultModelArrays(): DefaultModelArrays {37return {38modelIds: [''],39modelLabels: [localize('defaultModel', 'Auto (Vendor Default)')],40modelDescriptions: [localize('defaultModelDescription', "Use the vendor's default model")],41};42}4344/**45* Shared base class for workbench contributions that populate a dynamic enum46* of language models for a settings picker.47*/48export abstract class DefaultModelContribution extends Disposable {4950constructor(51private readonly _arrays: DefaultModelArrays,52private readonly _options: DefaultModelContributionOptions,53@ILanguageModelsService private readonly _languageModelsService: ILanguageModelsService,54@ILogService private readonly _logService: ILogService,55) {56super();57this._register(_languageModelsService.onDidChangeLanguageModels(() => this._updateModelValues()));58this._updateModelValues();59}6061private _updateModelValues(): void {62const { modelIds, modelLabels, modelDescriptions } = this._arrays;63const { configKey, configSectionId, logPrefix, filter } = this._options;6465try {66// Clear arrays67modelIds.length = 0;68modelLabels.length = 0;69modelDescriptions.length = 0;7071// Add default/empty option72modelIds.push('');73modelLabels.push(localize('defaultModel', 'Auto (Vendor Default)'));74modelDescriptions.push(localize('defaultModelDescription', "Use the vendor's default model"));7576const models: { identifier: string; metadata: ILanguageModelChatMetadata }[] = [];77const allModelIds = this._languageModelsService.getLanguageModelIds();7879for (const modelId of allModelIds) {80try {81const metadata = this._languageModelsService.lookupLanguageModel(modelId);82if (metadata) {83models.push({ identifier: modelId, metadata });84} else {85this._logService.warn(`${logPrefix} No metadata found for model ID: ${modelId}`);86}87} catch (e) {88this._logService.error(`${logPrefix} Error looking up model ${modelId}:`, e);89}90}9192const supportedModels = models.filter(model => {93if (!model.metadata?.isUserSelectable) {94return false;95}96if (filter && !filter(model.metadata)) {97return false;98}99return true;100});101102supportedModels.sort((a, b) => {103const aCategory = a.metadata.modelPickerCategory ?? DEFAULT_MODEL_PICKER_CATEGORY;104const bCategory = b.metadata.modelPickerCategory ?? DEFAULT_MODEL_PICKER_CATEGORY;105106if (aCategory.order !== bCategory.order) {107return aCategory.order - bCategory.order;108}109110return a.metadata.name.localeCompare(b.metadata.name);111});112113for (const model of supportedModels) {114try {115const qualifiedName = ILanguageModelChatMetadata.asQualifiedName(model.metadata);116modelIds.push(qualifiedName);117modelLabels.push(model.metadata.name);118modelDescriptions.push(model.metadata.tooltip ?? model.metadata.detail ?? '');119} catch (e) {120this._logService.error(`${logPrefix} Error adding model ${model.metadata.name}:`, e);121}122}123124if (configSectionId) {125configurationRegistry.notifyConfigurationSchemaUpdated({126id: configSectionId,127properties: {128[configKey]: {}129}130});131}132} catch (e) {133this._logService.error(`${logPrefix} Error updating model values:`, e);134}135}136}137138139