From c4c7ef5c48a797640f277d0bcf67010cf0e05936 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 3 May 2024 15:42:29 +0200 Subject: [PATCH 1/7] API-todos for language model namespace --- .../vscode.proposed.languageModels.d.ts | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 72db29500b9..b2d11b9ac21 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -12,14 +12,36 @@ declare module 'vscode' { */ export interface LanguageModelChatResponse { + //TODO@API give this some structure + // https://github.com/openai/openai-openapi/blob/master/openapi.yaml#L7700 + // https://github.com/ollama/ollama/blob/main/docs/api.md#response-7 + // https://docs.anthropic.com/claude/reference/messages_post + /** * An async iterable that is a stream of text chunks forming the overall response. * - * *Note* that this stream will error when during data receiving an error occurrs. + * *Note* that this stream will error when during data receiving an error occurs. Consumers of + * the stream should handle the errors accordingly. + * + * @example + * ```ts + * try { + * // consume stream + * for await (const chunk of response.stream) { + * console.log(chunk); + * } + * + * } catch(e) { + * // stream ended with an error + * console.error(e); + * } + * ``` */ stream: AsyncIterable; } + // TODO@API have just LanguageModelChatMessage & LanguageModelChatMessageRole + /** * A language model message that represents a system message. * @@ -115,6 +137,7 @@ declare module 'vscode' { /** * The version of the language model. */ + // TODO@API drop this for now? readonly version: string; /** @@ -123,6 +146,8 @@ declare module 'vscode' { * * @see {@link lm.sendChatRequest} */ + // TODO@API is this optional? + // TODO@API rename: contextLength? readonly tokens: number; } From d5b3c2f1c74ad3dd54f9f37a3a6f0e07cc73604f Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 08:38:55 +0200 Subject: [PATCH 2/7] notes --- .../vscode.proposed.languageModels.d.ts | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index b2d11b9ac21..bd2977ba5ee 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -13,7 +13,7 @@ declare module 'vscode' { export interface LanguageModelChatResponse { //TODO@API give this some structure - // https://github.com/openai/openai-openapi/blob/master/openapi.yaml#L7700 + // https://github.com/openai/openai-openapi/blob/master/openapi.yaml#L7700, https://platform.openai.com/docs/guides/text-generation/chat-completions-api // https://github.com/ollama/ollama/blob/main/docs/api.md#response-7 // https://docs.anthropic.com/claude/reference/messages_post @@ -40,7 +40,48 @@ declare module 'vscode' { stream: AsyncIterable; } + export enum LanguageModelChatMessageRole { + User, + Assistant, + // System see https://github.com/anthropics/anthropic-sdk-typescript/blob/c2da9604646ff103fbdbca016a9a9d49b03b387b/src/resources/messages.ts#L384 + } + + export class LanguageModelChatMessage2 { + /** + * The role of this message. + */ + role: LanguageModelChatMessageRole; + + /** + * The content of this message. + */ + content: string; + + /** + * The optional name of a user for this message. + */ + name: string | undefined; + + /** + * Create a new user message. + * + * @param content The content of the message. + * @param name The optional name of a user for the message. + */ + constructor(role: LanguageModelChatMessageRole, content: string, name?: string); + } + + export interface LanguageModelChatResponse2 { + + message: { + role: LanguageModelChatMessageRole; + content: AsyncIterable; + }; + } + // TODO@API have just LanguageModelChatMessage & LanguageModelChatMessageRole + // TODO@API don't have SystemChatMessage because it encourages to mix system and user messages + // but AFAIK system message should/must come first (https://github.com/anthropics/anthropic-sdk-typescript/blob/c2da9604646ff103fbdbca016a9a9d49b03b387b/src/resources/messages.ts#L384) /** * A language model message that represents a system message. @@ -209,6 +250,8 @@ declare module 'vscode' { */ export interface LanguageModelChatRequestOptions { + // TODO@API add short name for the feature using the language model + /** * A human-readable message that explains why access to a language model is needed and what feature is enabled by it. */ @@ -220,6 +263,8 @@ declare module 'vscode' { // TODO@API Revisit this, how do you do the first request? silent?: boolean; + // TODO@API add system messages here? + /** * A set of options that control the behavior of the language model. These options are specific to the language model * and need to be lookup in the respective documentation. From 22efbdf96cca32d1bad2fac39400a750c20027eb Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 11:00:17 +0200 Subject: [PATCH 3/7] * return `LanguageModelChatMessageRole` and the simple chat message `LanguageModelChatMessage2` because all types became same, same * deprecate the other types and prepare deletion * move System-role into its own proposal because of API and RAI concerns * more API notes --- .../workbench/api/common/extHost.api.impl.ts | 13 ++-- .../api/common/extHostLanguageModels.ts | 43 ++++++++--- .../api/common/extHostTypeConverters.ts | 23 ++++++ src/vs/workbench/api/common/extHostTypes.ts | 19 +++++ .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.chatProvider.d.ts | 2 + .../vscode.proposed.languageModelSystem.d.ts | 17 +++++ .../vscode.proposed.languageModels.d.ts | 71 ++++++++++--------- 8 files changed, 140 insertions(+), 49 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.languageModelSystem.d.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3caf8e8556a..23ef3d71187 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1438,7 +1438,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'languageModels'); return extHostLanguageModels.onDidChangeProviders(listener, thisArgs, disposables); }, - sendChatRequest(languageModel: string, messages: vscode.LanguageModelChatMessage[], options: vscode.LanguageModelChatRequestOptions, token: vscode.CancellationToken) { + sendChatRequest(languageModel: string, messages: (vscode.LanguageModelChatMessage | vscode.LanguageModelChatMessage2)[], options: vscode.LanguageModelChatRequestOptions, token: vscode.CancellationToken) { checkProposedApiEnabled(extension, 'languageModels'); return extHostLanguageModels.sendChatRequest(extension, languageModel, messages, options, token); }, @@ -1707,12 +1707,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatRequestTurn: extHostTypes.ChatRequestTurn, ChatResponseTurn: extHostTypes.ChatResponseTurn, ChatLocation: extHostTypes.ChatLocation, - LanguageModelChatSystemMessage: extHostTypes.LanguageModelChatSystemMessage, - LanguageModelChatUserMessage: extHostTypes.LanguageModelChatUserMessage, - LanguageModelChatAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage, - LanguageModelSystemMessage: extHostTypes.LanguageModelChatSystemMessage, // TODO@jrieken REMOVE - LanguageModelUserMessage: extHostTypes.LanguageModelChatUserMessage, // TODO@jrieken REMOVE - LanguageModelAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage, // TODO@jrieken REMOVE + LanguageModelChatMessageRole: extHostTypes.LanguageModelChatMessageRole, + LanguageModelChatMessage2: extHostTypes.LanguageModelChatMessage2, + LanguageModelChatSystemMessage: extHostTypes.LanguageModelChatSystemMessage,// TODO@jrieken REMOVE + LanguageModelChatUserMessage: extHostTypes.LanguageModelChatUserMessage,// TODO@jrieken REMOVE + LanguageModelChatAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage,// TODO@jrieken REMOVE LanguageModelError: extHostTypes.LanguageModelError, NewSymbolName: extHostTypes.NewSymbolName, NewSymbolNameTag: extHostTypes.NewSymbolNameTag, diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index d0202a84451..4a707110621 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ExtHostLanguageModelsShape, MainContext, MainThreadLanguageModelsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; -import { LanguageModelError } from 'vs/workbench/api/common/extHostTypes'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { Progress } from 'vs/platform/progress/common/progress'; import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata } from 'vs/workbench/contrib/chat/common/languageModels'; @@ -22,6 +22,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; import { ILogService } from 'vs/platform/log/common/log'; import { Iterable } from 'vs/base/common/iterator'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; export interface IExtHostLanguageModels extends ExtHostLanguageModels { } @@ -185,7 +186,11 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { this._proxy.$handleProgressChunk(requestId, { index: fragment.index, part: fragment.part }); }); - return data.provider.provideLanguageModelResponse2(messages.map(typeConvert.LanguageModelMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); + if (data.provider.provideLanguageModelResponse) { + return data.provider.provideLanguageModelResponse(messages.map(typeConvert.LanguageModelChatMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); + } else { + return data.provider.provideLanguageModelResponse2(messages.map(typeConvert.LanguageModelMessage.to), options, ExtensionIdentifier.toKey(from), progress, token); + } } @@ -259,25 +264,27 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { } } - async sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: vscode.LanguageModelChatMessage[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) { + async sendChatRequest(extension: IExtensionDescription, languageModelId: string, messages: (vscode.LanguageModelChatMessage | vscode.LanguageModelChatMessage2)[], options: vscode.LanguageModelChatRequestOptions, token: CancellationToken) { + + const internalMessages: IChatMessage[] = this._convertMessages(extension, messages); const from = extension.identifier; const metadata = await this._proxy.$prepareChatAccess(from, languageModelId, options.justification); if (!metadata || !this._allLanguageModelData.has(languageModelId)) { - throw LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`); + throw extHostTypes.LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`); } if (this._isUsingAuth(from, metadata)) { const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, options.silent); if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) { - throw LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); + throw extHostTypes.LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); } } const requestId = (Math.random() * 1e6) | 0; - const requestPromise = this._proxy.$fetchResponse(from, languageModelId, requestId, messages.map(typeConvert.LanguageModelMessage.from), options.modelOptions ?? {}, token); + const requestPromise = this._proxy.$fetchResponse(from, languageModelId, requestId, internalMessages, options.modelOptions ?? {}, token); const barrier = new Barrier(); @@ -303,11 +310,11 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { await barrier.wait(); if (error) { - if (error.name === LanguageModelError.name) { + if (error.name === extHostTypes.LanguageModelError.name) { throw error; } - throw new LanguageModelError( + throw new extHostTypes.LanguageModelError( `Language model '${languageModelId}' errored, check cause for more details`, 'Unknown', error @@ -317,6 +324,24 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { return res.apiObject; } + private _convertMessages(extension: IExtensionDescription, messages: (vscode.LanguageModelChatMessage2 | vscode.LanguageModelChatMessage)[]) { + const internalMessages: IChatMessage[] = []; + for (const message of messages) { + if (message instanceof extHostTypes.LanguageModelChatMessage2) { + if (message.role as number === extHostTypes.LanguageModelChatMessageRole.System) { + checkProposedApiEnabled(extension, 'languageModelSystem'); + } + internalMessages.push(typeConvert.LanguageModelChatMessage.from(message)); + } else { + if (message instanceof extHostTypes.LanguageModelChatSystemMessage) { + checkProposedApiEnabled(extension, 'languageModelSystem'); + } + internalMessages.push(typeConvert.LanguageModelMessage.from(message)); + } + } + return internalMessages; + } + async $handleResponseFragment(requestId: number, chunk: IChatResponseFragment): Promise { const data = this._pendingRequest.get(requestId);//.report(chunk); if (data) { @@ -376,7 +401,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { const data = this._allLanguageModelData.get(languageModelId); if (!data) { - throw LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`); + throw extHostTypes.LanguageModelError.NotFound(`Language model '${languageModelId}' is unknown.`); } const local = Iterable.find(this._languageModels.values(), candidate => candidate.languageModelId === languageModelId); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d163386bf00..f9571a57858 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2239,6 +2239,29 @@ export namespace ChatFollowup { } } + +export namespace LanguageModelChatMessage { + + export function to(message: chatProvider.IChatMessage): vscode.LanguageModelChatMessage2 { + switch (message.role) { + case chatProvider.ChatMessageRole.System: return new types.LanguageModelChatMessage2(types.LanguageModelChatMessageRole.System, message.content); + case chatProvider.ChatMessageRole.User: return new types.LanguageModelChatMessage2(types.LanguageModelChatMessageRole.User, message.content); + case chatProvider.ChatMessageRole.Assistant: return new types.LanguageModelChatMessage2(types.LanguageModelChatMessageRole.Assistant, message.content); + } + } + + export function from(message: vscode.LanguageModelChatMessage2): chatProvider.IChatMessage { + switch (message.role as types.LanguageModelChatMessageRole) { + case types.LanguageModelChatMessageRole.System: return { role: chatProvider.ChatMessageRole.System, content: message.content }; + case types.LanguageModelChatMessageRole.User: return { role: chatProvider.ChatMessageRole.User, content: message.content }; + case types.LanguageModelChatMessageRole.Assistant: return { role: chatProvider.ChatMessageRole.Assistant, content: message.content }; + } + } +} + +/** + * @deprecated + */ export namespace LanguageModelMessage { export function to(message: chatProvider.IChatMessage): vscode.LanguageModelChatMessage { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 9a85e2f08e8..c52e1c26cc2 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4424,6 +4424,25 @@ export enum ChatLocation { Editor = 4, } +export enum LanguageModelChatMessageRole { + User = 1, + Assistant = 2, + System = 3 +} + +export class LanguageModelChatMessage2 implements vscode.LanguageModelChatMessage2 { + + role: vscode.LanguageModelChatMessageRole; + content: string; + name: string | undefined; + + constructor(role: vscode.LanguageModelChatMessageRole, content: string, name?: string) { + this.role = role; + this.content = content; + this.name = name; + } +} + export class LanguageModelChatSystemMessage { content: string; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 402560ad1bd..c8c0a5d0e74 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -74,6 +74,7 @@ export const allApiProposals = Object.freeze({ interactive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactive.d.ts', interactiveWindow: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.interactiveWindow.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', + languageModelSystem: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', languageModels: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModels.d.ts', languageStatusText: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatusText.d.ts', mappedEditsProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', diff --git a/src/vscode-dts/vscode.proposed.chatProvider.d.ts b/src/vscode-dts/vscode.proposed.chatProvider.d.ts index 504027744c4..6651c4ff859 100644 --- a/src/vscode-dts/vscode.proposed.chatProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatProvider.d.ts @@ -19,6 +19,8 @@ declare module 'vscode' { onDidReceiveLanguageModelResponse2?: Event<{ readonly extensionId: string; readonly participant?: string; readonly tokenCount?: number }>; + provideLanguageModelResponse?(messages: LanguageModelChatMessage2[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; + provideLanguageModelResponse2(messages: LanguageModelChatMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress, token: CancellationToken): Thenable; provideTokenCount(text: string | LanguageModelChatMessage, token: CancellationToken): Thenable; diff --git a/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts b/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts new file mode 100644 index 00000000000..54e1da27a5b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/206265 + + // TODO@API don't have this dedicated type but as property, e.g anthropic doesn't have a system-role, see + // https://github.com/anthropics/anthropic-sdk-typescript/blob/c2da9604646ff103fbdbca016a9a9d49b03b387b/src/resources/messages.ts#L384 + // So, we could have `LanguageModelChatRequestOptions#system` which would be more limiting but also more natural? + + export enum LanguageModelChatMessageRole { + System = 3 + } +} diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index bd2977ba5ee..db75bbeb4ea 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -5,6 +5,8 @@ declare module 'vscode' { + // https://github.com/microsoft/vscode/issues/206265 + /** * Represents a language model response. * @@ -12,11 +14,6 @@ declare module 'vscode' { */ export interface LanguageModelChatResponse { - //TODO@API give this some structure - // https://github.com/openai/openai-openapi/blob/master/openapi.yaml#L7700, https://platform.openai.com/docs/guides/text-generation/chat-completions-api - // https://github.com/ollama/ollama/blob/main/docs/api.md#response-7 - // https://docs.anthropic.com/claude/reference/messages_post - /** * An async iterable that is a stream of text chunks forming the overall response. * @@ -40,12 +37,35 @@ declare module 'vscode' { stream: AsyncIterable; } - export enum LanguageModelChatMessageRole { - User, - Assistant, - // System see https://github.com/anthropics/anthropic-sdk-typescript/blob/c2da9604646ff103fbdbca016a9a9d49b03b387b/src/resources/messages.ts#L384 + //TODO@API give this some structure + // https://github.com/openai/openai-openapi/blob/master/openapi.yaml#L7700, https://platform.openai.com/docs/guides/text-generation/chat-completions-api + // https://github.com/ollama/ollama/blob/main/docs/api.md#response-7 + // https://docs.anthropic.com/claude/reference/messages_post + export interface LanguageModelChatResponse2 { + + message: { + role: LanguageModelChatMessageRole; + content: AsyncIterable; + }; } + /** + * Represents the role of a chat message. This is either the user or the assistant/model. + */ + export enum LanguageModelChatMessageRole { + /** + * The user role, e.g the human interacting with a language model. + */ + User = 1, + + /** + * The assistant role, e.g. the language model generating responses. + */ + // TODO@API name: model? + Assistant = 2 + } + + // TODO@API name: LanguageModelChatMessage once the deprecated stuff is removed export class LanguageModelChatMessage2 { /** * The role of this message. @@ -71,26 +91,9 @@ declare module 'vscode' { constructor(role: LanguageModelChatMessageRole, content: string, name?: string); } - export interface LanguageModelChatResponse2 { - - message: { - role: LanguageModelChatMessageRole; - content: AsyncIterable; - }; - } - - // TODO@API have just LanguageModelChatMessage & LanguageModelChatMessageRole - // TODO@API don't have SystemChatMessage because it encourages to mix system and user messages - // but AFAIK system message should/must come first (https://github.com/anthropics/anthropic-sdk-typescript/blob/c2da9604646ff103fbdbca016a9a9d49b03b387b/src/resources/messages.ts#L384) /** - * A language model message that represents a system message. - * - * System messages provide instructions to the language model that define the context in - * which user messages are interpreted. - * - * *Note* that a language model may choose to add additional system messages to the ones - * provided by extensions. + * @deprecated */ export class LanguageModelChatSystemMessage { @@ -108,7 +111,7 @@ declare module 'vscode' { } /** - * A language model message that represents a user message. + * @deprecated */ export class LanguageModelChatUserMessage { @@ -132,8 +135,7 @@ declare module 'vscode' { } /** - * A language model message that represents an assistant message, usually in response to a user message - * or as a sample response/reply-pair. + * @deprecated */ export class LanguageModelChatAssistantMessage { @@ -158,6 +160,7 @@ declare module 'vscode' { /** * Different types of language model messages. + * @deprecated */ export type LanguageModelChatMessage = LanguageModelChatSystemMessage | LanguageModelChatUserMessage | LanguageModelChatAssistantMessage; @@ -263,7 +266,6 @@ declare module 'vscode' { // TODO@API Revisit this, how do you do the first request? silent?: boolean; - // TODO@API add system messages here? /** * A set of options that control the behavior of the language model. These options are specific to the language model @@ -317,7 +319,7 @@ declare module 'vscode' { * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when the request couldn't be made. */ - export function sendChatRequest(languageModel: string, messages: LanguageModelChatMessage[], options: LanguageModelChatRequestOptions, token: CancellationToken): Thenable; + export function sendChatRequest(languageModel: string, messages: (LanguageModelChatMessage | LanguageModelChatMessage2)[], options: LanguageModelChatRequestOptions, token: CancellationToken): Thenable; /** * Uses the language model specific tokenzier and computes the length in token of a given message. @@ -329,7 +331,10 @@ declare module 'vscode' { * @param token Optional cancellation token. * @returns A thenable that resolves to the length of the message in tokens. */ - export function computeTokenLength(languageModel: string, text: string | LanguageModelChatMessage, token?: CancellationToken): Thenable; + // TODO@API optional? + // ollama has nothing + // anthropic suggests to count after the fact https://github.com/anthropics/anthropic-tokenizer-typescript?tab=readme-ov-file#anthropic-typescript-tokenizer + export function computeTokenLength(languageModel: string, text: string | LanguageModelChatMessage | LanguageModelChatMessage2, token?: CancellationToken): Thenable; } /** From fff5d2408b288504410354232bbc21f0dbda642c Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 11:16:29 +0200 Subject: [PATCH 4/7] remove `LanguageModelChatRequestOptions.silent` because there is `LanguageModelAccessInformation#canSendRequest` --- .../api/common/extHostLanguageModels.ts | 2 +- .../vscode.proposed.languageModels.d.ts | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index 4a707110621..89534da4033 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -276,7 +276,7 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { } if (this._isUsingAuth(from, metadata)) { - const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, options.silent); + const success = await this._getAuthAccess(extension, { identifier: metadata.extension, displayName: metadata.auth.providerLabel }, options.justification, false); if (!success || !this._modelAccessList.get(from)?.has(metadata.extension)) { throw extHostTypes.LanguageModelError.NoPermissions(`Language model '${languageModelId}' cannot be used by '${from.value}'.`); diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index db75bbeb4ea..8e56ee576f5 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -260,13 +260,6 @@ declare module 'vscode' { */ justification?: string; - /** - * Do not show the consent UI if the user has not yet granted access to the language model but fail the request instead. - */ - // TODO@API Revisit this, how do you do the first request? - silent?: boolean; - - /** * A set of options that control the behavior of the language model. These options are specific to the language model * and need to be lookup in the respective documentation. @@ -300,18 +293,22 @@ declare module 'vscode' { /** * Make a chat request using a language model. * - * - *Note 1:* language model use may be subject to access restrictions and user consent. + * - *Note 1:* language model use may be subject to access restrictions and user consent. Calling this function + * for the first time (for a extension) will show a consent dialog to the user and because of that this function + * must _only be called in response to a user action!_ Extension can use {@link LanguageModelAccessInformation.canSendRequest} + * to check if they have the necessary permissions to make a request. * * - *Note 2:* language models are contributed by other extensions and as they evolve and change, * the set of available language models may change over time. Therefore it is strongly recommend to check - * {@link languageModels} for aviailable values and handle missing language models gracefully. + * {@link languageModels} for available values and handle missing language models gracefully. * * This function will return a rejected promise if making a request to the language model is not * possible. Reasons for this can be: * * - user consent not given, see {@link LanguageModelError.NoPermissions `NoPermissions`} * - model does not exist, see {@link LanguageModelError.NotFound `NotFound`} - * - quota limits exceeded, see {@link LanguageModelError.cause `LanguageModelError.cause`} + * - quota limits exceeded, see {@link LanguageModelError.Blocked `Blocked`} + * - other issues in which case extension must check {@link LanguageModelError.cause `LanguageModelError.cause`} * * @param languageModel A language model identifier. * @param messages An array of message instances. From 1ca4972d82badaed01c0bfa14c68943176fde9fe Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 11:21:00 +0200 Subject: [PATCH 5/7] make token and options optional when calling `sendChatRequest` --- src/vs/workbench/api/common/extHost.api.impl.ts | 4 +++- src/vscode-dts/vscode.proposed.languageModels.d.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 23ef3d71187..d3c6638851a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1438,8 +1438,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'languageModels'); return extHostLanguageModels.onDidChangeProviders(listener, thisArgs, disposables); }, - sendChatRequest(languageModel: string, messages: (vscode.LanguageModelChatMessage | vscode.LanguageModelChatMessage2)[], options: vscode.LanguageModelChatRequestOptions, token: vscode.CancellationToken) { + sendChatRequest(languageModel: string, messages: (vscode.LanguageModelChatMessage | vscode.LanguageModelChatMessage2)[], options?: vscode.LanguageModelChatRequestOptions, token?: vscode.CancellationToken) { checkProposedApiEnabled(extension, 'languageModels'); + token ??= CancellationToken.None; + options ??= {}; return extHostLanguageModels.sendChatRequest(extension, languageModel, messages, options, token); }, computeTokenLength(languageModel: string, text: string | vscode.LanguageModelChatMessage, token?: vscode.CancellationToken) { diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 8e56ee576f5..a54e68a5106 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -316,7 +316,7 @@ declare module 'vscode' { * @param token A cancellation token which controls the request. See {@link CancellationTokenSource} for how to create one. * @returns A thenable that resolves to a {@link LanguageModelChatResponse}. The promise will reject when the request couldn't be made. */ - export function sendChatRequest(languageModel: string, messages: (LanguageModelChatMessage | LanguageModelChatMessage2)[], options: LanguageModelChatRequestOptions, token: CancellationToken): Thenable; + export function sendChatRequest(languageModel: string, messages: (LanguageModelChatMessage | LanguageModelChatMessage2)[], options?: LanguageModelChatRequestOptions, token?: CancellationToken): Thenable; /** * Uses the language model specific tokenzier and computes the length in token of a given message. From 0cfd5a3baf5f3b6d323933a440bcafbf728a65d6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 15:49:00 +0200 Subject: [PATCH 6/7] todo updates --- src/vscode-dts/vscode.proposed.languageModels.d.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index a54e68a5106..34299a308c0 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -44,9 +44,11 @@ declare module 'vscode' { export interface LanguageModelChatResponse2 { message: { - role: LanguageModelChatMessageRole; + role: LanguageModelChatMessageRole.Assistant; content: AsyncIterable; }; + + } /** @@ -61,7 +63,6 @@ declare module 'vscode' { /** * The assistant role, e.g. the language model generating responses. */ - // TODO@API name: model? Assistant = 2 } @@ -253,8 +254,6 @@ declare module 'vscode' { */ export interface LanguageModelChatRequestOptions { - // TODO@API add short name for the feature using the language model - /** * A human-readable message that explains why access to a language model is needed and what feature is enabled by it. */ @@ -328,10 +327,10 @@ declare module 'vscode' { * @param token Optional cancellation token. * @returns A thenable that resolves to the length of the message in tokens. */ - // TODO@API optional? + // TODO@API `undefined` when the language model does not support computing token length // ollama has nothing // anthropic suggests to count after the fact https://github.com/anthropics/anthropic-tokenizer-typescript?tab=readme-ov-file#anthropic-typescript-tokenizer - export function computeTokenLength(languageModel: string, text: string | LanguageModelChatMessage | LanguageModelChatMessage2, token?: CancellationToken): Thenable; + export function computeTokenLength(languageModel: string, text: string | LanguageModelChatMessage | LanguageModelChatMessage2, token?: CancellationToken): Thenable; } /** From 837430d8ad8dc7298dcd1ccce67f995f22182596 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 6 May 2024 16:27:32 +0200 Subject: [PATCH 7/7] use `LanguageModelInformation#contextLength` --- src/vs/workbench/api/common/extHostLanguageModels.ts | 6 +++--- src/vscode-dts/vscode.proposed.languageModels.d.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index 89534da4033..a7823af0e4c 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -419,12 +419,12 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { return undefined; } - return { + return Object.freeze({ id: data.identifier, name: data.name, version: data.version, - tokens: data.tokens, - }; + contextLength: data.tokens, + }); } private readonly _languageAccessInformationExtensions = new Set>(); diff --git a/src/vscode-dts/vscode.proposed.languageModels.d.ts b/src/vscode-dts/vscode.proposed.languageModels.d.ts index 34299a308c0..be546fe75b1 100644 --- a/src/vscode-dts/vscode.proposed.languageModels.d.ts +++ b/src/vscode-dts/vscode.proposed.languageModels.d.ts @@ -189,11 +189,11 @@ declare module 'vscode' { * The number of available tokens that can be used when sending requests * to the language model. * + * _Note_ that input- and output-tokens count towards this limit. + * * @see {@link lm.sendChatRequest} */ - // TODO@API is this optional? - // TODO@API rename: contextLength? - readonly tokens: number; + readonly contextLength: number; } /**