* 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
This commit is contained in:
Johannes
2024-05-06 11:00:17 +02:00
parent 940b65c9ab
commit 22efbdf96c
8 changed files with 140 additions and 49 deletions
@@ -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,
@@ -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<void> {
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);
@@ -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(<number>types.LanguageModelChatMessageRole.System, message.content);
case chatProvider.ChatMessageRole.User: return new types.LanguageModelChatMessage2(<number>types.LanguageModelChatMessageRole.User, message.content);
case chatProvider.ChatMessageRole.Assistant: return new types.LanguageModelChatMessage2(<number>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 {
@@ -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;
@@ -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',
+2
View File
@@ -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<ChatResponseFragment>, token: CancellationToken): Thenable<any>;
provideLanguageModelResponse2(messages: LanguageModelChatMessage[], options: { [name: string]: any }, extensionId: string, progress: Progress<ChatResponseFragment>, token: CancellationToken): Thenable<any>;
provideTokenCount(text: string | LanguageModelChatMessage, token: CancellationToken): Thenable<number>;
+17
View File
@@ -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
}
}
+38 -33
View File
@@ -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<string>;
}
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<string>;
};
}
/**
* 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<string>;
};
}
// 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<LanguageModelChatResponse>;
export function sendChatRequest(languageModel: string, messages: (LanguageModelChatMessage | LanguageModelChatMessage2)[], options: LanguageModelChatRequestOptions, token: CancellationToken): Thenable<LanguageModelChatResponse>;
/**
* 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<number>;
// 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<number>;
}
/**