use a composted, semi-random identifier for language model and leave metadata#id for model selection only

This commit is contained in:
Johannes
2024-05-08 16:48:21 +02:00
parent 4baa94788e
commit 69a80f9f1d
5 changed files with 35 additions and 44 deletions
@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { coalesce } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@@ -37,8 +36,7 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape {
@IExtensionService private readonly _extensionService: IExtensionService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatProvider);
this._proxy.$acceptChatModelMetadata({ added: coalesce(_chatProviderService.getLanguageModelIds().map(id => _chatProviderService.lookupLanguageModel(id))) });
this._proxy.$acceptChatModelMetadata({ added: _chatProviderService.getLanguageModelIds().map(id => ({ identifier: id, metadata: _chatProviderService.lookupLanguageModel(id)! })) });
this._store.add(_chatProviderService.onDidChangeLanguageModels(this._proxy.$acceptChatModelMetadata, this._proxy));
}
@@ -88,18 +86,6 @@ export class MainThreadLanguageModels implements MainThreadLanguageModelsShape {
async $prepareChatAccess(extension: ExtensionIdentifier, providerId: string, justification?: string): Promise<ILanguageModelChatMetadata | undefined> {
const activate = this._extensionService.activateByEvent(`onLanguageModelAccess:${providerId}`);
const metadata = this._chatProviderService.lookupLanguageModel(providerId);
if (metadata) {
return metadata;
}
await Promise.race([
activate,
Event.toPromise(Event.filter(this._chatProviderService.onDidChangeLanguageModels, e => Boolean(e.added?.some(value => value.identifier === providerId))))
]);
return this._chatProviderService.lookupLanguageModel(providerId);
}
@@ -1430,7 +1430,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const lm: typeof vscode.lm = {
selectChatModels: (selector) => {
checkProposedApiEnabled(extension, 'languageModels');
return extHostLanguageModels.selectLanguageModels(extension, selector);
return extHostLanguageModels.selectLanguageModels(extension, selector ?? {});
},
onDidChangeChatModels: (listener, thisArgs?, disposables?) => {
checkProposedApiEnabled(extension, 'languageModels');
@@ -54,7 +54,7 @@ import { ChatAgentLocation, IChatAgentMetadata, IChatAgentRequest, IChatAgentRes
import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolverProgress } from 'vs/workbench/contrib/chat/common/chatVariables';
import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata, ILanguageModelChatSelector } from 'vs/workbench/contrib/chat/common/languageModels';
import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata, ILanguageModelChatSelector, ILanguageModelsChangeEvent } from 'vs/workbench/contrib/chat/common/languageModels';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from 'vs/workbench/contrib/debug/common/debug';
import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
@@ -1211,7 +1211,7 @@ export interface MainThreadLanguageModelsShape extends IDisposable {
}
export interface ExtHostLanguageModelsShape {
$acceptChatModelMetadata(data: { added?: ILanguageModelChatMetadata[]; removed?: string[] }): void;
$acceptChatModelMetadata(data: ILanguageModelsChangeEvent): void;
$updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void;
$provideLanguageModelResponse(handle: number, requestId: number, from: ExtensionIdentifier, messages: IChatMessage[], options: { [name: string]: any }, token: CancellationToken): Promise<any>;
$handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise<void>;
@@ -152,9 +152,9 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
accountLabel: typeof metadata.auth === 'object' ? metadata.auth.label : undefined
};
}
this._proxy.$registerLanguageModelProvider(handle, identifier, {
this._proxy.$registerLanguageModelProvider(handle, `${ExtensionIdentifier.toKey(extension.identifier)}/${handle}/${identifier}`, {
extension: extension.identifier,
identifier: identifier,
id: identifier,
vendor: metadata.vendor ?? ExtensionIdentifier.toKey(extension.identifier),
name: metadata.name ?? '',
family: metadata.family ?? '',
@@ -211,20 +211,16 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
//#region --- making request
$acceptChatModelMetadata(data: { added?: ILanguageModelChatMetadata[] | undefined; removed?: string[] | undefined }): void {
const added: string[] = [];
const removed: string[] = [];
$acceptChatModelMetadata(data: { added?: { identifier: string; metadata: ILanguageModelChatMetadata }[] | undefined; removed?: string[] | undefined }): void {
if (data.added) {
for (const metadata of data.added) {
this._allLanguageModelData.set(metadata.identifier, { metadata, apiObjects: new ExtensionIdentifierMap() });
added.push(metadata.identifier);
for (const { identifier, metadata } of data.added) {
this._allLanguageModelData.set(identifier, { metadata, apiObjects: new ExtensionIdentifierMap() });
}
}
if (data.removed) {
for (const id of data.removed) {
// clean up
this._allLanguageModelData.delete(id);
removed.push(id);
// cancel pending requests for this model
for (const [key, value] of this._pendingRequest) {
@@ -236,10 +232,10 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
}
}
this._onDidChangeProviders.fire(undefined);
// TODO@jrieken@TylerLeonhardt - this is a temporary hack to populate the auth providers
data.added?.forEach(this._fakeAuthPopulate, this);
data.added?.forEach(added => this._fakeAuthPopulate(added.metadata));
this._onDidChangeProviders.fire(undefined);
}
async selectLanguageModels(extension: IExtensionDescription, selector: vscode.LanguageModelChatSelector) {
@@ -433,7 +429,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape {
return local.provider.provideTokenCount(value, token);
}
return this._proxy.$countTokens(data.metadata.identifier, (typeof value === 'string' ? value : typeConvert.LanguageModelChatMessage.from(value)), token);
return this._proxy.$countTokens(languageModelId, (typeof value === 'string' ? value : typeConvert.LanguageModelChatMessage.from(value)), token);
}
$updateModelAccesslist(data: { from: ExtensionIdentifier; to: ExtensionIdentifier; enabled: boolean }[]): void {
@@ -34,8 +34,9 @@ export interface IChatResponseFragment {
export interface ILanguageModelChatMetadata {
readonly extension: ExtensionIdentifier;
readonly name: string;
readonly identifier: string;
readonly id: string;
readonly vendor: string;
readonly version: string;
readonly family: string;
@@ -66,11 +67,19 @@ export interface ILanguageModelChatSelector {
export const ILanguageModelsService = createDecorator<ILanguageModelsService>('ILanguageModelsService');
export interface ILanguageModelsChangeEvent {
added?: {
identifier: string;
metadata: ILanguageModelChatMetadata;
}[];
removed?: string[];
}
export interface ILanguageModelsService {
readonly _serviceBrand: undefined;
onDidChangeLanguageModels: Event<{ added?: ILanguageModelChatMetadata[]; removed?: string[] }>;
onDidChangeLanguageModels: Event<ILanguageModelsChangeEvent>;
getLanguageModelIds(): string[];
@@ -113,7 +122,7 @@ export const languageModelExtensionPoint = ExtensionsRegistry.registerExtensionP
},
activationEventsGenerator: (contribs: IUserFriendlyLanguageModel[], result: { push(item: string): void }) => {
for (const contrib of contribs) {
result.push(`onLanguageModel:${contrib.vendor}`);
result.push(`onLanguageModelChat:${contrib.vendor}`);
}
}
});
@@ -125,8 +134,8 @@ export class LanguageModelsService implements ILanguageModelsService {
private readonly _providers = new Map<string, ILanguageModelChat>();
private readonly _vendors = new Set<string>();
private readonly _onDidChangeProviders = new Emitter<{ added?: ILanguageModelChatMetadata[]; removed?: string[] }>();
readonly onDidChangeLanguageModels: Event<{ added?: ILanguageModelChatMetadata[]; removed?: string[] }> = this._onDidChangeProviders.event;
private readonly _onDidChangeProviders = new Emitter<ILanguageModelsChangeEvent>();
readonly onDidChangeLanguageModels: Event<ILanguageModelsChangeEvent> = this._onDidChangeProviders.event;
constructor(
@IExtensionService private readonly _extensionService: IExtensionService,
@@ -161,10 +170,10 @@ export class LanguageModelsService implements ILanguageModelsService {
}
const removed: string[] = [];
for (const [key, value] of this._providers) {
for (const [identifier, value] of this._providers) {
if (!this._vendors.has(value.metadata.vendor)) {
this._providers.delete(key);
removed.push(key);
this._providers.delete(identifier);
removed.push(identifier);
}
}
if (removed.length > 0) {
@@ -199,16 +208,16 @@ export class LanguageModelsService implements ILanguageModelsService {
const result: string[] = [];
for (const model of this._providers.values()) {
for (const [identifier, model] of this._providers) {
if (selector.vendor !== undefined && model.metadata.vendor === selector.vendor
|| selector.family !== undefined && model.metadata.family === selector.family
|| selector.version !== undefined && model.metadata.version === selector.version
|| selector.identifier !== undefined && model.metadata.identifier === selector.identifier
|| selector.identifier !== undefined && model.metadata.id === selector.identifier
|| selector.extension !== undefined && model.metadata.targetExtensions?.some(candidate => ExtensionIdentifier.equals(candidate, selector.extension))
) {
// true selection
result.push(model.metadata.identifier);
result.push(identifier);
} else if (!selector || (
selector.vendor === undefined
@@ -217,7 +226,7 @@ export class LanguageModelsService implements ILanguageModelsService {
&& selector.identifier === undefined)
) {
// no selection
result.push(model.metadata.identifier);
result.push(identifier);
}
}
@@ -234,7 +243,7 @@ export class LanguageModelsService implements ILanguageModelsService {
throw new Error(`Chat response provider with identifier ${identifier} is already registered.`);
}
this._providers.set(identifier, provider);
this._onDidChangeProviders.fire({ added: [provider.metadata] });
this._onDidChangeProviders.fire({ added: [{ identifier, metadata: provider.metadata }] });
return toDisposable(() => {
if (this._providers.delete(identifier)) {
this._onDidChangeProviders.fire({ removed: [identifier] });