diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 4cc3d85620b..52179154197 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -255,6 +255,9 @@ const _allApiProposals = { languageModelCapabilities: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelCapabilities.d.ts', }, + languageModelProxy: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelProxy.d.ts', + }, languageModelSystem: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f72d478be71..463fe9c7201 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1552,6 +1552,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I registerLanguageModelChatProvider: (vendor, provider) => { return extHostLanguageModels.registerLanguageModelChatProvider(extension, vendor, provider); }, + getModelProxy: () => { + checkProposedApiEnabled(extension, 'languageModelProxy'); + return extHostLanguageModels.getModelProxy(extension); + }, + registerLanguageModelProxyProvider: (provider) => { + checkProposedApiEnabled(extension, 'chatParticipantPrivate'); + return extHostLanguageModels.registerLanguageModelProxyProvider(extension, provider); + }, // --- embeddings get embeddingModels() { checkProposedApiEnabled(extension, 'embeddings'); diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index dce627ed19a..0342f7285bd 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -121,6 +121,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { private readonly _modelAccessList = new ExtensionIdentifierMap(); private readonly _pendingRequest = new Map(); private readonly _ignoredFileProviders = new Map(); + private _languageModelProxyProvider: vscode.LanguageModelProxyProvider | undefined; constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @@ -607,6 +608,27 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { return this._proxy.$fileIsIgnored(uri, token); } + async getModelProxy(extension: IExtensionDescription): Promise { + checkProposedApiEnabled(extension, 'languageModelProxy'); + + if (!this._languageModelProxyProvider) { + this._logService.warn('[LanguageModelProxy] No LanguageModelProxyProvider registered'); + return undefined; + } + + const requestingExtensionId = ExtensionIdentifier.toKey(extension.identifier); + try { + const result = await Promise.resolve(this._languageModelProxyProvider.provideModelProxy(requestingExtensionId, CancellationToken.None)); + if (result) { + return result; + } + } catch (err) { + this._logService.error(`[LanguageModelProxy] Provider ${ExtensionIdentifier.toKey(extension.identifier)} failed`, err); + } + + return undefined; + } + async $isFileIgnored(handle: number, uri: UriComponents, token: CancellationToken): Promise { const provider = this._ignoredFileProviders.get(handle); if (!provider) { @@ -627,4 +649,15 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { this._ignoredFileProviders.delete(handle); }); } + + registerLanguageModelProxyProvider(extension: IExtensionDescription, provider: vscode.LanguageModelProxyProvider): vscode.Disposable { + checkProposedApiEnabled(extension, 'chatParticipantPrivate'); + + this._languageModelProxyProvider = provider; + return toDisposable(() => { + if (this._languageModelProxyProvider === provider) { + this._languageModelProxyProvider = undefined; + } + }); + } } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts index b8f290ec476..9f90021eeba 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts @@ -285,4 +285,16 @@ declare module 'vscode' { } // #endregion + + // #region LanguageModelProxyProvider + + export interface LanguageModelProxyProvider { + provideModelProxy(forExtensionId: string, token: CancellationToken): ProviderResult; + } + + export namespace lm { + export function registerLanguageModelProxyProvider(provider: LanguageModelProxyProvider): Disposable; + } + + // #endregion } diff --git a/src/vscode-dts/vscode.proposed.languageModelProxy.d.ts b/src/vscode-dts/vscode.proposed.languageModelProxy.d.ts new file mode 100644 index 00000000000..32fcc0b0181 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.languageModelProxy.d.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + export interface LanguageModelProxyInfo { + readonly uri: Uri; + readonly key: string; + } + + export namespace lm { + /** + * Returns undefined if + * - The user is not logged in, or isn't the right SKU, with expected model access + * - The server fails to start for some reason + */ + export function getModelProxy(): Thenable; + } +}