Skip to content

Commit 702a1ff

Browse files
Access tweaks for requestLanguageModelAccess (microsoft#205156)
1. remove the requirement that it has to be done during agent invocation 2. don't ask for auth when the model provider and the model requester are the same extension 3. since we don't have "language model activation events" start with a simple 3*2s timeout poll to wait for the language model registration to happen. (scenario: an extension activates before the extension that registers the model activates)
1 parent 8bec045 commit 702a1ff

File tree

5 files changed

+24
-37
lines changed

5 files changed

+24
-37
lines changed

src/vs/workbench/api/browser/mainThreadChatProvider.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
import { timeout } from 'vs/base/common/async';
67
import { CancellationToken } from 'vs/base/common/cancellation';
78
import { Emitter, Event } from 'vs/base/common/event';
89
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -81,7 +82,15 @@ export class MainThreadChatProvider implements MainThreadChatProviderShape {
8182
}
8283

8384
async $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise<IChatResponseProviderMetadata | undefined> {
84-
return this._chatProviderService.lookupChatResponseProvider(providerId);
85+
const metadata = this._chatProviderService.lookupChatResponseProvider(providerId);
86+
// TODO: This should use a real activation event. Perhaps following what authentication does.
87+
for (let i = 0; i < 3; i++) {
88+
if (metadata) {
89+
return metadata;
90+
}
91+
await timeout(2000);
92+
}
93+
return undefined;
8594
}
8695

8796
async $fetchResponse(extension: ExtensionIdentifier, providerId: string, requestId: number, messages: IChatMessage[], options: {}, token: CancellationToken): Promise<any> {

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
208208
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService));
209209
const extHostInteractiveEditor = rpcProtocol.set(ExtHostContext.ExtHostInlineChat, new ExtHostInteractiveEditor(rpcProtocol, extHostCommands, extHostDocuments, extHostLogService));
210210
const extHostChatProvider = rpcProtocol.set(ExtHostContext.ExtHostChatProvider, new ExtHostChatProvider(rpcProtocol, extHostLogService, extHostAuthentication));
211-
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostChatProvider, extHostLogService, extHostCommands));
211+
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands));
212212
const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol));
213213
const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol));
214214
const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol));

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,6 @@ export interface MainThreadChatProviderShape extends IDisposable {
11831183

11841184
export interface ExtHostChatProviderShape {
11851185
$updateLanguageModels(data: { added?: string[]; removed?: string[] }): void;
1186-
$updateAccesslist(data: { extension: ExtensionIdentifier; enabled: boolean }[]): void;
11871186
$updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void;
11881187
$provideLanguageModelResponse(handle: number, requestId: number, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, token: CancellationToken): Promise<any>;
11891188
$handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise<void>;

src/vs/workbench/api/common/extHostChatAgents2.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { localize } from 'vs/nls';
1616
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1717
import { ILogService } from 'vs/platform/log/common/log';
1818
import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol';
19-
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider';
2019
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
2120
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
2221
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
@@ -164,7 +163,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
164163

165164
constructor(
166165
mainContext: IMainContext,
167-
private readonly _extHostChatProvider: ExtHostChatProvider,
168166
private readonly _logService: ILogService,
169167
private readonly commands: ExtHostCommands,
170168
) {
@@ -186,8 +184,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
186184
throw new Error(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`);
187185
}
188186

189-
this._extHostChatProvider.$updateAccesslist([{ extension: agent.extension.identifier, enabled: true }]);
190-
191187
// Init session disposables
192188
let sessionDisposables = this._sessionDisposables.get(request.sessionId);
193189
if (!sessionDisposables) {
@@ -224,7 +220,6 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
224220

225221
} finally {
226222
stream.close();
227-
this._extHostChatProvider.$updateAccesslist([{ extension: agent.extension.identifier, enabled: false }]);
228223
}
229224
}
230225

src/vs/workbench/api/common/extHostChatProvider.ts

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ExtHostChatProviderShape, IMainContext, MainContext, MainThreadChatProv
1010
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
1111
import type * as vscode from 'vscode';
1212
import { Progress } from 'vs/platform/progress/common/progress';
13-
import { IChatMessage, IChatResponseFragment } from 'vs/workbench/contrib/chat/common/chatProvider';
13+
import { IChatMessage, IChatResponseFragment, IChatResponseProviderMetadata } from 'vs/workbench/contrib/chat/common/chatProvider';
1414
import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1515
import { AsyncIterableSource } from 'vs/base/common/async';
1616
import { Emitter, Event } from 'vs/base/common/event';
@@ -97,7 +97,6 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
9797

9898
private readonly _languageModels = new Map<number, LanguageModelData>();
9999
private readonly _languageModelIds = new Set<string>(); // these are ALL models, not just the one in this EH
100-
private readonly _accesslist = new ExtensionIdentifierMap<boolean>();
101100
private readonly _modelAccessList = new ExtensionIdentifierMap<ExtensionIdentifierSet>();
102101
private readonly _pendingRequest = new Map<number, { languageModelId: string; res: LanguageModelRequest }>();
103102

@@ -197,18 +196,6 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
197196
return Array.from(this._languageModelIds);
198197
}
199198

200-
$updateAccesslist(data: { extension: ExtensionIdentifier; enabled: boolean }[]): void {
201-
const updated = new ExtensionIdentifierSet();
202-
for (const { extension, enabled } of data) {
203-
const oldValue = this._accesslist.get(extension);
204-
if (oldValue !== enabled) {
205-
this._accesslist.set(extension, enabled);
206-
updated.add(extension);
207-
}
208-
}
209-
this._onDidChangeAccess.fire(updated);
210-
}
211-
212199
$updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void {
213200
const updated = new Array<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();
214201
for (const { from, to, enabled } of data) {
@@ -230,23 +217,15 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
230217

231218
async requestLanguageModelAccess(extension: IExtensionDescription, languageModelId: string, options?: vscode.LanguageModelAccessOptions): Promise<vscode.LanguageModelAccess> {
232219
const from = extension.identifier;
233-
// check if the extension is in the access list and allowed to make chat requests
234-
if (this._accesslist.get(from) === false) {
235-
throw new Error('Extension is NOT allowed to make chat requests');
236-
}
237-
238220
const justification = options?.justification;
239221
const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, justification);
240222

241223
if (!metadata) {
242-
if (!this._accesslist.get(from)) {
243-
throw new Error('Extension is NOT allowed to make chat requests');
244-
}
245224
throw new Error(`Language model '${languageModelId}' NOT found`);
246225
}
247226

248-
if (metadata.auth) {
249-
await this._checkAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth?.providerLabel }, justification);
227+
if (this._isUsingAuth(from, metadata)) {
228+
await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, justification);
250229
}
251230

252231
const that = this;
@@ -256,9 +235,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
256235
return metadata.model;
257236
},
258237
get isRevoked() {
259-
return !that._accesslist.get(from)
260-
|| (metadata.auth && !that._modelAccessList.get(from)?.has(metadata.extension))
261-
|| !that._languageModelIds.has(languageModelId);
238+
return (that._isUsingAuth(from, metadata) && !that._modelAccessList.get(from)?.has(metadata.extension)) || !that._languageModelIds.has(languageModelId);
262239
},
263240
get onDidChangeAccess() {
264241
const onDidChangeAccess = Event.filter(that._onDidChangeAccess.event, set => set.has(from));
@@ -267,7 +244,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
267244
return Event.signal(Event.any(onDidChangeAccess, onDidRemoveLM, onDidChangeModelAccess));
268245
},
269246
makeChatRequest(messages, options, token) {
270-
if (!that._accesslist.get(from) || (metadata.auth && !that._modelAccessList.get(from)?.has(metadata.extension))) {
247+
if (that._isUsingAuth(from, metadata) && !that._modelAccessList.get(from)?.has(metadata.extension)) {
271248
throw new Error('Access to chat has been revoked');
272249
}
273250
if (!that._languageModelIds.has(languageModelId)) {
@@ -297,7 +274,7 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
297274
}
298275

299276
// BIG HACK: Using AuthenticationProviders to check access to Language Models
300-
private async _checkAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, detail?: string): Promise<void> {
277+
private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, detail?: string): Promise<void> {
301278
// This needs to be done in both MainThread & ExtHost ChatProvider
302279
const providerId = INTERNAL_AUTH_PROVIDER_PREFIX + to.identifier.value;
303280
const session = await this._extHostAuthentication.getSession(from, providerId, [], { silent: true });
@@ -315,4 +292,11 @@ export class ExtHostChatProvider implements ExtHostChatProviderShape {
315292

316293
this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]);
317294
}
295+
296+
private _isUsingAuth(from: ExtensionIdentifier, toMetadata: IChatResponseProviderMetadata): toMetadata is IChatResponseProviderMetadata & { auth: NonNullable<IChatResponseProviderMetadata['auth']> } {
297+
// If the 'to' extension uses an auth check
298+
return !!toMetadata.auth
299+
// And we're asking from a different extension
300+
&& !ExtensionIdentifier.equals(toMetadata.extension, from);
301+
}
318302
}

0 commit comments

Comments
 (0)