diff --git a/src/vs/workbench/api/browser/mainThreadChat.ts b/src/vs/workbench/api/browser/mainThreadChat.ts index 8ccce9f3b1fd5..630ea5116d29d 100644 --- a/src/vs/workbench/api/browser/mainThreadChat.ts +++ b/src/vs/workbench/api/browser/mainThreadChat.ts @@ -109,6 +109,9 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { provideWelcomeMessage: (token) => { return this._proxy.$provideWelcomeMessage(handle, token); }, + provideSampleQuestions: (token) => { + return this._proxy.$provideSampleQuestions(handle, token); + }, provideSlashCommands: (session, token) => { return this._proxy.$provideSlashCommands(handle, session.id, token); }, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1dbe62c996d82..710db4904443e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1241,6 +1241,7 @@ export interface MainThreadChatShape extends IDisposable { export interface ExtHostChatShape { $prepareChat(handle: number, initialState: any, token: CancellationToken): Promise; $provideWelcomeMessage(handle: number, token: CancellationToken): Promise<(string | IChatReplyFollowup[])[] | undefined>; + $provideSampleQuestions(handle: number, token: CancellationToken): Promise; $provideFollowups(handle: number, sessionId: number, token: CancellationToken): Promise; $provideReply(handle: number, sessionId: number, request: IChatRequestDto, token: CancellationToken): Promise; $removeRequest(handle: number, sessionId: number, requestId: string): void; diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index b4b3ce2b22c99..efa6d30c49d9c 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -140,6 +140,24 @@ export class ExtHostChat implements ExtHostChatShape { return rawFollowups?.map(f => typeConvert.ChatFollowup.from(f)); } + async $provideSampleQuestions(handle: number, token: CancellationToken): Promise { + const entry = this._chatProvider.get(handle); + if (!entry) { + return undefined; + } + + if (!entry.provider.provideSampleQuestions) { + return undefined; + } + + const rawFollowups = await entry.provider.provideSampleQuestions(token); + if (!rawFollowups) { + return undefined; + } + + return rawFollowups?.map(f => typeConvert.ChatReplyFollowup.from(f)); + } + $removeRequest(handle: number, sessionId: number, requestId: string): void { const entry = this._chatProvider.get(handle); if (!entry) { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index ece9f0fd488c4..706319d7b6f3f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -264,6 +264,8 @@ export class ChatWidget extends Disposable implements IChatWidget { const lastItem = treeItems[treeItems.length - 1]?.element; if (lastItem && isResponseVM(lastItem) && lastItem.isComplete) { this.renderFollowups(lastItem.replyFollowups); + } else if (lastItem && isWelcomeVM(lastItem)) { + this.renderFollowups(lastItem.sampleQuestions); } else { this.renderFollowups(undefined); } diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index f9ea5dc62ccc7..58e534489e364 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -551,7 +551,7 @@ export class ChatModel extends Disposable implements IChatModel { if (obj.welcomeMessage) { const content = obj.welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item); - this._welcomeMessage = new ChatWelcomeMessageModel(this, content); + this._welcomeMessage = new ChatWelcomeMessageModel(this, content, []); } try { @@ -796,6 +796,7 @@ export type IChatWelcomeMessageContent = IMarkdownString | IChatReplyFollowup[]; export interface IChatWelcomeMessageModel { readonly id: string; readonly content: IChatWelcomeMessageContent[]; + readonly sampleQuestions: IChatReplyFollowup[]; readonly username: string; readonly avatarIconUri?: URI; @@ -812,6 +813,7 @@ export class ChatWelcomeMessageModel implements IChatWelcomeMessageModel { constructor( private readonly session: ChatModel, public readonly content: IChatWelcomeMessageContent[], + public readonly sampleQuestions: IChatReplyFollowup[] ) { this._id = 'welcome_' + ChatWelcomeMessageModel.nextId++; } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 73a0b7a7381b4..3d280772e1ecf 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -108,6 +108,7 @@ export interface IChatProvider { readonly iconUrl?: string; prepareSession(initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult; provideWelcomeMessage?(token: CancellationToken): ProviderResult<(string | IChatReplyFollowup[])[] | undefined>; + provideSampleQuestions?(token: CancellationToken): ProviderResult; provideFollowups?(session: IChat, token: CancellationToken): ProviderResult; provideReply(request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult; provideSlashCommands?(session: IChat, token: CancellationToken): ProviderResult; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 1ffbac4491625..14ed59883499d 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -362,7 +362,10 @@ export class ChatService extends Disposable implements IChatService { const welcomeMessage = model.welcomeMessage ? undefined : await provider.provideWelcomeMessage?.(token) ?? undefined; const welcomeModel = welcomeMessage && new ChatWelcomeMessageModel( - model, welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[])); + model, + welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[]), + await provider.provideSampleQuestions?.(token) ?? [] + ); model.initialize(session, welcomeModel); } catch (err) { diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 10cfa2b719b4b..09aa29178b2a7 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -373,5 +373,6 @@ export interface IChatWelcomeMessageViewModel { readonly username: string; readonly avatarIconUri?: URI; readonly content: IChatWelcomeMessageContent[]; + readonly sampleQuestions: IChatReplyFollowup[]; currentRenderedHeight?: number; } diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index e9161140c3365..a3c2fd927bf49 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -204,6 +204,7 @@ declare module 'vscode' { export interface InteractiveSessionProvider { provideWelcomeMessage?(token: CancellationToken): ProviderResult; + provideSampleQuestions?(token: CancellationToken): ProviderResult; provideFollowups?(session: S, token: CancellationToken): ProviderResult<(string | InteractiveSessionFollowup)[]>; provideSlashCommands?(session: S, token: CancellationToken): ProviderResult;