Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/chat/browser/agentHost/agentHostModelPicker.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { BaseActionViewItem } from '../../../../../base/browser/ui/actionbar/actionViewItems.js';
7
import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';
8
import { autorun, observableValue } from '../../../../../base/common/observable.js';
9
import * as nls from '../../../../../nls.js';
10
import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js';
11
import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js';
12
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
13
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
14
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
15
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../../workbench/common/contributions.js';
16
import { type ILanguageModelChatMetadataAndIdentifier, ILanguageModelsService } from '../../../../../workbench/contrib/chat/common/languageModels.js';
17
import { type IChatInputPickerOptions } from '../../../../../workbench/contrib/chat/browser/widget/input/chatInputPickerActionItem.js';
18
import { ModelPickerActionItem, type IModelPickerDelegate } from '../../../../../workbench/contrib/chat/browser/widget/input/modelPickerActionItem.js';
19
import { ActiveSessionProviderIdContext } from '../../../../common/contextkeys.js';
20
import { type ISession } from '../../../../services/sessions/common/session.js';
21
import { ISessionsManagementService } from '../../../../services/sessions/common/sessionsManagement.js';
22
import { ISessionsProvidersService } from '../../../../services/sessions/browser/sessionsProvidersService.js';
23
import { Menus } from '../../../../browser/menus.js';
24
import { LOCAL_AGENT_HOST_PROVIDER_ID, REMOTE_AGENT_HOST_PROVIDER_RE } from '../../../../common/agentHostSessionsProvider.js';
25
26
const IsActiveSessionAgentHost = ContextKeyExpr.or(
27
ContextKeyExpr.equals(ActiveSessionProviderIdContext.key, LOCAL_AGENT_HOST_PROVIDER_ID),
28
ContextKeyExpr.regex(ActiveSessionProviderIdContext.key, REMOTE_AGENT_HOST_PROVIDER_RE),
29
);
30
31
// -- Agent Host Model Picker Action --
32
33
registerAction2(class extends Action2 {
34
constructor() {
35
super({
36
id: 'sessions.agentHost.modelPicker',
37
title: nls.localize2('agentHostModelPicker', "Model"),
38
f1: false,
39
menu: [{
40
id: Menus.NewSessionConfig,
41
group: 'navigation',
42
order: 1,
43
when: IsActiveSessionAgentHost,
44
}],
45
});
46
}
47
override async run(): Promise<void> { /* handled by action view item */ }
48
});
49
50
// -- Agent Host Model Picker Contribution --
51
52
function getAgentHostModels(
53
languageModelsService: ILanguageModelsService,
54
session: ISession | undefined,
55
): ILanguageModelChatMetadataAndIdentifier[] {
56
if (!session) {
57
return [];
58
}
59
// Filter models by resource scheme. For remote agent hosts the scheme is
60
// a unique per-connection ID; for local agent hosts it equals the session
61
// type. Both are used as the targetChatSessionType when registering
62
// models via AgentHostLanguageModelProvider.
63
const resourceScheme = session.resource.scheme;
64
return languageModelsService.getLanguageModelIds()
65
.map(id => {
66
const metadata = languageModelsService.lookupLanguageModel(id);
67
return metadata ? { metadata, identifier: id } : undefined;
68
})
69
.filter((m): m is ILanguageModelChatMetadataAndIdentifier => !!m && m.metadata.targetChatSessionType === resourceScheme);
70
}
71
72
const STORAGE_KEY = 'sessions.agentHostModelPicker.selectedModelId';
73
74
class AgentHostModelPickerContribution extends Disposable implements IWorkbenchContribution {
75
76
static readonly ID = 'sessions.contrib.agentHostModelPicker';
77
78
constructor(
79
@IActionViewItemService actionViewItemService: IActionViewItemService,
80
@IInstantiationService instantiationService: IInstantiationService,
81
@ILanguageModelsService languageModelsService: ILanguageModelsService,
82
@ISessionsManagementService sessionsManagementService: ISessionsManagementService,
83
@ISessionsProvidersService sessionsProvidersService: ISessionsProvidersService,
84
@IStorageService storageService: IStorageService,
85
) {
86
super();
87
88
this._register(actionViewItemService.register(
89
Menus.NewSessionConfig, 'sessions.agentHost.modelPicker',
90
() => {
91
const currentModel = observableValue<ILanguageModelChatMetadataAndIdentifier | undefined>('currentModel', undefined);
92
const delegate: IModelPickerDelegate = {
93
currentModel,
94
setModel: (model: ILanguageModelChatMetadataAndIdentifier) => {
95
currentModel.set(model, undefined);
96
storageService.store(STORAGE_KEY, model.identifier, StorageScope.PROFILE, StorageTarget.MACHINE);
97
const session = sessionsManagementService.activeSession.get();
98
if (session) {
99
const provider = sessionsProvidersService.getProviders().find(p => p.id === session.providerId);
100
provider?.setModel(session.sessionId, model.identifier);
101
}
102
},
103
getModels: () => getAgentHostModels(languageModelsService, sessionsManagementService.activeSession.get()),
104
useGroupedModelPicker: () => true,
105
showManageModelsAction: () => false,
106
showUnavailableFeatured: () => false,
107
showFeatured: () => true,
108
};
109
const pickerOptions: IChatInputPickerOptions = {
110
hideChevrons: observableValue('hideChevrons', false),
111
};
112
const action = { id: 'sessions.agentHost.modelPicker', label: '', enabled: true, class: undefined, tooltip: '', run: () => { } };
113
const modelPicker = instantiationService.createInstance(ModelPickerActionItem, action, delegate, pickerOptions);
114
115
const rememberedModelId = storageService.get(STORAGE_KEY, StorageScope.PROFILE);
116
const initModel = (session: ISession | undefined, sessionModelId: string | undefined) => {
117
const models = getAgentHostModels(languageModelsService, session);
118
modelPicker.setEnabled(models.length > 0);
119
120
let resolvedModel = sessionModelId
121
? models.find(model => model.identifier === sessionModelId)
122
: undefined;
123
124
// When no model is explicitly selected, restore the
125
// remembered model or pick the first available one so
126
// the picker shows a real model name instead of the
127
// misleading "Auto" label (the copilot "auto"
128
// pseudo-model is not available in agent host sessions).
129
if (!resolvedModel && models.length > 0) {
130
const remembered = rememberedModelId ? models.find(m => m.identifier === rememberedModelId) : undefined;
131
resolvedModel = remembered ?? models[0];
132
delegate.setModel(resolvedModel);
133
}
134
135
currentModel.set(resolvedModel, undefined);
136
};
137
const initModelFromActiveSession = () => {
138
const session = sessionsManagementService.activeSession.get();
139
initModel(session, session?.modelId.get());
140
};
141
initModelFromActiveSession();
142
143
const disposableStore = new DisposableStore();
144
disposableStore.add(languageModelsService.onDidChangeLanguageModels(() => initModelFromActiveSession()));
145
146
disposableStore.add(autorun(reader => {
147
const session = sessionsManagementService.activeSession.read(reader);
148
const sessionModelId = session?.modelId.read(reader);
149
initModel(session, sessionModelId);
150
}));
151
152
// When the active session changes, push the selected model to the new session
153
disposableStore.add(autorun(reader => {
154
const session = sessionsManagementService.activeSession.read(reader);
155
const model = currentModel.read(reader);
156
if (session && model) {
157
const provider = sessionsProvidersService.getProviders().find(p => p.id === session.providerId);
158
provider?.setModel(session.sessionId, model.identifier);
159
}
160
}));
161
162
return new AgentHostPickerActionViewItem(modelPicker, disposableStore);
163
},
164
));
165
}
166
}
167
168
class AgentHostPickerActionViewItem extends BaseActionViewItem {
169
constructor(private readonly picker: { render(container: HTMLElement): void; dispose(): void }, disposable?: DisposableStore) {
170
super(undefined, { id: '', label: '', enabled: true, class: undefined, tooltip: '', run: () => { } });
171
if (disposable) {
172
this._register(disposable);
173
}
174
}
175
176
override render(container: HTMLElement): void {
177
this.picker.render(container);
178
}
179
180
override dispose(): void {
181
this.picker.dispose();
182
super.dispose();
183
}
184
}
185
186
registerWorkbenchContribution2(AgentHostModelPickerContribution.ID, AgentHostModelPickerContribution, WorkbenchPhase.AfterRestored);
187
188