mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-09 08:15:05 +01:00
polishing model picker and enable it in chat (#296356)
This commit is contained in:
committed by
GitHub
parent
8679e41a8f
commit
e4e882fb4a
@@ -3,8 +3,10 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as dom from '../../../base/browser/dom.js';
|
||||
import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js';
|
||||
import { renderMarkdown } from '../../../base/browser/markdownRenderer.js';
|
||||
import { ActionBar } from '../../../base/browser/ui/actionbar/actionbar.js';
|
||||
import { getAnchorRect, IAnchor } from '../../../base/browser/ui/contextview/contextview.js';
|
||||
import { KeybindingLabel } from '../../../base/browser/ui/keybindingLabel/keybindingLabel.js';
|
||||
import { IListEvent, IListMouseEvent, IListRenderer, IListVirtualDelegate } from '../../../base/browser/ui/list/list.js';
|
||||
import { IListAccessibilityProvider, List } from '../../../base/browser/ui/list/listWidget.js';
|
||||
@@ -13,6 +15,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../base/common
|
||||
import { Codicon } from '../../../base/common/codicons.js';
|
||||
import { IMarkdownString, MarkdownString } from '../../../base/common/htmlContent.js';
|
||||
import { ResolvedKeybinding } from '../../../base/common/keybindings.js';
|
||||
import { AnchorPosition } from '../../../base/common/layout.js';
|
||||
import { Disposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { OS } from '../../../base/common/platform.js';
|
||||
import { ThemeIcon } from '../../../base/common/themables.js';
|
||||
@@ -333,11 +336,6 @@ export interface IActionListOptions {
|
||||
*/
|
||||
readonly showFilter?: boolean;
|
||||
|
||||
/**
|
||||
* Placement of the filter input. Defaults to 'top'.
|
||||
*/
|
||||
readonly filterPlacement?: 'top' | 'bottom';
|
||||
|
||||
/**
|
||||
* Section IDs that should be collapsed by default.
|
||||
*/
|
||||
@@ -373,6 +371,18 @@ export class ActionList<T> extends Disposable {
|
||||
private _lastMinWidth = 0;
|
||||
private _cachedMaxWidth: number | undefined;
|
||||
private _hasLaidOut = false;
|
||||
private _showAbove: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Returns the resolved anchor position after the first layout.
|
||||
* Used by the context view delegate to lock the dropdown direction.
|
||||
*/
|
||||
get anchorPosition(): AnchorPosition | undefined {
|
||||
if (this._showAbove === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return this._showAbove ? AnchorPosition.ABOVE : AnchorPosition.BELOW;
|
||||
}
|
||||
|
||||
constructor(
|
||||
user: string,
|
||||
@@ -381,6 +391,7 @@ export class ActionList<T> extends Disposable {
|
||||
private readonly _delegate: IActionListDelegate<T>,
|
||||
accessibilityProvider: Partial<IListAccessibilityProvider<IActionListItem<T>>> | undefined,
|
||||
private readonly _options: IActionListOptions | undefined,
|
||||
private readonly _anchor: HTMLElement | StandardMouseEvent | IAnchor,
|
||||
@IContextViewService private readonly _contextViewService: IContextViewService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@ILayoutService private readonly _layoutService: ILayoutService,
|
||||
@@ -650,8 +661,13 @@ export class ActionList<T> extends Disposable {
|
||||
return this._filterContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved filter placement based on the dropdown direction.
|
||||
* When shown above the anchor, filter is at the bottom (closest to anchor);
|
||||
* when shown below, filter is at the top.
|
||||
*/
|
||||
get filterPlacement(): 'top' | 'bottom' {
|
||||
return this._options?.filterPlacement ?? 'top';
|
||||
return this._showAbove ? 'bottom' : 'top';
|
||||
}
|
||||
|
||||
get filterInput(): HTMLInputElement | undefined {
|
||||
@@ -692,6 +708,13 @@ export class ActionList<T> extends Disposable {
|
||||
this._contextViewService.hideContextView();
|
||||
}
|
||||
|
||||
private hasDynamicHeight(): boolean {
|
||||
if (this._options?.showFilter) {
|
||||
return true;
|
||||
}
|
||||
return this._allMenuItems.some(item => item.isSectionToggle);
|
||||
}
|
||||
|
||||
private computeHeight(): number {
|
||||
// Compute height based on currently visible items in the list
|
||||
const visibleCount = this._list.length;
|
||||
@@ -714,9 +737,36 @@ export class ActionList<T> extends Disposable {
|
||||
const filterHeight = this._filterContainer ? 36 : 0;
|
||||
const padding = 10;
|
||||
const targetWindow = dom.getWindow(this.domNode);
|
||||
const windowHeight = this._layoutService.getContainer(targetWindow).clientHeight;
|
||||
const widgetTop = this.domNode.getBoundingClientRect().top;
|
||||
const availableHeight = widgetTop > 0 ? windowHeight - widgetTop - padding : windowHeight * 0.7;
|
||||
let availableHeight;
|
||||
|
||||
if (this.hasDynamicHeight()) {
|
||||
const viewportHeight = targetWindow.innerHeight;
|
||||
const anchorRect = getAnchorRect(this._anchor);
|
||||
const anchorTopInViewport = anchorRect.top - targetWindow.pageYOffset;
|
||||
const spaceBelow = viewportHeight - anchorTopInViewport - anchorRect.height - padding;
|
||||
const spaceAbove = anchorTopInViewport - padding;
|
||||
|
||||
// Lock the direction on first layout based on whether the full
|
||||
// unconstrained list fits below. Once decided, the dropdown stays
|
||||
// in the same position even when the visible item count changes.
|
||||
if (this._showAbove === undefined) {
|
||||
let fullHeight = filterHeight;
|
||||
for (const item of this._allMenuItems) {
|
||||
switch (item.kind) {
|
||||
case ActionListItemKind.Header: fullHeight += this._headerLineHeight; break;
|
||||
case ActionListItemKind.Separator: fullHeight += this._separatorLineHeight; break;
|
||||
default: fullHeight += this._actionLineHeight; break;
|
||||
}
|
||||
}
|
||||
this._showAbove = fullHeight > spaceBelow && spaceAbove > spaceBelow;
|
||||
}
|
||||
availableHeight = this._showAbove ? spaceAbove : spaceBelow;
|
||||
} else {
|
||||
const windowHeight = this._layoutService.getContainer(targetWindow).clientHeight;
|
||||
const widgetTop = this.domNode.getBoundingClientRect().top;
|
||||
availableHeight = widgetTop > 0 ? windowHeight - widgetTop - padding : windowHeight * 0.7;
|
||||
}
|
||||
|
||||
const maxHeight = Math.max(availableHeight, this._actionLineHeight * 3 + filterHeight);
|
||||
const height = Math.min(listHeight + filterHeight, maxHeight);
|
||||
return height - filterHeight;
|
||||
@@ -798,6 +848,21 @@ export class ActionList<T> extends Disposable {
|
||||
this._cachedMaxWidth = this.computeMaxWidth(minWidth);
|
||||
this._list.layout(listHeight, this._cachedMaxWidth);
|
||||
this.domNode.style.height = `${listHeight}px`;
|
||||
|
||||
// Place filter container on the correct side based on dropdown direction.
|
||||
// When shown above, filter goes below the list (closest to anchor).
|
||||
// When shown below, filter goes above the list (closest to anchor).
|
||||
if (this._filterContainer && this._filterContainer.parentElement) {
|
||||
const parent = this._filterContainer.parentElement;
|
||||
if (this._showAbove) {
|
||||
// Move filter after the list
|
||||
parent.appendChild(this._filterContainer);
|
||||
} else {
|
||||
// Move filter before the list
|
||||
parent.insertBefore(this._filterContainer, this.domNode);
|
||||
}
|
||||
}
|
||||
|
||||
return this._cachedMaxWidth;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class ActionWidgetService extends Disposable implements IActionWidgetService {
|
||||
show<T>(user: string, supportsPreview: boolean, items: readonly IActionListItem<T>[], delegate: IActionListDelegate<T>, anchor: HTMLElement | StandardMouseEvent | IAnchor, container: HTMLElement | undefined, actionBarActions?: readonly IAction[], accessibilityProvider?: Partial<IListAccessibilityProvider<IActionListItem<T>>>, listOptions?: IActionListOptions): void {
|
||||
const visibleContext = ActionWidgetContextKeys.Visible.bindTo(this._contextKeyService);
|
||||
|
||||
const list = this._instantiationService.createInstance(ActionList, user, supportsPreview, items, delegate, accessibilityProvider, listOptions);
|
||||
const list = this._instantiationService.createInstance(ActionList, user, supportsPreview, items, delegate, accessibilityProvider, listOptions, anchor);
|
||||
this._contextViewService.showContextView({
|
||||
getAnchor: () => anchor,
|
||||
render: (container: HTMLElement) => {
|
||||
@@ -75,6 +75,7 @@ class ActionWidgetService extends Disposable implements IActionWidgetService {
|
||||
visibleContext.reset();
|
||||
this._onWidgetClosed(didCancel);
|
||||
},
|
||||
get anchorPosition() { return list.anchorPosition; },
|
||||
}, container, false);
|
||||
}
|
||||
|
||||
@@ -118,15 +119,10 @@ class ActionWidgetService extends Disposable implements IActionWidgetService {
|
||||
|
||||
this._list.value = list;
|
||||
if (this._list.value) {
|
||||
// Filter input at the top
|
||||
if (this._list.value.filterContainer && this._list.value.filterPlacement === 'top') {
|
||||
if (this._list.value.filterContainer) {
|
||||
widget.appendChild(this._list.value.filterContainer);
|
||||
}
|
||||
widget.appendChild(this._list.value.domNode);
|
||||
// Filter input at the bottom
|
||||
if (this._list.value.filterContainer && this._list.value.filterPlacement === 'bottom') {
|
||||
widget.appendChild(this._list.value.filterContainer);
|
||||
}
|
||||
} else {
|
||||
throw new Error('List has no value');
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js';
|
||||
import { AnchorAlignment, AnchorAxisAlignment, IAnchor, IContextViewProvider } from '../../../base/browser/ui/contextview/contextview.js';
|
||||
import { IAction } from '../../../base/common/actions.js';
|
||||
import { Event } from '../../../base/common/event.js';
|
||||
import { AnchorPosition } from '../../../base/common/layout.js';
|
||||
import { IDisposable } from '../../../base/common/lifecycle.js';
|
||||
import { IMenuActionOptions, MenuId } from '../../actions/common/actions.js';
|
||||
import { IContextKeyService } from '../../contextkey/common/contextkey.js';
|
||||
@@ -43,6 +44,7 @@ export interface IContextViewDelegate {
|
||||
focus?(): void;
|
||||
anchorAlignment?: AnchorAlignment;
|
||||
anchorAxisAlignment?: AnchorAxisAlignment;
|
||||
anchorPosition?: AnchorPosition;
|
||||
|
||||
// context views with higher layers are rendered over contet views with lower layers
|
||||
layer?: number; // Default: 0
|
||||
|
||||
@@ -467,7 +467,6 @@ class NewChatWidget extends Disposable {
|
||||
currentModel: this._currentLanguageModel,
|
||||
setModel: (model: ILanguageModelChatMetadataAndIdentifier) => {
|
||||
this._currentLanguageModel.set(model, undefined);
|
||||
this.languageModelsService.addToRecentlyUsedList(model);
|
||||
},
|
||||
getModels: () => this._getAvailableModels(),
|
||||
canManageModels: () => true,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Action2, registerAction2 } from '../../../../../platform/actions/common
|
||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
|
||||
import { IChatService } from '../../common/chatService/chatService.js';
|
||||
import { ILanguageModelsService } from '../../common/languageModels.js';
|
||||
import { IChatWidgetService } from '../chat.js';
|
||||
|
||||
function uriReplacer(_key: string, value: unknown): unknown {
|
||||
@@ -31,6 +32,7 @@ export function registerChatDeveloperActions() {
|
||||
registerAction2(LogChatInputHistoryAction);
|
||||
registerAction2(LogChatIndexAction);
|
||||
registerAction2(InspectChatModelAction);
|
||||
registerAction2(ClearRecentlyUsedLanguageModelsAction);
|
||||
}
|
||||
|
||||
class LogChatInputHistoryAction extends Action2 {
|
||||
@@ -126,3 +128,21 @@ class InspectChatModelAction extends Action2 {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ClearRecentlyUsedLanguageModelsAction extends Action2 {
|
||||
static readonly ID = 'workbench.action.chat.clearRecentlyUsedLanguageModels';
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
id: ClearRecentlyUsedLanguageModelsAction.ID,
|
||||
title: localize2('workbench.action.chat.clearRecentlyUsedLanguageModels.label', "Clear Recently Used Language Models"),
|
||||
category: Categories.Developer,
|
||||
f1: true,
|
||||
precondition: ChatContextKeys.enabled
|
||||
});
|
||||
}
|
||||
|
||||
override run(accessor: ServicesAccessor): void {
|
||||
accessor.get(ILanguageModelsService).clearRecentlyUsedList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,12 +120,13 @@ import { ChatInputPartWidgetController } from './chatInputPartWidgets.js';
|
||||
import { IChatInputPickerOptions } from './chatInputPickerActionItem.js';
|
||||
import { ChatSelectedTools } from './chatSelectedTools.js';
|
||||
import { DelegationSessionPickerActionItem } from './delegationSessionPickerActionItem.js';
|
||||
import { IModelPickerDelegate, ModelPickerActionItem } from './modelPickerActionItem.js';
|
||||
import { IModelPickerDelegate } from './modelPickerActionItem.js';
|
||||
import { IModePickerDelegate, ModePickerActionItem } from './modePickerActionItem.js';
|
||||
import { SessionTypePickerActionItem } from './sessionTargetPickerActionItem.js';
|
||||
import { WorkspacePickerActionItem } from './workspacePickerActionItem.js';
|
||||
import { ChatContextUsageWidget } from '../../widgetHosts/viewPane/chatContextUsageWidget.js';
|
||||
import { Target } from '../../../common/promptSyntax/service/promptsService.js';
|
||||
import { EnhancedModelPickerActionItem } from './modelPickerActionItem2.js';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -357,7 +358,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
private agentSessionTypeKey: IContextKey<string>;
|
||||
private chatSessionHasCustomAgentTarget: IContextKey<boolean>;
|
||||
private chatSessionHasTargetedModels: IContextKey<boolean>;
|
||||
private modelWidget: ModelPickerActionItem | undefined;
|
||||
private modelWidget: EnhancedModelPickerActionItem | undefined;
|
||||
private modeWidget: ModePickerActionItem | undefined;
|
||||
private sessionTargetWidget: SessionTypePickerActionItem | undefined;
|
||||
private delegationWidget: DelegationSessionPickerActionItem | undefined;
|
||||
@@ -998,8 +999,6 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
public setCurrentLanguageModel(model: ILanguageModelChatMetadataAndIdentifier) {
|
||||
this._currentLanguageModel.set(model, undefined);
|
||||
|
||||
this.languageModelsService.addToRecentlyUsedList(model);
|
||||
|
||||
if (this.cachedWidth) {
|
||||
// For quick chat and editor chat, relayout because the input may need to shrink to accomodate the model name
|
||||
this.layout(this.cachedWidth);
|
||||
@@ -2185,7 +2184,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
getModels: () => this.getModels(),
|
||||
canManageModels: () => !this.getCurrentSessionType()
|
||||
};
|
||||
return this.modelWidget = this.instantiationService.createInstance(ModelPickerActionItem, action, undefined, itemDelegate, pickerOptions);
|
||||
return this.modelWidget = this.instantiationService.createInstance(EnhancedModelPickerActionItem, action, itemDelegate, pickerOptions);
|
||||
} else if (action.id === OpenModePickerAction.ID && action instanceof MenuItemAction) {
|
||||
const delegate: IModePickerDelegate = {
|
||||
currentMode: this._currentModeObservable,
|
||||
|
||||
@@ -232,18 +232,21 @@ export function buildModelPickerItems(
|
||||
}
|
||||
}
|
||||
|
||||
// Render promoted section: available sorted alphabetically, then unavailable
|
||||
// Render promoted section: sorted alphabetically by name
|
||||
if (promotedItems.length > 0) {
|
||||
const available = promotedItems.filter((i): i is PromotedItem & { kind: 'available' } => i.kind === 'available');
|
||||
const unavailable = promotedItems.filter((i): i is PromotedItem & { kind: 'unavailable' } => i.kind === 'unavailable');
|
||||
available.sort((a, b) => a.model.metadata.name.localeCompare(b.model.metadata.name));
|
||||
promotedItems.sort((a, b) => {
|
||||
const aName = a.kind === 'available' ? a.model.metadata.name : a.entry.label;
|
||||
const bName = b.kind === 'available' ? b.model.metadata.name : b.entry.label;
|
||||
return aName.localeCompare(bName);
|
||||
});
|
||||
|
||||
items.push({ kind: ActionListItemKind.Separator });
|
||||
for (const { model } of available) {
|
||||
items.push(createModelItem(createModelAction(model, selectedModelId, onSelect), model));
|
||||
}
|
||||
for (const { entry, reason } of unavailable) {
|
||||
items.push(createUnavailableModelItem(entry, reason, upgradePlanUrl, updateStateType));
|
||||
for (const item of promotedItems) {
|
||||
if (item.kind === 'available') {
|
||||
items.push(createModelItem(createModelAction(item.model, selectedModelId, onSelect), item.model));
|
||||
} else {
|
||||
items.push(createUnavailableModelItem(item.entry, item.reason, upgradePlanUrl, updateStateType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +327,7 @@ function createUnavailableModelItem(
|
||||
|
||||
if (reason === 'upgrade') {
|
||||
description = upgradePlanUrl
|
||||
? new MarkdownString(localize('chat.modelPicker.upgradeLink', "[Upgrade]({0})", upgradePlanUrl), { isTrusted: true })
|
||||
? new MarkdownString(localize('chat.modelPicker.upgradeLink', "[Upgrade your plan]({0})", upgradePlanUrl), { isTrusted: true })
|
||||
: localize('chat.modelPicker.upgrade', "Upgrade");
|
||||
} else {
|
||||
icon = Codicon.warning;
|
||||
@@ -619,23 +622,6 @@ function getModelHoverContent(model: ILanguageModelChatMetadataAndIdentifier): M
|
||||
markdown.appendText(`\n`);
|
||||
}
|
||||
|
||||
if (model.metadata.capabilities) {
|
||||
markdown.appendMarkdown(`${localize('models.capabilities', 'Capabilities')}: `);
|
||||
if (model.metadata.capabilities?.toolCalling) {
|
||||
markdown.appendMarkdown(` <span style="background-color:#8080802B;"> _${localize('models.toolCalling', 'Tools')}_ </span>`);
|
||||
}
|
||||
if (model.metadata.capabilities?.vision) {
|
||||
markdown.appendMarkdown(` <span style="background-color:#8080802B;"> _${localize('models.vision', 'Vision')}_ </span>`);
|
||||
}
|
||||
if (model.metadata.capabilities?.agentMode) {
|
||||
markdown.appendMarkdown(` <span style="background-color:#8080802B;"> _${localize('models.agentMode', 'Agent Mode')}_ </span>`);
|
||||
}
|
||||
for (const editTool of model.metadata.capabilities.editTools ?? []) {
|
||||
markdown.appendMarkdown(` <span style="background-color:#8080802B;"> _${editTool}_ </span>`);
|
||||
}
|
||||
markdown.appendText(`\n`);
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import { IChatTransferService } from '../model/chatTransferService.js';
|
||||
import { LocalChatSessionUri } from '../model/chatUri.js';
|
||||
import { IChatRequestVariableEntry } from '../attachments/chatVariableEntries.js';
|
||||
import { ChatAgentLocation, ChatConfiguration, ChatModeKind } from '../constants.js';
|
||||
import { ChatMessageRole, IChatMessage } from '../languageModels.js';
|
||||
import { ChatMessageRole, IChatMessage, ILanguageModelsService } from '../languageModels.js';
|
||||
import { ILanguageModelToolsService } from '../tools/languageModelToolsService.js';
|
||||
import { ChatSessionOperationLog } from '../model/chatSessionOperationLog.js';
|
||||
import { IPromptsService } from '../promptSyntax/service/promptsService.js';
|
||||
@@ -159,6 +159,7 @@ export class ChatService extends Disposable implements IChatService {
|
||||
@IMcpService private readonly mcpService: IMcpService,
|
||||
@IPromptsService private readonly promptsService: IPromptsService,
|
||||
@IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService,
|
||||
@ILanguageModelsService private readonly languageModelsService: ILanguageModelsService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -1194,6 +1195,9 @@ export class ChatService extends Disposable implements IChatService {
|
||||
this.processNextPendingRequest(model);
|
||||
}
|
||||
});
|
||||
if (options?.userSelectedModelId) {
|
||||
this.languageModelsService.addToRecentlyUsedList(options.userSelectedModelId);
|
||||
}
|
||||
this._onDidSubmitRequest.fire({ chatSessionResource: model.sessionResource });
|
||||
return {
|
||||
responseCreatedPromise: responseCreated.p,
|
||||
|
||||
@@ -376,7 +376,12 @@ export interface ILanguageModelsService {
|
||||
/**
|
||||
* Records that a model was used, updating the recently used list.
|
||||
*/
|
||||
addToRecentlyUsedList(model: ILanguageModelChatMetadataAndIdentifier): void;
|
||||
addToRecentlyUsedList(modelIdentifier: string): void;
|
||||
|
||||
/**
|
||||
* Clears the recently used model list.
|
||||
*/
|
||||
clearRecentlyUsedList(): void;
|
||||
|
||||
/**
|
||||
* Returns the models from the control manifest,
|
||||
@@ -1378,22 +1383,22 @@ export class LanguageModelsService implements ILanguageModelsService {
|
||||
getRecentlyUsedModelIds(): string[] {
|
||||
// Filter to only include models that still exist in the cache
|
||||
return this._recentlyUsedModelIds
|
||||
.filter(id => this._modelCache.has(id) && id !== 'auto')
|
||||
.filter(id => this._modelCache.has(id) && id !== 'copilot/auto')
|
||||
.slice(0, 5);
|
||||
}
|
||||
|
||||
addToRecentlyUsedList(model: ILanguageModelChatMetadataAndIdentifier): void {
|
||||
if (model.metadata.id === 'auto' && this._vendors.get(model.metadata.vendor)?.isDefault) {
|
||||
addToRecentlyUsedList(modelIdentifier: string): void {
|
||||
if (modelIdentifier === 'copilot/auto') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove if already present (to move to front)
|
||||
const index = this._recentlyUsedModelIds.indexOf(model.identifier);
|
||||
const index = this._recentlyUsedModelIds.indexOf(modelIdentifier);
|
||||
if (index !== -1) {
|
||||
this._recentlyUsedModelIds.splice(index, 1);
|
||||
}
|
||||
// Add to front
|
||||
this._recentlyUsedModelIds.unshift(model.identifier);
|
||||
this._recentlyUsedModelIds.unshift(modelIdentifier);
|
||||
// Cap at a reasonable max to avoid unbounded growth
|
||||
if (this._recentlyUsedModelIds.length > 20) {
|
||||
this._recentlyUsedModelIds.length = 20;
|
||||
@@ -1401,6 +1406,11 @@ export class LanguageModelsService implements ILanguageModelsService {
|
||||
this._saveRecentlyUsedModels();
|
||||
}
|
||||
|
||||
clearRecentlyUsedList(): void {
|
||||
this._recentlyUsedModelIds = [];
|
||||
this._saveRecentlyUsedModels();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Models control manifest
|
||||
|
||||
@@ -140,6 +140,7 @@ class MockLanguageModelsService implements ILanguageModelsService {
|
||||
|
||||
getRecentlyUsedModelIds(): string[] { return []; }
|
||||
addToRecentlyUsedList(): void { }
|
||||
clearRecentlyUsedList(): void { }
|
||||
getModelsControlManifest(): IModelsControlManifest { return { free: {}, paid: {} }; }
|
||||
restrictedChatParticipants = observableValue('restrictedChatParticipants', Object.create(null));
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ export class NullLanguageModelsService implements ILanguageModelsService {
|
||||
}
|
||||
|
||||
addToRecentlyUsedList(): void { }
|
||||
clearRecentlyUsedList(): void { }
|
||||
|
||||
getModelsControlManifest(): IModelsControlManifest {
|
||||
return { free: {}, paid: {} };
|
||||
|
||||
Reference in New Issue
Block a user