Skip to content

Commit

Permalink
Don't register chat participants in stable (#213244)
Browse files Browse the repository at this point in the history
* Don't register chat participants in stable
And fork some Additions APIs into chatParticipantPrivate

* Remove stale proposals

* Move more API out of Additions
  • Loading branch information
roblourens authored May 22, 2024
1 parent 4ebc77f commit b9d35d9
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 140 deletions.
2 changes: 0 additions & 2 deletions extensions/vscode-api-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
"enabledApiProposals": [
"activeComment",
"authSession",
"chatParticipant",
"languageModels",
"defaultChatParticipant",
"chatVariableResolver",
"contribViewsRemote",
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol));
const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService));
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands));
const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, initData.quality));
const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol));
const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol));
const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol));
Expand Down Expand Up @@ -1427,7 +1427,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostChatAgents2.createChatAgent(extension, id, handler);
},
createDynamicChatParticipant(id: string, dynamicProps: vscode.DynamicChatParticipantProps, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant {
checkProposedApiEnabled(extension, 'chatParticipantAdditions');
checkProposedApiEnabled(extension, 'chatParticipantPrivate');
return extHostChatAgents2.createDynamicChatAgent(extension, id, dynamicProps, handler);
},
attachContext(name: string, value: string | vscode.Uri | vscode.Location | unknown, location: vscode.ChatLocation.Panel) {
Expand Down
30 changes: 25 additions & 5 deletions src/vs/workbench/api/common/extHostChatAgents2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
mainContext: IMainContext,
private readonly _logService: ILogService,
private readonly commands: ExtHostCommands,
private readonly quality: string | undefined
) {
super();
this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2);
Expand All @@ -274,16 +275,19 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS

createChatAgent(extension: IExtensionDescription, id: string, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant {
const handle = ExtHostChatAgents2._idPool++;
const agent = new ExtHostChatAgent(extension, id, this._proxy, handle, handler);
const agent = new ExtHostChatAgent(extension, this.quality, id, this._proxy, handle, handler);
this._agents.set(handle, agent);

this._proxy.$registerAgent(handle, extension.identifier, id, {}, undefined);
if (agent.isAgentEnabled()) {
this._proxy.$registerAgent(handle, extension.identifier, id, {}, undefined);
}

return agent.apiAgent;
}

createDynamicChatAgent(extension: IExtensionDescription, id: string, dynamicProps: vscode.DynamicChatParticipantProps, handler: vscode.ChatExtendedRequestHandler): vscode.ChatParticipant {
const handle = ExtHostChatAgents2._idPool++;
const agent = new ExtHostChatAgent(extension, id, this._proxy, handle, handler);
const agent = new ExtHostChatAgent(extension, this.quality, id, this._proxy, handle, handler);
this._agents.set(handle, agent);

this._proxy.$registerAgent(handle, extension.identifier, id, { isSticky: true } satisfies IExtensionChatAgentMetadata, dynamicProps);
Expand Down Expand Up @@ -330,6 +334,10 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS
responseIsIncomplete: true
};
}
if (errorDetails?.responseIsRedacted) {
checkProposedApiEnabled(agent.extension, 'chatParticipantPrivate');
}

return { errorDetails, timings: stream.timings, metadata: result?.metadata } satisfies IChatAgentResult;
}), token);
} catch (e) {
Expand Down Expand Up @@ -485,6 +493,7 @@ class ExtHostChatAgent {

constructor(
public readonly extension: IExtensionDescription,
private readonly quality: string | undefined,
public readonly id: string,
private readonly _proxy: MainThreadChatAgentsShape2,
private readonly _handle: number,
Expand All @@ -507,6 +516,11 @@ class ExtHostChatAgent {
return await this._agentVariableProvider.provider.provideCompletionItems(query, token) ?? [];
}

public isAgentEnabled() {
// If in stable and this extension doesn't have the right proposed API, then don't register the agent
return !(this.quality === 'stable' && !isProposedApiEnabled(this.extension, 'chatParticipantPrivate'));
}

async provideFollowups(result: vscode.ChatResult, context: vscode.ChatContext, token: CancellationToken): Promise<vscode.ChatFollowup[]> {
if (!this._followupProvider) {
return [];
Expand Down Expand Up @@ -564,6 +578,10 @@ class ExtHostChatAgent {
}
updateScheduled = true;
queueMicrotask(() => {
if (!that.isAgentEnabled()) {
return;
}

this._proxy.$updateAgent(this._handle, {
icon: !this._iconPath ? undefined :
this._iconPath instanceof URI ? this._iconPath :
Expand Down Expand Up @@ -658,11 +676,11 @@ class ExtHostChatAgent {
updateMetadataSoon();
},
get supportIssueReporting() {
checkProposedApiEnabled(that.extension, 'chatParticipantAdditions');
checkProposedApiEnabled(that.extension, 'chatParticipantPrivate');
return that._supportIssueReporting;
},
set supportIssueReporting(v) {
checkProposedApiEnabled(that.extension, 'chatParticipantAdditions');
checkProposedApiEnabled(that.extension, 'chatParticipantPrivate');
that._supportIssueReporting = v;
updateMetadataSoon();
},
Expand Down Expand Up @@ -707,10 +725,12 @@ class ExtHostChatAgent {
return that._requester;
},
set supportsSlowReferences(v) {
checkProposedApiEnabled(that.extension, 'chatParticipantPrivate');
that._supportsSlowReferences = v;
updateMetadataSoon();
},
get supportsSlowReferences() {
checkProposedApiEnabled(that.extension, 'chatParticipantPrivate');
return that._supportsSlowReferences;
},
dispose() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
private handleAndRegisterChatExtensions(): void {
chatParticipantExtensionPoint.setHandler((extensions, delta) => {
for (const extension of delta.added) {
if (this.productService.quality === 'stable' && !isProposedApiEnabled(extension.description, 'chatParticipantPrivate')) {
this.logService.warn(`Chat participants are not yet enabled in VS Code Stable (${extension.description.identifier.value})`);
continue;
}

for (const providerDescriptor of extension.value) {
if (!providerDescriptor.name.match(/^[\w0-9_-]+$/)) {
this.logService.error(`Extension '${extension.description.identifier.value}' CANNOT register participant with invalid name: ${providerDescriptor.name}. Name must match /^[\\w0-9_-]+$/.`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const allApiProposals = Object.freeze({
authSession: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.authSession.d.ts',
canonicalUriProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts',
chatParticipantAdditions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts',
chatParticipantPrivate: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts',
chatProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts',
chatTab: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts',
chatVariableResolver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts',
Expand Down
131 changes: 0 additions & 131 deletions src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,8 @@

declare module 'vscode' {

/**
* The location at which the chat is happening.
*/
export enum ChatLocation {
/**
* The chat panel
*/
Panel = 1,
/**
* Terminal inline chat
*/
Terminal = 2,
/**
* Notebook inline chat
*/
Notebook = 3,
/**
* Code editor inline chat
*/
Editor = 4
}

export interface ChatRequest {
/**
* The attempt number of the request. The first request has attempt number 0.
*/
readonly attempt: number;

/**
* If automatic command detection is enabled.
*/
readonly enableCommandDetection: boolean;

/**
* The location at which the chat is happening. This will always be one of the supported values
*/
readonly location: ChatLocation;
}

export interface ChatParticipant {
onDidPerformAction: Event<ChatUserActionEvent>;
supportIssueReporting?: boolean;

/**
* Temp, support references that are slow to resolve and should be tools rather than references.
*/
supportsSlowReferences?: boolean;
}

export interface ChatErrorDetails {
/**
* If set to true, the message content is completely hidden. Only ChatErrorDetails#message will be shown.
*/
responseIsRedacted?: boolean;
}

/**
Expand Down Expand Up @@ -224,8 +172,6 @@ declare module 'vscode' {
*/
export function createChatParticipant(id: string, handler: ChatExtendedRequestHandler): ChatParticipant;

export function createDynamicChatParticipant(id: string, dynamicProps: DynamicChatParticipantProps, handler: ChatExtendedRequestHandler): ChatParticipant;

/**
* Current version of the proposal. Changes whenever backwards-incompatible changes are made.
* If a new feature is added that doesn't break existing code, the version is not incremented. When the extension uses this new feature, it should set its engines.vscode version appropriately.
Expand All @@ -235,16 +181,6 @@ declare module 'vscode' {
export const _version: 1 | number;
}

/**
* These don't get set on the ChatParticipant after creation, like other props, because they are typically defined in package.json and we want them at the time of creation.
*/
export interface DynamicChatParticipantProps {
name: string;
publisherName: string;
description?: string;
fullName?: string;
}

/*
* User action events
*/
Expand Down Expand Up @@ -313,71 +249,4 @@ declare module 'vscode' {
*/
readonly name: string;
}

/**
* The detail level of this chat variable value.
*/
export enum ChatVariableLevel {
Short = 1,
Medium = 2,
Full = 3
}

export interface ChatVariableValue {
/**
* The detail level of this chat variable value. If possible, variable resolvers should try to offer shorter values that will consume fewer tokens in an LLM prompt.
*/
level: ChatVariableLevel;

/**
* The variable's value, which can be included in an LLM prompt as-is, or the chat participant may decide to read the value and do something else with it.
*/
value: string | Uri;

/**
* A description of this value, which could be provided to the LLM as a hint.
*/
description?: string;
}

export interface ChatVariableResolverResponseStream {
/**
* Push a progress part to this stream. Short-hand for
* `push(new ChatResponseProgressPart(value))`.
*
* @param value
* @returns This stream.
*/
progress(value: string): ChatVariableResolverResponseStream;

/**
* Push a reference to this stream. Short-hand for
* `push(new ChatResponseReferencePart(value))`.
*
* *Note* that the reference is not rendered inline with the response.
*
* @param value A uri or location
* @returns This stream.
*/
reference(value: Uri | Location): ChatVariableResolverResponseStream;

/**
* Pushes a part to this stream.
*
* @param part A response part, rendered or metadata
*/
push(part: ChatVariableResolverResponsePart): ChatVariableResolverResponseStream;
}

export type ChatVariableResolverResponsePart = ChatResponseProgressPart | ChatResponseReferencePart;

export interface ChatVariableResolver {
/**
* A callback to resolve the value of a chat variable.
* @param name The name of the variable.
* @param context Contextual information about this chat request.
* @param token A cancellation token.
*/
resolve2?(name: string, context: ChatVariableContext, stream: ChatVariableResolverResponseStream, token: CancellationToken): ProviderResult<ChatVariableValue[]>;
}
}
76 changes: 76 additions & 0 deletions src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* 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' {

/**
* The location at which the chat is happening.
*/
export enum ChatLocation {
/**
* The chat panel
*/
Panel = 1,
/**
* Terminal inline chat
*/
Terminal = 2,
/**
* Notebook inline chat
*/
Notebook = 3,
/**
* Code editor inline chat
*/
Editor = 4
}

export interface ChatRequest {
/**
* The attempt number of the request. The first request has attempt number 0.
*/
readonly attempt: number;

/**
* If automatic command detection is enabled.
*/
readonly enableCommandDetection: boolean;

/**
* The location at which the chat is happening. This will always be one of the supported values
*/
readonly location: ChatLocation;
}

export interface ChatParticipant {
supportIssueReporting?: boolean;

/**
* Temp, support references that are slow to resolve and should be tools rather than references.
*/
supportsSlowReferences?: boolean;
}

export interface ChatErrorDetails {
/**
* If set to true, the message content is completely hidden. Only ChatErrorDetails#message will be shown.
*/
responseIsRedacted?: boolean;
}

export namespace chat {
export function createDynamicChatParticipant(id: string, dynamicProps: DynamicChatParticipantProps, handler: ChatExtendedRequestHandler): ChatParticipant;
}

/**
* These don't get set on the ChatParticipant after creation, like other props, because they are typically defined in package.json and we want them at the time of creation.
*/
export interface DynamicChatParticipantProps {
name: string;
publisherName: string;
description?: string;
fullName?: string;
}
}
Loading

0 comments on commit b9d35d9

Please sign in to comment.