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

768 LOC · 149 covered · 619 uncovered · 35 ranges · 38 concepts · 1 introducers · 33 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/workbench/api/common/extHostAuthentication.ts · 1040 LOCcommon/extHostAuthentica…src/vs/workbench/api/common/extHostExtensionService.ts · 1285 LOCcommon/extHostExtensionS…src/vs/workbench/api/common/extHostFileSystemConsumer.ts · 260 LOCcommon/extHostFileSystem…src/vs/workbench/api/common/extHostLocalizationService.ts · 109 LOCcommon/extHostLocalizati…src/vs/workbench/api/common/extHostManagedSockets.ts · 121 LOCcommon/extHostManagedSoc…src/vs/workbench/api/common/extHostMemento.ts · 122 LOCcommon/extHostMemento.tssrc/vs/workbench/api/common/extHostProgress.ts · 107 LOCcommon/extHostProgress.t…src/vs/workbench/api/common/extHostSecretState.ts · 42 LOCcommon/extHostSecretStat…src/vs/workbench/api/common/extHostSecrets.ts · 51 LOCcommon/extHostSecrets.tssrc/vs/workbench/api/common/extHostStorage.ts · 75 LOCcommon/extHostStorage.tssrc/vs/workbench/api/common/extHostStoragePaths.ts · 95 LOCcommon/extHostStoragePat…src/vs/workbench/api/common/extHostUrls.ts · 70 LOCcommon/extHostUrls.tssrc/vs/workbench/api/common/extHostWindow.ts · 116 LOCcommon/extHostWindow.tssrc/vs/workbench/api/common/extHostXaaAuthProvider.ts · 402 LOCcommon/extHostXaaAuthPro…src/vs/workbench/services/extensions/common/workspaceContains.ts · 139 LOCcommon/workspaceContains…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 3 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 4 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×2 · 4 introduced LOCextHostMcp.ts ×2extHostMcp.ts ×1 · 1 introduced LOCextHostMcp.ts ×1extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×4 · 6 introduced LOCextHostMcp.ts ×4extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1, extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×1 · 6 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×4 · 13 introduced LOCextHostMcp.ts ×4extHostMcpNode.ts ×8 · 45 introduced LOCextHostMcpNode.ts ×8extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×5 · 25 introduced LOCextHostMcp.ts ×5extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · 0 introduced LOCextHostMcp.test|title=Ex…extHostMcp.ts ×3 · 23 introduced LOCextHostMcp.ts ×3extHostMcp.ts ×1 · 2 introduced LOCextHostMcp.ts ×1extHostMcp.ts ×8 · 60 introduced LOCextHostMcp.ts ×8extHostMcp.ts ×43 · 874 introduced LOCextHostMcp.ts ×43extensionHostMain.ts ×1 · 2 introduced LOCextensionHostMain.ts ×1reset prepareStackTrace-callback|occurrence=1, extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · 0 introduced LOCreset prepareStackTrace-…extensionHostMain.ts ×5 · 26 introduced LOCextensionHostMain.ts ×5f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · 0 introduced LOCf0f2e182082072efdaf0f8e1…extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×1 · 3 introduced LOCextensionHostMain.ts ×1extensionHostMain.ts ×11 · 164 introduced LOCextensionHostMain.ts ×11extHostExtensionService.ts ×64 · 1246 introduced LOCextHostExtensionService.…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should allow undefined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata properties should expose readonly properties|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should handle multiple Bearer challenges and use first scope|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should ignore non-Bearer schemes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when no WWW-Authenticate header and scopes are already undefined|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are same but in different order|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return false when scopes are the same|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true and update scopes when WWW-Authenticate header contains new scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from defined scopes to undefined (no scope in header)|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp IAuthMetadata update() should return true when updating from undefined scopes to defined scopes|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should create IAuthMetadata with fetched server metadata|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should fall back to default metadata when server metadata fetch fails|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle empty scope string in WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle invalid JSON in resource metadata response|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle malformed WWW-Authenticate header gracefully|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should handle non-401 status codes in update()|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should pass launch headers when fetching metadata from same origin|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use resource_metadata challenge URL from WWW-Authenticate header|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header even when resource metadata has scopes_supported|occurrence=1extHostMcp.test|title=Ex…extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extHostMcp.test|title=ExtHostMcp createAuthMetadata should use scopes from WWW-Authenticate header when resource metadata has none|occurrence=1extHostMcp.test|title=Ex…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 basics|occurrence=1extensionHostMain.test|t…f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Never restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, separate operations|occurrence=1f0f2e182082072efdaf0f8e1…f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 https://gist.github.com/thecrypticace/f0f2e182082072efdaf0f8e1537d2cce Restored, too many uses before restoration|occurrence=1f0f2e182082072efdaf0f8e1…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 prevent rewrapping|occurrence=1extensionHostMain.test|t…reset prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 set/reset prepareStackTrace-callback|occurrence=1reset prepareStackTrace-…extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/common/extensionHostMain.test|title=ExtensionHostMain#ErrorHandler - Wrapping prepareStackTrace can cause slowdown and eventual stack overflow #184926 wrap prepareStackTrace-callback|occurrence=1extensionHostMain.test|t…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg does not add stray ^ to argument values|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg doubles embedded double quotes (cmd.exe convention)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg neutralizes cmd.exe metacharacters inside quotes (CVE-2024-27980)|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with parentheses without injecting ^|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg preserves paths with spaces|occurrence=1extHostMcpNode.test|titl…extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/api/test/node/extHostMcpNode.test|title=extHostMcpNode - escapeCmdArg wraps simple values in double quotes|occurrence=1extHostMcpNode.test|titl…Focused file · src/vs/workbench/api/common/extHostLanguageModels.ts · 768 LOCcommon/extHostLanguageMo…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- extHostExtensionService.ts ×64
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 type * as vscode from 'vscode';
7 > import { AsyncIterableProducer, AsyncIterableSource, RunOnceScheduler } from '../../../base/common/async.js';
8 > import { VSBuffer } from '../../../base/common/buffer.js';
9 > import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js';
10 > import { SerializedError, transformErrorForSerialization, transformErrorFromSerialization } from '../../../base/common/errors.js';
11 > import { Emitter, Event } from '../../../base/common/event.js';
12 > import { Iterable } from '../../../base/common/iterator.js';
13 > import { DisposableMap, IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
14 > import { IJSONSchema } from '../../../base/common/jsonSchema.js';
15 > import { URI, UriComponents } from '../../../base/common/uri.js';
16 > import { localize } from '../../../nls.js';
17 > import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
18 > import { createDecorator } from '../../../platform/instantiation/common/instantiation.js';
19 > import { ILogService } from '../../../platform/log/common/log.js';
20 > import { Progress } from '../../../platform/progress/common/progress.js';
21 > import { COPILOT_VENDOR_ID, IChatMessage, IChatResponsePart, ILanguageModelChatInfoOptions, ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier, ILanguageModelChatRequestOptions } from '../../contrib/chat/common/languageModels.js';
22 > import { INTERNAL_AUTH_PROVIDER_PREFIX } from '../../services/authentication/common/authentication.js';
23 > import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js';
24 > import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js';
25 > import { ExtHostLanguageModelsShape, MainContext, MainThreadLanguageModelsShape } from './extHost.protocol.js';
26 > import { IExtHostAuthentication } from './extHostAuthentication.js';
27 > import { IExtHostRpcService } from './extHostRpcService.js';
28 > import * as typeConvert from './extHostTypeConverters.js';
29 > import * as extHostTypes from './extHostTypes.js';
30 > import { ChatAgentLocation } from '../../contrib/chat/common/constants.js';
31 >
32 > export interface IExtHostLanguageModels extends ExtHostLanguageModels { }
33 >
34 > export const IExtHostLanguageModels = createDecorator<IExtHostLanguageModels>('IExtHostLanguageModels');
35 >
36 > type LanguageModelProviderData = {
37 > readonly extension: IExtensionDescription;
38 > readonly provider: vscode.LanguageModelChatProvider;
39 > };
40 >
41 > type LMResponsePart = vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart;
42 >
43 >
44 > class LanguageModelResponse {
45 >
46 > readonly apiObject: vscode.LanguageModelChatResponse;
47 >
48 > private readonly _defaultStream = new AsyncIterableSource<LMResponsePart>();
49 > private _isDone: boolean = false;
50 >
51 > constructor() {
52
53 const that = this;
54
55 const [stream1, stream2] = AsyncIterableProducer.tee(that._defaultStream.asyncIterable);
56
57 this.apiObject = {
58 // result: promise,
59 get stream() {
60 return stream1;
61 },
62 get text() {
63 return stream2.map(part => {
64 if (part instanceof extHostTypes.LanguageModelTextPart) {
65 return part.value;
66 } else {
67 return undefined;
68 }
69 }).coalesce();
70 },
71 };
72 }
74 > handleResponsePart(parts: IChatResponsePart | IChatResponsePart[]): void {
75 if (this._isDone) {
76 return;
77 }
78
79 const lmResponseParts: LMResponsePart[] = [];
80
81 for (const part of Iterable.wrap(parts)) {
82
83 let out: LMResponsePart;
84 if (part.type === 'text') {
85 out = new extHostTypes.LanguageModelTextPart(part.value, part.audience);
86 } else if (part.type === 'thinking') {
87 out = new extHostTypes.LanguageModelThinkingPart(part.value, part.id, part.metadata);
88
89 } else if (part.type === 'data') {
90 out = new extHostTypes.LanguageModelDataPart(part.data.buffer, part.mimeType, part.audience);
91 } else {
92 out = new extHostTypes.LanguageModelToolCallPart(part.toolCallId, part.name, part.parameters);
93 }
94 lmResponseParts.push(out);
95 }
96
97 this._defaultStream.emitMany(lmResponseParts);
98 }
100 > reject(err: Error): void {
101 this._isDone = true;
102 this._defaultStream.reject(err);
103 }
105 > resolve(): void {
106 this._isDone = true;
107 this._defaultStream.resolve();
108 }
110 >
111 > export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
112 >
113 > declare _serviceBrand: undefined;
114 >
115 > private static _idPool = 1;
116 >
117 > private readonly _proxy: MainThreadLanguageModelsShape;
118 > private readonly _onDidChangeModelAccess = new Emitter<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();
119 > private readonly _onDidChangeProviders = new Emitter<void>();
120 > readonly onDidChangeProviders = this._onDidChangeProviders.event;
121 > private readonly _onDidChangeModelProxyAvailability = new Emitter<void>();
122 > readonly onDidChangeModelProxyAvailability = this._onDidChangeModelProxyAvailability.event;
123 >
124 > private readonly _languageModelProviders = new Map<string, LanguageModelProviderData>();
125 > // TODO @lramos15 - Remove the need for both info and metadata as it's a lot of redundancy. Should just need one
126 > private readonly _localModels = new Map<string, { group: string | undefined; metadata: ILanguageModelChatMetadata; info: vscode.LanguageModelChatInformation }>();
127 > private readonly _modelAccessList = new ExtensionIdentifierMap<ExtensionIdentifierSet>();
128 > private readonly _pendingRequest = new Map<number, { languageModelId: string; res: LanguageModelResponse }>();
129 > private readonly _pendingCancelCTS = new DisposableMap<number, CancellationTokenSource>();
130 > private readonly _ignoredFileProviders = new Map<number, vscode.LanguageModelIgnoredFileProvider>();
131 > private _languageModelProxyProvider: vscode.LanguageModelProxyProvider | undefined;
132 >
133 > constructor(
134 @IExtHostRpcService extHostRpc: IExtHostRpcService,
135 @ILogService private readonly _logService: ILogService,
136 @IExtHostAuthentication private readonly _extHostAuthentication: IExtHostAuthentication,
137 ) {
138 this._proxy = extHostRpc.getProxy(MainContext.MainThreadLanguageModels);
139 }
141 > dispose(): void {
142 this._onDidChangeModelAccess.dispose();
143 this._onDidChangeProviders.dispose();
144 this._onDidChangeModelProxyAvailability.dispose();
145 this._pendingRequest.clear();
146 this._pendingCancelCTS.dispose();
147 }
149 > registerLanguageModelChatProvider(extension: IExtensionDescription, vendor: string, provider: vscode.LanguageModelChatProvider): IDisposable {
150
151 this._languageModelProviders.set(vendor, { extension: extension, provider });
152 this._proxy.$registerLanguageModelProvider(vendor);
153
154 let providerChangeEventDisposable: IDisposable | undefined;
155 if (provider.onDidChangeLanguageModelChatInformation) {
156 providerChangeEventDisposable = provider.onDidChangeLanguageModelChatInformation(() => {
157 this._proxy.$onLMProviderChange(vendor);
158 });
159 }
160
161 return toDisposable(() => {
162 this._languageModelProviders.delete(vendor);
163 this._localModels.forEach((value, key) => {
164 if (value.metadata.vendor === vendor) {
165 this._localModels.delete(key);
166 }
167 });
168 providerChangeEventDisposable?.dispose();
169 this._proxy.$unregisterProvider(vendor);
170 });
171 }
173 > private toModelIdentifier(vendor: string, group: string | undefined, modelId: string): string {
174 return group ? `${vendor}/${group}/${modelId}` : `${vendor}/${modelId}`;
175 }
177 > private getVendorFromModelIdentifier(modelIdentifier: string): string | undefined {
178 const firstSlash = modelIdentifier.indexOf('/');
179 return firstSlash === -1 ? undefined : modelIdentifier.substring(0, firstSlash);
180 }
182 > async $provideLanguageModelChatInfo(vendor: string, options: ILanguageModelChatInfoOptions, token: CancellationToken): Promise<ILanguageModelChatMetadataAndIdentifier[]> {
183 const data = this._languageModelProviders.get(vendor);
184 if (!data) {
185 return [];
186 }
187 const modelInformation: vscode.LanguageModelChatInformation[] = await data.provider.provideLanguageModelChatInformation({ silent: options.silent, configuration: options.configuration }, token) ?? [];
188 const modelMetadataAndIdentifier: ILanguageModelChatMetadataAndIdentifier[] = modelInformation.map((m): ILanguageModelChatMetadataAndIdentifier => {
189 let auth;
190 if (m.requiresAuthorization && isProposedApiEnabled(data.extension, 'chatProvider')) {
191 auth = {
192 providerLabel: data.extension.displayName || data.extension.name,
193 accountLabel: typeof m.requiresAuthorization === 'object' ? m.requiresAuthorization.label : undefined
194 };
195 }
196 if (m.capabilities.editTools) {
197 checkProposedApiEnabled(data.extension, 'chatProvider');
198 }
199
200 const isDefaultForLocation: { [K in ChatAgentLocation]?: boolean } = {};
201 if (isProposedApiEnabled(data.extension, 'chatProvider')) {
202 if (m.isDefault === true) {
203 for (const key of Object.values(ChatAgentLocation)) {
204 if (typeof key === 'string') {
205 isDefaultForLocation[key as ChatAgentLocation] = true;
206 }
207 }
208 } else if (typeof m.isDefault === 'object') {
209 for (const key of Object.keys(m.isDefault)) {
210 const enumKey = parseInt(key) as extHostTypes.ChatLocation;
211 isDefaultForLocation[typeConvert.ChatLocation.from(enumKey)] = m.isDefault[enumKey];
212 }
213 }
214 }
215
216 return {
217 metadata: {
218 extension: data.extension.identifier,
219 id: m.id,
220 vendor,
221 name: m.name ?? '',
222 family: m.family ?? '',
223 detail: m.detail,
224 tooltip: m.tooltip,
225 version: m.version,
226 multiplierNumeric: m.multiplierNumeric,
227 isBYOK: m.isBYOK,
228 pricing: m.pricing,
229 inputCost: m.inputCost,
230 outputCost: m.outputCost,
231 cacheCost: m.cacheCost,
232 cacheWriteCost: m.cacheWriteCost,
233 longContextInputCost: m.longContextInputCost,
234 longContextOutputCost: m.longContextOutputCost,
235 longContextCacheCost: m.longContextCacheCost,
236 longContextCacheWriteCost: m.longContextCacheWriteCost,
237 priceCategory: m.priceCategory,
238 category: m.category,
239 maxInputTokens: m.maxInputTokens,
240 maxOutputTokens: m.maxOutputTokens,
241 auth,
242 isDefaultForLocation,
243 isUserSelectable: m.isUserSelectable,
244 statusIcon: m.statusIcon,
245 targetChatSessionType: m.targetChatSessionType,
246 configurationSchema: m.configurationSchema as IJSONSchema | undefined,
247 warningText: m.warningText,
248 promo: m.promo,
249 capabilities: m.capabilities ? {
250 vision: m.capabilities.imageInput,
251 editTools: m.capabilities.editTools,
252 toolCalling: !!m.capabilities.toolCalling,
253 agentMode: !!m.capabilities.toolCalling
254 } : undefined,
255 },
256 identifier: this.toModelIdentifier(vendor, options.group, m.id)
257 };
258 });
259
260 this._localModels.forEach((value, key) => {
261 if (value.metadata.vendor === vendor && value.group === options.group) {
262 this._localModels.delete(key);
263 }
264 });
265
266 for (let i = 0; i < modelMetadataAndIdentifier.length; i++) {
267 this._localModels.set(modelMetadataAndIdentifier[i].identifier, {
268 group: options.group,
269 metadata: modelMetadataAndIdentifier[i].metadata,
270 info: modelInformation[i]
271 });
272 }
273
274 return modelMetadataAndIdentifier;
275 }
277 > async $startChatRequest(modelId: string, requestId: number, from: ExtensionIdentifier | undefined, messages: SerializableObjectWithBuffers<IChatMessage[]>, options: ILanguageModelChatRequestOptions, token: CancellationToken): Promise<void> {
278 const knownModel = this._localModels.get(modelId);
279 if (!knownModel) {
280 throw new Error('Model not found');
281 }
282
283 const data = this._languageModelProviders.get(knownModel.metadata.vendor);
284 if (!data) {
285 throw new Error(`Language model provider for '${knownModel.metadata.id}' not found.`);
286 }
287
288 // Create a local CTS so the provider's token can be cancelled via
289 // $cancelLanguageModelChatRequest even after the RPC cancel handler
290 // for the original token has been removed.
291 const cts = new CancellationTokenSource(token);
292 this._pendingCancelCTS.set(requestId, cts);
293
294 const providerToken = cts.token;
295
296 const queue: IChatResponsePart[] = [];
297 const sendNow = () => {
298 if (queue.length > 0) {
299 this._proxy.$reportResponsePart(requestId, new SerializableObjectWithBuffers(queue));
300 queue.length = 0;
301 }
302 };
303 const queueScheduler = new RunOnceScheduler(sendNow, 30);
304 const sendSoon = (part: IChatResponsePart) => {
305 const newLen = queue.push(part);
306 // flush/send if things pile up more than expected
307 if (newLen > 30) {
308 sendNow();
309 queueScheduler.cancel();
310 } else {
311 queueScheduler.schedule();
312 }
313 };
314
315 const progress = new Progress<vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart>(async fragment => {
316 if (providerToken.isCancellationRequested) {
317 this._logService.warn(`[CHAT](${data.extension.identifier.value}) CANNOT send progress because the REQUEST IS CANCELLED`);
318 return;
319 }
320
321 let part: IChatResponsePart | undefined;
322 if (fragment instanceof extHostTypes.LanguageModelToolCallPart) {
323 part = { type: 'tool_use', name: fragment.name, parameters: fragment.input, toolCallId: fragment.callId };
324 } else if (fragment instanceof extHostTypes.LanguageModelTextPart) {
325 part = { type: 'text', value: fragment.value, audience: fragment.audience };
326 } else if (fragment instanceof extHostTypes.LanguageModelDataPart) {
327 part = { type: 'data', mimeType: fragment.mimeType, data: VSBuffer.wrap(fragment.data), audience: fragment.audience };
328 } else if (fragment instanceof extHostTypes.LanguageModelThinkingPart) {
329 part = { type: 'thinking', value: fragment.value, id: fragment.id, metadata: fragment.metadata };
330 }
331
332 if (!part) {
333 this._logService.warn(`[CHAT](${data.extension.identifier.value}) UNKNOWN part ${JSON.stringify(fragment)}`);
334 return;
335 }
336
337 sendSoon(part);
338 });
339
340 let value: unknown;
341
342 try {
343 value = data.provider.provideLanguageModelChatResponse(
344 knownModel.info,
345 messages.value.map(typeConvert.LanguageModelChatMessage2.to),
346 // todo@connor4312: move `core` -> `undefined` after 1.111 Insiders is out
347 { ...options, modelOptions: options.modelOptions ?? {}, modelConfiguration: options.configuration, requestInitiator: from ? ExtensionIdentifier.toKey(from) : 'core', toolMode: options.toolMode ?? extHostTypes.LanguageModelChatToolMode.Auto },
348 progress,
349 providerToken
350 );
351
352 } catch (err) {
353 // synchronously failed
354 this._pendingCancelCTS.deleteAndDispose(requestId);
355 throw err;
356 }
357
358 Promise.resolve(value).then(() => {
359 sendNow();
360 this._pendingCancelCTS.deleteAndDispose(requestId);
361 this._proxy.$reportResponseDone(requestId, undefined);
362 }, err => {
363 sendNow();
364 this._pendingCancelCTS.deleteAndDispose(requestId);
365 this._proxy.$reportResponseDone(requestId, transformErrorForSerialization(err));
366 });
367 }
369 > //#region --- token counting
370 >
371 > $cancelLanguageModelChatRequest(requestId: number): void {
372 this._pendingCancelCTS.get(requestId)?.cancel();
373 }
375 > $provideTokenLength(modelId: string, value: string, token: CancellationToken): Promise<number> {
376 const knownModel = this._localModels.get(modelId);
377 if (!knownModel) {
378 return Promise.resolve(0);
379 }
380 const data = this._languageModelProviders.get(knownModel.metadata.vendor);
381 if (!data) {
382 return Promise.resolve(0);
383 }
384 return Promise.resolve(data.provider.provideTokenCount(knownModel.info, value, token));
385 }
387 >
388 > //#region --- making request
389 >
390 > async getDefaultLanguageModel(extension: IExtensionDescription, forceResolveModels?: boolean): Promise<vscode.LanguageModelChat | undefined> {
391 let defaultModelId: string | undefined;
392
393 if (forceResolveModels) {
394 await this.selectLanguageModels(extension, {});
395 }
396
397 for (const [modelIdentifier, modelData] of this._localModels) {
398 if (modelData.metadata.isDefaultForLocation[ChatAgentLocation.Chat] && modelData.metadata.vendor === COPILOT_VENDOR_ID) {
399 defaultModelId = modelIdentifier;
400 break;
401 }
402 }
403 if (!defaultModelId && !forceResolveModels) {
404 // Maybe the default wasn't cached so we will try again with resolving the models too
405 return this.getDefaultLanguageModel(extension, true);
406 }
407 return this.getLanguageModelByIdentifier(extension, defaultModelId);
408 }
410 > async getLanguageModelByIdentifier(extension: IExtensionDescription, modelId: string | undefined): Promise<vscode.LanguageModelChat | undefined> {
411 if (!modelId) {
412 return undefined;
413 }
414
415 if (!this._localModels.has(modelId)) {
416 // model gone? is this an error on us? Try to resolve model again
417 const vendor = this.getVendorFromModelIdentifier(modelId);
418 if (!vendor) {
419 this._logService.warn(`[LanguageModelProxy](${extension.identifier.value}) Could not extract vendor from model identifier '${modelId}'.`);
420 return undefined;
421 }
422 this._logService.trace(`[LanguageModelProxy](${extension.identifier.value}) Could not find model '${modelId}' in local cache. Trying to resolve model again.`);
423 // Call proxy directly: routing through `selectLanguageModels` would recurse here for every identifier and blow up when the cache stays empty (provider in another ext host).
424 await this._proxy.$selectChatModels({ vendor, extension: extension.identifier });
425 if (!this._localModels.has(modelId)) {
426 this._logService.warn(`[LanguageModelProxy](${extension.identifier.value}) Could not find model '${modelId}' in local cache after re-resolving models.`);
427 return undefined;
428 }
429 }
430
431 return this._createLanguageModelChatApi(extension, modelId);
432 }
434 > private async _createLanguageModelChatApi(extension: IExtensionDescription, modelId: string): Promise<vscode.LanguageModelChat | undefined> {
435 const model = this._localModels.get(modelId);
436 if (!model) {
437 return undefined;
438 }
439
440 // make sure auth information is correct
441 if (this._isUsingAuth(extension.identifier, model.metadata)) {
442 await this._fakeAuthPopulate(model.metadata);
443 }
444
445 const that = this;
446 const apiObject: vscode.LanguageModelChat = {
447 id: model.info.id,
448 vendor: model.metadata.vendor,
449 family: model.info.family,
450 version: model.info.version,
451 name: model.info.name,
452 pricing: model.metadata.pricing,
453 inputCost: model.metadata.inputCost,
454 outputCost: model.metadata.outputCost,
455 cacheCost: model.metadata.cacheCost,
456 cacheWriteCost: model.metadata.cacheWriteCost,
457 longContextInputCost: model.metadata.longContextInputCost,
458 longContextOutputCost: model.metadata.longContextOutputCost,
459 longContextCacheCost: model.metadata.longContextCacheCost,
460 longContextCacheWriteCost: model.metadata.longContextCacheWriteCost,
461 priceCategory: model.metadata.priceCategory,
462 category: model.metadata.category,
463 capabilities: {
464 supportsImageToText: model.metadata.capabilities?.vision ?? false,
465 supportsToolCalling: !!model.metadata.capabilities?.toolCalling,
466 editToolsHint: model.metadata.capabilities?.editTools,
467 },
468 maxInputTokens: model.metadata.maxInputTokens,
469 countTokens(text, token) {
470 if (!that._localModels.has(modelId)) {
471 throw extHostTypes.LanguageModelError.NotFound(modelId);
472 }
473 return that._computeTokenLength(modelId, text, token ?? CancellationToken.None);
474 },
475 sendRequest(messages, options, token) {
476 if (!that._localModels.has(modelId)) {
477 throw extHostTypes.LanguageModelError.NotFound(modelId);
478 }
479 return that._sendChatRequest(extension, modelId, messages, options ?? {}, token ?? CancellationToken.None);
480 }
481 };
482
483 Object.freeze(apiObject);
484 return apiObject;
485 }
487 > async selectLanguageModels(extension: IExtensionDescription, selector: vscode.LanguageModelChatSelector) {
488
489 // this triggers extension activation
490 const models = await this._proxy.$selectChatModels({ ...selector, extension: extension.identifier });
491
492 // Skip the warn/retry path in `getLanguageModelByIdentifier`: identifiers are fresh, so a missing local entry means the provider lives in another ext host and re-resolving will not help.
493 const modelResults = await Promise.all(models.map(identifier => this._createLanguageModelChatApi(extension, identifier)));
494 return modelResults.filter((m): m is vscode.LanguageModelChat => !!m);
495 }
497 > private async _sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelChatMessage2[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) {
498
499 const internalMessages: IChatMessage[] = this._convertMessages(extension, messages);
500
501 const from = extension.identifier;
502 const metadata = this._localModels.get(languageModelId)?.metadata;
503
504 if (!metadata || !this._localModels.has(languageModelId)) {
505 throw extHostTypes.LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`);
506 }
507
508 if (this._isUsingAuth(from, metadata)) {
509 const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, false);
510
511 if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) {
512 throw extHostTypes.LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`);
513 }
514 }
515
516 const requestId = (Math.random() * 1e6) | 0;
517 const res = new LanguageModelResponse();
518 this._pendingRequest.set(requestId, { languageModelId, res });
519
520 const cts = new CancellationTokenSource(token);
521 this._pendingCancelCTS.set(requestId, cts);
522 cts.token.onCancellationRequested(() => {
523 this._proxy.$cancelLanguageModelChatRequest(requestId);
524 });
525
526 try {
527 await this._proxy.$tryStartChatRequest(from, languageModelId, requestId, new SerializableObjectWithBuffers(internalMessages), options, cts.token);
528
529 } catch (error) {
530 // error'ing here means that the request could NOT be started/made, e.g. wrong model, no access, etc, but
531 // later the response can fail as well. Those failures are communicated via the stream-object
532 this._pendingRequest.delete(requestId);
533 this._pendingCancelCTS.deleteAndDispose(requestId);
534 throw extHostTypes.LanguageModelError.tryDeserialize(error) ?? error;
535 }
536
537 return res.apiObject;
538 }
540 > private _convertMessages(extension: IExtensionDescription, messages: vscode.LanguageModelChatMessage2[]) {
541 const internalMessages: IChatMessage[] = [];
542 for (const message of messages) {
543 if (message.role as number === extHostTypes.LanguageModelChatMessageRole.System) {
544 checkProposedApiEnabled(extension, 'languageModelSystem');
545 }
546 internalMessages.push(typeConvert.LanguageModelChatMessage2.from(message));
547 }
548 return internalMessages;
549 }
551 > async $acceptResponsePart(requestId: number, chunk: SerializableObjectWithBuffers<IChatResponsePart | IChatResponsePart[]>): Promise<void> {
552 const data = this._pendingRequest.get(requestId);
553 if (data) {
554 data.res.handleResponsePart(chunk.value);
555 }
556 }
558 > $onChatModelsChange(): void {
559 this._onDidChangeProviders.fire();
560 }
562 > async $acceptResponseDone(requestId: number, error: SerializedError | undefined): Promise<void> {
563 const data = this._pendingRequest.get(requestId);
564 if (!data) {
565 return;
566 }
567 this._pendingRequest.delete(requestId);
568 this._pendingCancelCTS.deleteAndDispose(requestId);
569 if (error) {
570 // we error the stream because that's the only way to signal
571 // that the request has failed
572 data.res.reject(extHostTypes.LanguageModelError.tryDeserialize(error) ?? transformErrorFromSerialization(error));
573 } else {
574 data.res.resolve();
575 }
576 }
578 > // BIG HACK: Using AuthenticationProviders to check access to Language Models
579 > private async _getAuthAccess(from: IExtensionDescription, to: { identifier: ExtensionIdentifier; displayName: string }, justification: string | undefined, silent: boolean | undefined): Promise<boolean> {
580 // This needs to be done in both MainThread & ExtHost ChatProvider
581 const providerId = INTERNAL_AUTH_PROVIDER_PREFIX + to.identifier.value;
582 const session = await this._extHostAuthentication.getSession(from, providerId, [], { silent: true });
583
584 if (session) {
585 this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]);
586 return true;
587 }
588
589 if (silent) {
590 return false;
591 }
592
593 try {
594 const detail = justification
595 ? localize('chatAccessWithJustification', "Justification: {1}", to.displayName, justification)
596 : undefined;
597 await this._extHostAuthentication.getSession(from, providerId, [], { forceNewSession: { detail } });
598 this.$updateModelAccesslist([{ from: from.identifier, to: to.identifier, enabled: true }]);
599 return true;
600
601 } catch (err) {
602 // ignore
603 return false;
604 }
605 }
607 > private _isUsingAuth(from: ExtensionIdentifier, toMetadata: ILanguageModelChatMetadata): toMetadata is ILanguageModelChatMetadata & { auth: NonNullable<ILanguageModelChatMetadata['auth']> } {
608 // If the 'to' extension uses an auth check
609 return !!toMetadata.auth
610 // And we're asking from a different extension
611 && !ExtensionIdentifier.equals(toMetadata.extension, from);
612 }
614 > private async _fakeAuthPopulate(metadata: ILanguageModelChatMetadata): Promise<void> {
615
616 if (!metadata.auth) {
617 return;
618 }
619
620 for (const from of this._languageAccessInformationExtensions) {
621 try {
622 await this._getAuthAccess(from, { identifier: metadata.extension, displayName: '' }, undefined, true);
623 } catch (err) {
624 this._logService.error('Fake Auth request failed');
625 this._logService.error(err);
626 }
627 }
628 }
630 > private async _computeTokenLength(modelId: string, value: string | vscode.LanguageModelChatMessage2, token: vscode.CancellationToken): Promise<number> {
631
632 const data = this._localModels.get(modelId);
633 if (!data) {
634 throw extHostTypes.LanguageModelError.NotFound(`Language model '${modelId}' is unknown.`);
635 }
636 return this._languageModelProviders.get(data.metadata.vendor)?.provider.provideTokenCount(data.info, value, token) ?? 0;
637 // return this._proxy.$countTokens(languageModelId, (typeof value === 'string' ? value : typeConvert.LanguageModelChatMessage2.from(value)), token);
638 }
640 > $updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void {
641 const updated = new Array<{ from: ExtensionIdentifier; to: ExtensionIdentifier }>();
642 for (const { from, to, enabled } of data) {
643 const set = this._modelAccessList.get(from) ?? new ExtensionIdentifierSet();
644 const oldValue = set.has(to);
645 if (oldValue !== enabled) {
646 if (enabled) {
647 set.add(to);
648 } else {
649 set.delete(to);
650 }
651 this._modelAccessList.set(from, set);
652 const newItem = { from, to };
653 updated.push(newItem);
654 this._onDidChangeModelAccess.fire(newItem);
655 }
656 }
657 }
659 > private readonly _languageAccessInformationExtensions = new Set<Readonly<IExtensionDescription>>();
660 >
661 > createLanguageModelAccessInformation(from: Readonly<IExtensionDescription>): vscode.LanguageModelAccessInformation {
662
663 this._languageAccessInformationExtensions.add(from);
664
665 // const that = this;
666 const _onDidChangeAccess = Event.signal(Event.filter(this._onDidChangeModelAccess.event, e => ExtensionIdentifier.equals(e.from, from.identifier)));
667 const _onDidAddRemove = Event.signal(this._onDidChangeProviders.event);
668
669 return {
670 get onDidChange() {
671 return Event.any(_onDidChangeAccess, _onDidAddRemove);
672 },
673 canSendRequest(chat: vscode.LanguageModelChat): boolean | undefined {
674 return true;
675 // TODO @lramos15 - Fix
676
677 // let metadata: ILanguageModelChatMetadata | undefined;
678
679 // out: for (const [_, value] of that._allLanguageModelData) {
680 // for (const candidate of value.apiObjects.values()) {
681 // if (candidate === chat) {
682 // metadata = value.metadata;
683 // break out;
684 // }
685 // }
686 // }
687 // if (!metadata) {
688 // return undefined;
689 // }
690 // if (!that._isUsingAuth(from.identifier, metadata)) {
691 // return true;
692 // }
693
694 // const list = that._modelAccessList.get(from.identifier);
695 // if (!list) {
696 // return undefined;
697 // }
698 // return list.has(metadata.extension);
699 }
700 };
701 }
703 > fileIsIgnored(extension: IExtensionDescription, uri: vscode.Uri, token: vscode.CancellationToken = CancellationToken.None): Promise<boolean> {
704 checkProposedApiEnabled(extension, 'chatParticipantAdditions');
705
706 return this._proxy.$fileIsIgnored(uri, token);
707 }
709 > get isModelProxyAvailable(): boolean {
710 return !!this._languageModelProxyProvider;
711 }
713 > async getModelProxy(extension: IExtensionDescription): Promise<vscode.LanguageModelProxy> {
714 checkProposedApiEnabled(extension, 'languageModelProxy');
715
716 if (!this._languageModelProxyProvider) {
717 this._logService.trace('[LanguageModelProxy] No LanguageModelProxyProvider registered');
718 throw new Error('No language model proxy provider is registered.');
719 }
720
721 const requestingExtensionId = ExtensionIdentifier.toKey(extension.identifier);
722 try {
723 const result = await Promise.resolve(this._languageModelProxyProvider.provideModelProxy(requestingExtensionId, CancellationToken.None));
724 if (!result) {
725 this._logService.warn(`[LanguageModelProxy] Provider returned no proxy for ${requestingExtensionId}`);
726 throw new Error('Language model proxy is not available.');
727 }
728 return result;
729 } catch (err) {
730 this._logService.error(`[LanguageModelProxy] Provider failed to return proxy for ${requestingExtensionId}`, err);
731 throw err;
732 }
733 }
735 > async $isFileIgnored(handle: number, uri: UriComponents, token: CancellationToken): Promise<boolean> {
736 const provider = this._ignoredFileProviders.get(handle);
737 if (!provider) {
738 throw new Error('Unknown LanguageModelIgnoredFileProvider');
739 }
740
741 return (await provider.provideFileIgnored(URI.revive(uri), token)) ?? false;
742 }
744 > registerIgnoredFileProvider(extension: IExtensionDescription, provider: vscode.LanguageModelIgnoredFileProvider): vscode.Disposable {
745 checkProposedApiEnabled(extension, 'chatParticipantPrivate');
746
747 const handle = ExtHostLanguageModels._idPool++;
748 this._proxy.$registerFileIgnoreProvider(handle);
749 this._ignoredFileProviders.set(handle, provider);
750 return toDisposable(() => {
751 this._proxy.$unregisterFileIgnoreProvider(handle);
752 this._ignoredFileProviders.delete(handle);
753 });
754 }
756 > registerLanguageModelProxyProvider(extension: IExtensionDescription, provider: vscode.LanguageModelProxyProvider): vscode.Disposable {
757 checkProposedApiEnabled(extension, 'chatParticipantPrivate');
758
759 this._languageModelProxyProvider = provider;
760 this._onDidChangeModelProxyAvailability.fire();
761 return toDisposable(() => {
762 if (this._languageModelProxyProvider === provider) {
763 this._languageModelProxyProvider = undefined;
764 this._onDidChangeModelProxyAvailability.fire();
765 }
766 });
767 }