Path: blob/main/src/vs/workbench/contrib/chat/browser/modelPicker/modelPickerActionItem.ts
3296 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 { IAction } from '../../../../../base/common/actions.js';6import { Event } from '../../../../../base/common/event.js';7import { ILanguageModelChatMetadataAndIdentifier } from '../../common/languageModels.js';8import { localize } from '../../../../../nls.js';9import * as dom from '../../../../../base/browser/dom.js';10import { renderLabelWithIcons } from '../../../../../base/browser/ui/iconLabel/iconLabels.js';11import { IDisposable } from '../../../../../base/common/lifecycle.js';12import { ActionWidgetDropdownActionViewItem } from '../../../../../platform/actions/browser/actionWidgetDropdownActionViewItem.js';13import { IActionWidgetService } from '../../../../../platform/actionWidget/browser/actionWidget.js';14import { IActionWidgetDropdownAction, IActionWidgetDropdownActionProvider, IActionWidgetDropdownOptions } from '../../../../../platform/actionWidget/browser/actionWidgetDropdown.js';15import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';16import { ICommandService } from '../../../../../platform/commands/common/commands.js';17import { ChatEntitlement, IChatEntitlementService } from '../../common/chatEntitlementService.js';18import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';19import { DEFAULT_MODEL_PICKER_CATEGORY } from '../../common/modelPicker/modelPickerWidget.js';20import { ManageModelsAction } from '../actions/manageModelsActions.js';21import { IActionProvider } from '../../../../../base/browser/ui/dropdown/dropdown.js';2223export interface IModelPickerDelegate {24readonly onDidChangeModel: Event<ILanguageModelChatMetadataAndIdentifier>;25getCurrentModel(): ILanguageModelChatMetadataAndIdentifier | undefined;26setModel(model: ILanguageModelChatMetadataAndIdentifier): void;27getModels(): ILanguageModelChatMetadataAndIdentifier[];28}2930function modelDelegateToWidgetActionsProvider(delegate: IModelPickerDelegate): IActionWidgetDropdownActionProvider {31return {32getActions: () => {33return delegate.getModels().map(model => {34return {35id: model.metadata.id,36enabled: true,37icon: model.metadata.statusIcon,38checked: model.identifier === delegate.getCurrentModel()?.identifier,39category: model.metadata.modelPickerCategory || DEFAULT_MODEL_PICKER_CATEGORY,40class: undefined,41description: model.metadata.detail,42tooltip: model.metadata.tooltip ?? model.metadata.name,43label: model.metadata.name,44run: () => {45delegate.setModel(model);46}47} satisfies IActionWidgetDropdownAction;48});49}50};51}5253function getModelPickerActionBarActionProvider(commandService: ICommandService, chatEntitlementService: IChatEntitlementService): IActionProvider {5455const actionProvider: IActionProvider = {56getActions: () => {57const additionalActions: IAction[] = [];58if (59chatEntitlementService.entitlement === ChatEntitlement.Free ||60chatEntitlementService.entitlement === ChatEntitlement.Pro ||61chatEntitlementService.entitlement === ChatEntitlement.ProPlus ||62chatEntitlementService.isInternal63) {64additionalActions.push({65id: 'manageModels',66label: localize('chat.manageModels', "Manage Models..."),67enabled: true,68tooltip: localize('chat.manageModels.tooltip', "Manage language models"),69class: undefined,70run: () => {71const commandId = ManageModelsAction.ID;72commandService.executeCommand(commandId);73}74});75}7677// Add upgrade option if entitlement is free78if (chatEntitlementService.entitlement === ChatEntitlement.Free) {79additionalActions.push({80id: 'moreModels',81label: localize('chat.moreModels', "Add Premium Models"),82enabled: true,83tooltip: localize('chat.moreModels.tooltip', "Add premium models"),84class: undefined,85run: () => {86const commandId = 'workbench.action.chat.upgradePlan';87commandService.executeCommand(commandId);88}89});90}9192return additionalActions;93}94};95return actionProvider;96}9798/**99* Action view item for selecting a language model in the chat interface.100*/101export class ModelPickerActionItem extends ActionWidgetDropdownActionViewItem {102constructor(103action: IAction,104private currentModel: ILanguageModelChatMetadataAndIdentifier | undefined,105delegate: IModelPickerDelegate,106@IActionWidgetService actionWidgetService: IActionWidgetService,107@IContextKeyService contextKeyService: IContextKeyService,108@ICommandService commandService: ICommandService,109@IChatEntitlementService chatEntitlementService: IChatEntitlementService,110@IKeybindingService keybindingService: IKeybindingService,111) {112// Modify the original action with a different label and make it show the current model113const actionWithLabel: IAction = {114...action,115label: currentModel?.metadata.name ?? localize('chat.modelPicker.label', "Pick Model"),116tooltip: localize('chat.modelPicker.label', "Pick Model"),117run: () => { }118};119120const modelPickerActionWidgetOptions: Omit<IActionWidgetDropdownOptions, 'label' | 'labelRenderer'> = {121actionProvider: modelDelegateToWidgetActionsProvider(delegate),122actionBarActionProvider: getModelPickerActionBarActionProvider(commandService, chatEntitlementService)123};124125super(actionWithLabel, modelPickerActionWidgetOptions, actionWidgetService, keybindingService, contextKeyService);126127// Listen for model changes from the delegate128this._register(delegate.onDidChangeModel(model => {129this.currentModel = model;130if (this.element) {131this.renderLabel(this.element);132}133}));134}135136protected override renderLabel(element: HTMLElement): IDisposable | null {137const domChildren = [];138if (this.currentModel?.metadata.statusIcon) {139domChildren.push(...renderLabelWithIcons(`\$(${this.currentModel.metadata.statusIcon.id})`));140}141domChildren.push(dom.$('span.chat-model-label', undefined, this.currentModel?.metadata.name ?? localize('chat.modelPicker.label', "Pick Model")));142domChildren.push(...renderLabelWithIcons(`$(chevron-down)`));143144dom.reset(element, ...domChildren);145this.setAriaLabelAttributes(element);146return null;147}148149override render(container: HTMLElement): void {150super.render(container);151container.classList.add('chat-modelPicker-item');152}153}154155156