Path: blob/main/src/vs/workbench/api/common/extHostLanguageModels.ts
5221 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 type * as vscode from 'vscode';6import { AsyncIterableProducer, AsyncIterableSource, RunOnceScheduler } from '../../../base/common/async.js';7import { VSBuffer } from '../../../base/common/buffer.js';8import { CancellationToken } from '../../../base/common/cancellation.js';9import { SerializedError, transformErrorForSerialization, transformErrorFromSerialization } from '../../../base/common/errors.js';10import { Emitter, Event } from '../../../base/common/event.js';11import { Iterable } from '../../../base/common/iterator.js';12import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';13import { URI, UriComponents } from '../../../base/common/uri.js';14import { localize } from '../../../nls.js';15import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';16import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';17import { ILogService } from '../../../platform/log/common/log.js';18import { Progress } from '../../../platform/progress/common/progress.js';19import { IChatMessage, IChatResponsePart, ILanguageModelChatInfoOptions, ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier } from '../../contrib/chat/common/languageModels.js';20import { DEFAULT_MODEL_PICKER_CATEGORY } from '../../contrib/chat/common/widget/input/modelPickerWidget.js';21import { INTERNAL_AUTH_PROVIDER_PREFIX } from '../../services/authentication/common/authentication.js';22import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';23import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js';24import { ExtHostLanguageModelsShape, MainContext, MainThreadLanguageModelsShape } from './extHost.protocol.js';25import { IExtHostAuthentication } from './extHostAuthentication.js';26import { IExtHostRpcService } from './extHostRpcService.js';27import * as typeConvert from './extHostTypeConverters.js';28import * as extHostTypes from './extHostTypes.js';29import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';3031export interface IExtHostLanguageModels extends ExtHostLanguageModels { }3233export const IExtHostLanguageModels = createDecorator<IExtHostLanguageModels>('IExtHostLanguageModels');3435type LanguageModelProviderData = {36readonly extension: IExtensionDescription;37readonly provider: vscode.LanguageModelChatProvider;38};3940type LMResponsePart = vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart;414243class LanguageModelResponse {4445readonly apiObject: vscode.LanguageModelChatResponse;4647private readonly _defaultStream = new AsyncIterableSource<LMResponsePart>();48private _isDone: boolean = false;4950constructor() {5152const that = this;5354const [stream1, stream2] = AsyncIterableProducer.tee(that._defaultStream.asyncIterable);5556this.apiObject = {57// result: promise,58get stream() {59return stream1;60},61get text() {62return stream2.map(part => {63if (part instanceof extHostTypes.LanguageModelTextPart) {64return part.value;65} else {66return undefined;67}68}).coalesce();69},70};71}7273handleResponsePart(parts: IChatResponsePart | IChatResponsePart[]): void {74if (this._isDone) {75return;76}7778const lmResponseParts: LMResponsePart[] = [];7980for (const part of Iterable.wrap(parts)) {8182let out: LMResponsePart;83if (part.type === 'text') {84out = new extHostTypes.LanguageModelTextPart(part.value, part.audience);85} else if (part.type === 'thinking') {86out = new extHostTypes.LanguageModelThinkingPart(part.value, part.id, part.metadata);8788} else if (part.type === 'data') {89out = new extHostTypes.LanguageModelDataPart(part.data.buffer, part.mimeType, part.audience);90} else {91out = new extHostTypes.LanguageModelToolCallPart(part.toolCallId, part.name, part.parameters);92}93lmResponseParts.push(out);94}9596this._defaultStream.emitMany(lmResponseParts);97}9899reject(err: Error): void {100this._isDone = true;101this._defaultStream.reject(err);102}103104resolve(): void {105this._isDone = true;106this._defaultStream.resolve();107}108}109110export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {111112declare _serviceBrand: undefined;113114private static _idPool = 1;115116private readonly _proxy: MainThreadLanguageModelsShape;117private readonly _onDidChangeModelAccess = new Emitter<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();118private readonly _onDidChangeProviders = new Emitter<void>();119readonly onDidChangeProviders = this._onDidChangeProviders.event;120private readonly _onDidChangeModelProxyAvailability = new Emitter<void>();121readonly onDidChangeModelProxyAvailability = this._onDidChangeModelProxyAvailability.event;122123private readonly _languageModelProviders = new Map<string, LanguageModelProviderData>();124// TODO @lramos15 - Remove the need for both info and metadata as it's a lot of redundancy. Should just need one125private readonly _localModels = new Map<string, { group: string | undefined; metadata: ILanguageModelChatMetadata; info: vscode.LanguageModelChatInformation }>();126private readonly _modelAccessList = new ExtensionIdentifierMap<ExtensionIdentifierSet>();127private readonly _pendingRequest = new Map<number, { languageModelId: string; res: LanguageModelResponse }>();128private readonly _ignoredFileProviders = new Map<number, vscode.LanguageModelIgnoredFileProvider>();129private _languageModelProxyProvider: vscode.LanguageModelProxyProvider | undefined;130131constructor(132@IExtHostRpcService extHostRpc: IExtHostRpcService,133@ILogService private readonly _logService: ILogService,134@IExtHostAuthentication private readonly _extHostAuthentication: IExtHostAuthentication,135) {136this._proxy = extHostRpc.getProxy(MainContext.MainThreadLanguageModels);137}138139dispose(): void {140this._onDidChangeModelAccess.dispose();141this._onDidChangeProviders.dispose();142this._onDidChangeModelProxyAvailability.dispose();143}144145registerLanguageModelChatProvider(extension: IExtensionDescription, vendor: string, provider: vscode.LanguageModelChatProvider): IDisposable {146147this._languageModelProviders.set(vendor, { extension: extension, provider });148this._proxy.$registerLanguageModelProvider(vendor);149150let providerChangeEventDisposable: IDisposable | undefined;151if (provider.onDidChangeLanguageModelChatInformation) {152providerChangeEventDisposable = provider.onDidChangeLanguageModelChatInformation(() => {153this._proxy.$onLMProviderChange(vendor);154});155}156157return toDisposable(() => {158this._languageModelProviders.delete(vendor);159this._localModels.forEach((value, key) => {160if (value.metadata.vendor === vendor) {161this._localModels.delete(key);162}163});164providerChangeEventDisposable?.dispose();165this._proxy.$unregisterProvider(vendor);166});167}168169private toModelIdentifier(vendor: string, group: string | undefined, modelId: string): string {170return group ? `${vendor}/${group}/${modelId}` : `${vendor}/${modelId}`;171}172173private getVendorFromModelIdentifier(modelIdentifier: string): string | undefined {174const firstSlash = modelIdentifier.indexOf('/');175return firstSlash === -1 ? undefined : modelIdentifier.substring(0, firstSlash);176}177178async $provideLanguageModelChatInfo(vendor: string, options: ILanguageModelChatInfoOptions, token: CancellationToken): Promise<ILanguageModelChatMetadataAndIdentifier[]> {179const data = this._languageModelProviders.get(vendor);180if (!data) {181return [];182}183const modelInformation: vscode.LanguageModelChatInformation[] = await data.provider.provideLanguageModelChatInformation({ silent: options.silent, configuration: options.configuration }, token) ?? [];184const modelMetadataAndIdentifier: ILanguageModelChatMetadataAndIdentifier[] = modelInformation.map((m): ILanguageModelChatMetadataAndIdentifier => {185let auth;186if (m.requiresAuthorization && isProposedApiEnabled(data.extension, 'chatProvider')) {187auth = {188providerLabel: data.extension.displayName || data.extension.name,189accountLabel: typeof m.requiresAuthorization === 'object' ? m.requiresAuthorization.label : undefined190};191}192if (m.capabilities.editTools) {193checkProposedApiEnabled(data.extension, 'chatProvider');194}195196const isDefaultForLocation: { [K in ChatAgentLocation]?: boolean } = {};197if (isProposedApiEnabled(data.extension, 'chatProvider')) {198if (m.isDefault === true) {199for (const key of Object.values(ChatAgentLocation)) {200if (typeof key === 'string') {201isDefaultForLocation[key as ChatAgentLocation] = true;202}203}204} else if (typeof m.isDefault === 'object') {205for (const key of Object.keys(m.isDefault)) {206const enumKey = parseInt(key) as extHostTypes.ChatLocation;207isDefaultForLocation[typeConvert.ChatLocation.from(enumKey)] = m.isDefault[enumKey];208}209}210}211212return {213metadata: {214extension: data.extension.identifier,215id: m.id,216vendor,217name: m.name ?? '',218family: m.family ?? '',219detail: m.detail,220tooltip: m.tooltip,221version: m.version,222multiplier: m.multiplier,223multiplierNumeric: m.multiplierNumeric,224maxInputTokens: m.maxInputTokens,225maxOutputTokens: m.maxOutputTokens,226auth,227isDefaultForLocation,228isUserSelectable: m.isUserSelectable,229statusIcon: m.statusIcon,230modelPickerCategory: m.category ?? DEFAULT_MODEL_PICKER_CATEGORY,231capabilities: m.capabilities ? {232vision: m.capabilities.imageInput,233editTools: m.capabilities.editTools,234toolCalling: !!m.capabilities.toolCalling,235agentMode: !!m.capabilities.toolCalling236} : undefined,237},238identifier: this.toModelIdentifier(vendor, options.group, m.id)239};240});241242this._localModels.forEach((value, key) => {243if (value.metadata.vendor === vendor && value.group === options.group) {244this._localModels.delete(key);245}246});247248for (let i = 0; i < modelMetadataAndIdentifier.length; i++) {249this._localModels.set(modelMetadataAndIdentifier[i].identifier, {250group: options.group,251metadata: modelMetadataAndIdentifier[i].metadata,252info: modelInformation[i]253});254}255256return modelMetadataAndIdentifier;257}258259async $startChatRequest(modelId: string, requestId: number, from: ExtensionIdentifier, messages: SerializableObjectWithBuffers<IChatMessage[]>, options: vscode.LanguageModelChatRequestOptions, token: CancellationToken): Promise<void> {260const knownModel = this._localModels.get(modelId);261if (!knownModel) {262throw new Error('Model not found');263}264265const data = this._languageModelProviders.get(knownModel.metadata.vendor);266if (!data) {267throw new Error(`Language model provider for '${knownModel.metadata.id}' not found.`);268}269270const queue: IChatResponsePart[] = [];271const sendNow = () => {272if (queue.length > 0) {273this._proxy.$reportResponsePart(requestId, new SerializableObjectWithBuffers(queue));274queue.length = 0;275}276};277const queueScheduler = new RunOnceScheduler(sendNow, 30);278const sendSoon = (part: IChatResponsePart) => {279const newLen = queue.push(part);280// flush/send if things pile up more than expected281if (newLen > 30) {282sendNow();283queueScheduler.cancel();284} else {285queueScheduler.schedule();286}287};288289const progress = new Progress<vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart>(async fragment => {290if (token.isCancellationRequested) {291this._logService.warn(`[CHAT](${data.extension.identifier.value}) CANNOT send progress because the REQUEST IS CANCELLED`);292return;293}294295let part: IChatResponsePart | undefined;296if (fragment instanceof extHostTypes.LanguageModelToolCallPart) {297part = { type: 'tool_use', name: fragment.name, parameters: fragment.input, toolCallId: fragment.callId };298} else if (fragment instanceof extHostTypes.LanguageModelTextPart) {299part = { type: 'text', value: fragment.value, audience: fragment.audience };300} else if (fragment instanceof extHostTypes.LanguageModelDataPart) {301part = { type: 'data', mimeType: fragment.mimeType, data: VSBuffer.wrap(fragment.data), audience: fragment.audience };302} else if (fragment instanceof extHostTypes.LanguageModelThinkingPart) {303part = { type: 'thinking', value: fragment.value, id: fragment.id, metadata: fragment.metadata };304}305306if (!part) {307this._logService.warn(`[CHAT](${data.extension.identifier.value}) UNKNOWN part ${JSON.stringify(fragment)}`);308return;309}310311sendSoon(part);312});313314let value: unknown;315316try {317value = data.provider.provideLanguageModelChatResponse(318knownModel.info,319messages.value.map(typeConvert.LanguageModelChatMessage2.to),320{ ...options, modelOptions: options.modelOptions ?? {}, requestInitiator: ExtensionIdentifier.toKey(from), toolMode: options.toolMode ?? extHostTypes.LanguageModelChatToolMode.Auto },321progress,322token323);324325} catch (err) {326// synchronously failed327throw err;328}329330Promise.resolve(value).then(() => {331sendNow();332this._proxy.$reportResponseDone(requestId, undefined);333}, err => {334sendNow();335this._proxy.$reportResponseDone(requestId, transformErrorForSerialization(err));336});337}338339//#region --- token counting340341$provideTokenLength(modelId: string, value: string, token: CancellationToken): Promise<number> {342const knownModel = this._localModels.get(modelId);343if (!knownModel) {344return Promise.resolve(0);345}346const data = this._languageModelProviders.get(knownModel.metadata.vendor);347if (!data) {348return Promise.resolve(0);349}350return Promise.resolve(data.provider.provideTokenCount(knownModel.info, value, token));351}352353354//#region --- making request355356async getDefaultLanguageModel(extension: IExtensionDescription, forceResolveModels?: boolean): Promise<vscode.LanguageModelChat | undefined> {357let defaultModelId: string | undefined;358359if (forceResolveModels) {360await this.selectLanguageModels(extension, {});361}362363for (const [modelIdentifier, modelData] of this._localModels) {364if (modelData.metadata.isDefaultForLocation[ChatAgentLocation.Chat]) {365defaultModelId = modelIdentifier;366break;367}368}369if (!defaultModelId && !forceResolveModels) {370// Maybe the default wasn't cached so we will try again with resolving the models too371return this.getDefaultLanguageModel(extension, true);372}373return this.getLanguageModelByIdentifier(extension, defaultModelId);374}375376async getLanguageModelByIdentifier(extension: IExtensionDescription, modelId: string | undefined): Promise<vscode.LanguageModelChat | undefined> {377if (!modelId) {378return undefined;379}380381let model = this._localModels.get(modelId);382if (!model) {383// model gone? is this an error on us? Try to resolve model again384this._logService.warn(`[LanguageModelProxy](${extension.identifier.value}) Could not find model '${modelId}' in local cache. Trying to resolve model again.`);385const vendor = this.getVendorFromModelIdentifier(modelId);386if (!vendor) {387this._logService.warn(`[LanguageModelProxy](${extension.identifier.value}) Could not extract vendor from model identifier '${modelId}'.`);388return undefined;389}390await this.selectLanguageModels(extension, { vendor });391model = this._localModels.get(modelId);392if (!model) {393this._logService.warn(`[LanguageModelProxy](${extension.identifier.value}) Could not find model '${modelId}' in local cache after re-resolving models.`);394return undefined;395}396}397398// make sure auth information is correct399if (this._isUsingAuth(extension.identifier, model.metadata)) {400await this._fakeAuthPopulate(model.metadata);401}402403let apiObject: vscode.LanguageModelChat | undefined;404if (!apiObject) {405const that = this;406apiObject = {407id: model.info.id,408vendor: model.metadata.vendor,409family: model.info.family,410version: model.info.version,411name: model.info.name,412capabilities: {413supportsImageToText: model.metadata.capabilities?.vision ?? false,414supportsToolCalling: !!model.metadata.capabilities?.toolCalling,415editToolsHint: model.metadata.capabilities?.editTools,416},417maxInputTokens: model.metadata.maxInputTokens,418countTokens(text, token) {419if (!that._localModels.has(modelId)) {420throw extHostTypes.LanguageModelError.NotFound(modelId);421}422return that._computeTokenLength(modelId, text, token ?? CancellationToken.None);423},424sendRequest(messages, options, token) {425if (!that._localModels.has(modelId)) {426throw extHostTypes.LanguageModelError.NotFound(modelId);427}428return that._sendChatRequest(extension, modelId, messages, options ?? {}, token ?? CancellationToken.None);429}430};431432Object.freeze(apiObject);433}434435return apiObject;436}437438async selectLanguageModels(extension: IExtensionDescription, selector: vscode.LanguageModelChatSelector) {439440// this triggers extension activation441const models = await this._proxy.$selectChatModels({ ...selector, extension: extension.identifier });442443const result: vscode.LanguageModelChat[] = [];444445const modelPromises = models.map(identifier => this.getLanguageModelByIdentifier(extension, identifier));446const modelResults = await Promise.all(modelPromises);447for (const model of modelResults) {448if (model) {449result.push(model);450}451}452453return result;454}455456private async _sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelChatMessage2[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) {457458const internalMessages: IChatMessage[] = this._convertMessages(extension, messages);459460const from = extension.identifier;461const metadata = this._localModels.get(languageModelId)?.metadata;462463if (!metadata || !this._localModels.has(languageModelId)) {464throw extHostTypes.LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`);465}466467if (this._isUsingAuth(from, metadata)) {468const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, false);469470if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) {471throw extHostTypes.LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`);472}473}474475const requestId = (Math.random() * 1e6) | 0;476const res = new LanguageModelResponse();477this._pendingRequest.set(requestId, { languageModelId, res });478479try {480await this._proxy.$tryStartChatRequest(from, languageModelId, requestId, new SerializableObjectWithBuffers(internalMessages), options, token);481482} catch (error) {483// error'ing here means that the request could NOT be started/made, e.g. wrong model, no access, etc, but484// later the response can fail as well. Those failures are communicated via the stream-object485this._pendingRequest.delete(requestId);486throw extHostTypes.LanguageModelError.tryDeserialize(error) ?? error;487}488489return res.apiObject;490}491492private _convertMessages(extension: IExtensionDescription, messages: vscode.LanguageModelChatMessage2[]) {493const internalMessages: IChatMessage[] = [];494for (const message of messages) {495if (message.role as number === extHostTypes.LanguageModelChatMessageRole.System) {496checkProposedApiEnabled(extension, 'languageModelSystem');497}498internalMessages.push(typeConvert.LanguageModelChatMessage2.from(message));499}500return internalMessages;501}502503async $acceptResponsePart(requestId: number, chunk: SerializableObjectWithBuffers<IChatResponsePart | IChatResponsePart[]>): Promise<void> {504const data = this._pendingRequest.get(requestId);505if (data) {506data.res.handleResponsePart(chunk.value);507}508}509510async $acceptResponseDone(requestId: number, error: SerializedError | undefined): Promise<void> {511const data = this._pendingRequest.get(requestId);512if (!data) {513return;514}515this._pendingRequest.delete(requestId);516if (error) {517// we error the stream because that's the only way to signal518// that the request has failed519data.res.reject(extHostTypes.LanguageModelError.tryDeserialize(error) ?? transformErrorFromSerialization(error));520} else {521data.res.resolve();522}523}524525// BIG HACK: Using AuthenticationProviders to check access to Language Models526private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, justification: string | undefined, silent: boolean | undefined): Promise<boolean> {527// This needs to be done in both MainThread & ExtHost ChatProvider528const providerId = INTERNAL_AUTH_PROVIDER_PREFIX + to.identifier.value;529const session = await this._extHostAuthentication.getSession(from, providerId, [], { silent: true });530531if (session) {532this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]);533return true;534}535536if (silent) {537return false;538}539540try {541const detail = justification542? localize('chatAccessWithJustification', "Justification: {1}", to.displayName, justification)543: undefined;544await this._extHostAuthentication.getSession(from, providerId, [], { forceNewSession: { detail } });545this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]);546return true;547548} catch (err) {549// ignore550return false;551}552}553554private _isUsingAuth(from: ExtensionIdentifier, toMetadata: ILanguageModelChatMetadata): toMetadata is ILanguageModelChatMetadata & { auth: NonNullable<ILanguageModelChatMetadata['auth']> } {555// If the 'to' extension uses an auth check556return !!toMetadata.auth557// And we're asking from a different extension558&& !ExtensionIdentifier.equals(toMetadata.extension, from);559}560561private async _fakeAuthPopulate(metadata: ILanguageModelChatMetadata): Promise<void> {562563if (!metadata.auth) {564return;565}566567for (const from of this._languageAccessInformationExtensions) {568try {569await this._getAuthAccess(from, { identifier: metadata.extension, displayName: '' }, undefined, true);570} catch (err) {571this._logService.error('Fake Auth request failed');572this._logService.error(err);573}574}575}576577private async _computeTokenLength(modelId: string, value: string | vscode.LanguageModelChatMessage2, token: vscode.CancellationToken): Promise<number> {578579const data = this._localModels.get(modelId);580if (!data) {581throw extHostTypes.LanguageModelError.NotFound(`Language model '${modelId}' is unknown.`);582}583return this._languageModelProviders.get(data.metadata.vendor)?.provider.provideTokenCount(data.info, value, token) ?? 0;584// return this._proxy.$countTokens(languageModelId, (typeof value === 'string' ? value : typeConvert.LanguageModelChatMessage2.from(value)), token);585}586587$updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void {588const updated = new Array<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();589for (const { from, to, enabled } of data) {590const set = this._modelAccessList.get(from) ?? new ExtensionIdentifierSet();591const oldValue = set.has(to);592if (oldValue !== enabled) {593if (enabled) {594set.add(to);595} else {596set.delete(to);597}598this._modelAccessList.set(from, set);599const newItem = { from, to };600updated.push(newItem);601this._onDidChangeModelAccess.fire(newItem);602}603}604}605606private readonly _languageAccessInformationExtensions = new Set<Readonly<IExtensionDescription>>();607608createLanguageModelAccessInformation(from: Readonly<IExtensionDescription>): vscode.LanguageModelAccessInformation {609610this._languageAccessInformationExtensions.add(from);611612// const that = this;613const _onDidChangeAccess = Event.signal(Event.filter(this._onDidChangeModelAccess.event, e => ExtensionIdentifier.equals(e.from, from.identifier)));614const _onDidAddRemove = Event.signal(this._onDidChangeProviders.event);615616return {617get onDidChange() {618return Event.any(_onDidChangeAccess, _onDidAddRemove);619},620canSendRequest(chat: vscode.LanguageModelChat): boolean | undefined {621return true;622// TODO @lramos15 - Fix623624// let metadata: ILanguageModelChatMetadata | undefined;625626// out: for (const [_, value] of that._allLanguageModelData) {627// for (const candidate of value.apiObjects.values()) {628// if (candidate === chat) {629// metadata = value.metadata;630// break out;631// }632// }633// }634// if (!metadata) {635// return undefined;636// }637// if (!that._isUsingAuth(from.identifier, metadata)) {638// return true;639// }640641// const list = that._modelAccessList.get(from.identifier);642// if (!list) {643// return undefined;644// }645// return list.has(metadata.extension);646}647};648}649650fileIsIgnored(extension: IExtensionDescription, uri: vscode.Uri, token: vscode.CancellationToken = CancellationToken.None): Promise<boolean> {651checkProposedApiEnabled(extension, 'chatParticipantAdditions');652653return this._proxy.$fileIsIgnored(uri, token);654}655656get isModelProxyAvailable(): boolean {657return !!this._languageModelProxyProvider;658}659660async getModelProxy(extension: IExtensionDescription): Promise<vscode.LanguageModelProxy> {661checkProposedApiEnabled(extension, 'languageModelProxy');662663if (!this._languageModelProxyProvider) {664this._logService.trace('[LanguageModelProxy] No LanguageModelProxyProvider registered');665throw new Error('No language model proxy provider is registered.');666}667668const requestingExtensionId = ExtensionIdentifier.toKey(extension.identifier);669try {670const result = await Promise.resolve(this._languageModelProxyProvider.provideModelProxy(requestingExtensionId, CancellationToken.None));671if (!result) {672this._logService.warn(`[LanguageModelProxy] Provider returned no proxy for ${requestingExtensionId}`);673throw new Error('Language model proxy is not available.');674}675return result;676} catch (err) {677this._logService.error(`[LanguageModelProxy] Provider failed to return proxy for ${requestingExtensionId}`, err);678throw err;679}680}681682async $isFileIgnored(handle: number, uri: UriComponents, token: CancellationToken): Promise<boolean> {683const provider = this._ignoredFileProviders.get(handle);684if (!provider) {685throw new Error('Unknown LanguageModelIgnoredFileProvider');686}687688return (await provider.provideFileIgnored(URI.revive(uri), token)) ?? false;689}690691registerIgnoredFileProvider(extension: IExtensionDescription, provider: vscode.LanguageModelIgnoredFileProvider): vscode.Disposable {692checkProposedApiEnabled(extension, 'chatParticipantPrivate');693694const handle = ExtHostLanguageModels._idPool++;695this._proxy.$registerFileIgnoreProvider(handle);696this._ignoredFileProviders.set(handle, provider);697return toDisposable(() => {698this._proxy.$unregisterFileIgnoreProvider(handle);699this._ignoredFileProviders.delete(handle);700});701}702703registerLanguageModelProxyProvider(extension: IExtensionDescription, provider: vscode.LanguageModelProxyProvider): vscode.Disposable {704checkProposedApiEnabled(extension, 'chatParticipantPrivate');705706this._languageModelProxyProvider = provider;707this._onDidChangeModelProxyAvailability.fire();708return toDisposable(() => {709if (this._languageModelProxyProvider === provider) {710this._languageModelProxyProvider = undefined;711this._onDidChangeModelProxyAvailability.fire();712}713});714}715}716717718