diff --git a/src/vs/base/common/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index aec37b76d18..4400c6246f3 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -25,6 +25,7 @@ export const enum MarshalledId { ChatViewContext, LanguageModelToolResult, LanguageModelTextPart, + LanguageModelThinkingPart, LanguageModelPromptTsxPart, LanguageModelDataPart, ChatSessionContext, diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 388dd4c34ff..6292b923b14 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -251,6 +251,10 @@ const _allApiProposals = { languageModelSystem: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', }, + languageModelThinkingPart: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelThinkingPart.d.ts', + version: 1 + }, languageModelToolResultAudience: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelToolResultAudience.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 38631237c1c..3a97d98f1eb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1843,6 +1843,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatResponseAnchorPart: extHostTypes.ChatResponseAnchorPart, ChatResponseProgressPart: extHostTypes.ChatResponseProgressPart, ChatResponseProgressPart2: extHostTypes.ChatResponseProgressPart2, + ChatResponseThinkingProgressPart: extHostTypes.ChatResponseThinkingProgressPart, ChatResponseReferencePart: extHostTypes.ChatResponseReferencePart, ChatResponseReferencePart2: extHostTypes.ChatResponseReferencePart, ChatResponseCodeCitationPart: extHostTypes.ChatResponseCodeCitationPart, @@ -1880,6 +1881,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelPartAudience: extHostTypes.LanguageModelPartAudience, ToolResultAudience: extHostTypes.LanguageModelPartAudience, // back compat LanguageModelToolCallPart: extHostTypes.LanguageModelToolCallPart, + LanguageModelThinkingPart: extHostTypes.LanguageModelThinkingPart, LanguageModelError: extHostTypes.LanguageModelError, LanguageModelToolResult: extHostTypes.LanguageModelToolResult, LanguageModelToolResult2: extHostTypes.LanguageModelToolResult2, diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 25d74d124f3..56fe0163e45 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -191,6 +191,14 @@ export class ChatAgentResponseStream { _report(dto, task); return this; }, + thinkingProgress(value, id?, metadata?) { + throwIfDone(this.thinkingProgress); + checkProposedApiEnabled(that._extension, 'chatParticipantAdditions'); + const part = new extHostTypes.ChatResponseThinkingProgressPart(value, id, metadata); + const dto = typeConvert.ChatResponseThinkingProgressPart.from(part); + _report(dto); + return this; + }, warning(value) { throwIfDone(this.progress); checkProposedApiEnabled(that._extension, 'chatParticipantAdditions'); @@ -296,6 +304,7 @@ export class ChatAgentResponseStream { part instanceof extHostTypes.ChatResponseCodeCitationPart || part instanceof extHostTypes.ChatResponseMovePart || part instanceof extHostTypes.ChatResponseExtensionsPart || + part instanceof extHostTypes.ChatResponseThinkingProgressPart || part instanceof extHostTypes.ChatResponsePullRequestPart || part instanceof extHostTypes.ChatResponseProgressPart2 ) { @@ -308,6 +317,9 @@ export class ChatAgentResponseStream { } else if (part instanceof extHostTypes.ChatResponseProgressPart2) { const dto = part.task ? typeConvert.ChatTask.from(part) : typeConvert.ChatResponseProgressPart.from(part); _report(dto, part.task); + } else if (part instanceof extHostTypes.ChatResponseThinkingProgressPart) { + const dto = typeConvert.ChatResponseThinkingProgressPart.from(part); + _report(dto); } else if (part instanceof extHostTypes.ChatResponseAnchorPart) { const dto = typeConvert.ChatResponseAnchorPart.from(part); diff --git a/src/vs/workbench/api/common/extHostLanguageModels.ts b/src/vs/workbench/api/common/extHostLanguageModels.ts index 4b8863a58d0..ef8fe86f010 100644 --- a/src/vs/workbench/api/common/extHostLanguageModels.ts +++ b/src/vs/workbench/api/common/extHostLanguageModels.ts @@ -38,7 +38,7 @@ type LanguageModelProviderData = { readonly provider: vscode.LanguageModelChatProvider2; }; -type LMResponsePart = vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart; +type LMResponsePart = vscode.LanguageModelTextPart | vscode.LanguageModelToolCallPart | vscode.LanguageModelDataPart | vscode.LanguageModelThinkingPart; class LanguageModelResponseStream { @@ -102,6 +102,9 @@ class LanguageModelResponse { let out: LMResponsePart; if (fragment.part.type === 'text') { out = new extHostTypes.LanguageModelTextPart(fragment.part.value, fragment.part.audience); + } else if (fragment.part.type === 'thinking') { + out = new extHostTypes.LanguageModelThinkingPart(fragment.part.value, fragment.part.id, fragment.part.metadata); + } else if (fragment.part.type === 'data') { out = new extHostTypes.LanguageModelDataPart(fragment.part.data.buffer, fragment.part.mimeType, fragment.part.audience); } else { @@ -301,6 +304,8 @@ export class ExtHostLanguageModels implements ExtHostLanguageModelsShape { part = { type: 'text', value: fragment.part.value, audience: fragment.part.audience }; } else if (fragment.part instanceof extHostTypes.LanguageModelDataPart) { part = { type: 'data', mimeType: fragment.part.mimeType, data: VSBuffer.wrap(fragment.part.data), audience: fragment.part.audience }; + } else if (fragment.part instanceof extHostTypes.LanguageModelThinkingPart) { + part = { type: 'thinking', value: fragment.part.value, id: fragment.part.id, metadata: fragment.part.metadata }; } if (!part) { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index ea38c9a83ad..31af9b3b2dd 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -41,7 +41,7 @@ import { IViewBadge } from '../../common/views.js'; import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { IChatRequestVariableEntry, isImageVariableEntry } from '../../contrib/chat/common/chatVariableEntries.js'; -import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffData, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; +import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffData, IChatPrepareToolInvocationPart, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatThinkingPart, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; import { IToolResult, IToolResultInputOutputDetails, IToolResultOutputDetails, ToolDataSource } from '../../contrib/chat/common/languageModelToolsService.js'; import * as chatProvider from '../../contrib/chat/common/languageModels.js'; import { IChatMessageDataPart, IChatResponseDataPart, IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js'; @@ -2699,6 +2699,20 @@ export namespace ChatResponseProgressPart { } } +export namespace ChatResponseThinkingProgressPart { + export function from(part: vscode.ChatResponseThinkingProgressPart): Dto { + return { + kind: 'thinking', + value: part.value, + id: part.id, + metadata: part.metadata + }; + } + export function to(part: Dto): vscode.ChatResponseThinkingProgressPart { + return new types.ChatResponseThinkingProgressPart(part.value); + } +} + export namespace ChatResponseWarningPart { export function from(part: vscode.ChatResponseWarningPart): Dto { return { @@ -2999,6 +3013,8 @@ export namespace ChatResponsePart { return ChatResponseReferencePart.from(part); } else if (part instanceof types.ChatResponseProgressPart) { return ChatResponseProgressPart.from(part); + } else if (part instanceof types.ChatResponseThinkingProgressPart) { + return ChatResponseThinkingProgressPart.from(part); } else if (part instanceof types.ChatResponseFileTreePart) { return ChatResponseFilesPart.from(part); } else if (part instanceof types.ChatResponseMultiDiffPart) { @@ -3260,7 +3276,9 @@ export namespace ChatAgentResult { if (value.$mid === MarshalledId.LanguageModelToolResult) { return new types.LanguageModelToolResult(cloneAndChange(value.content, reviveMetadata)); } else if (value.$mid === MarshalledId.LanguageModelTextPart) { - return new types.LanguageModelTextPart(value.value, value.audience); + return new types.LanguageModelTextPart(value.value); + } else if (value.$mid === MarshalledId.LanguageModelThinkingPart) { + return new types.LanguageModelThinkingPart(value.value, value.id, value.metadata); } else if (value.$mid === MarshalledId.LanguageModelPromptTsxPart) { return new types.LanguageModelPromptTsxPart(value.value); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index c7fcd0f5128..328e184142a 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4648,6 +4648,17 @@ export class ChatResponseProgressPart2 { } } +export class ChatResponseThinkingProgressPart { + value: string; + id?: string; + metadata?: string; + constructor(value: string, id?: string, metadata?: string) { + this.value = value; + this.id = id; + this.metadata = metadata; + } +} + export class ChatResponseWarningPart { value: vscode.MarkdownString; constructor(value: string | vscode.MarkdownString) { @@ -5096,6 +5107,28 @@ export enum ChatImageMimeType { BMP = 'image/bmp', } +export class LanguageModelThinkingPart implements vscode.LanguageModelThinkingPart { + value: string; + id?: string; + metadata?: string; + + constructor(value: string, id?: string, metadata?: string) { + this.value = value; + this.id = id; + this.metadata = metadata; + } + + toJSON() { + return { + $mid: MarshalledId.LanguageModelThinkingPart, + value: this.value, + id: this.id, + metadata: this.metadata, + }; + } +} + + export class LanguageModelPromptTsxPart { value: unknown; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatThinkingContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatThinkingContentPart.ts new file mode 100644 index 00000000000..e856ff39768 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatThinkingContentPart.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, clearNode } from '../../../../../base/browser/dom.js'; +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { IChatThinkingPart } from '../../common/chatService.js'; +import { IChatContentPartRenderContext, IChatContentPart } from './chatContentParts.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { MarkdownRenderer, IMarkdownRenderResult } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; + +export class ChatThinkingContentPart extends Disposable implements IChatContentPart { + readonly domNode: HTMLElement; + public readonly codeblocks: undefined; + public readonly codeblocksPartId: undefined; + + private readonly _onDidChangeHeight = this._register(new Emitter()); + readonly onDidChangeHeight = this._onDidChangeHeight.event; + + private currentThinkingValue: string; + private readonly renderer: MarkdownRenderer; + private textContainer!: HTMLElement; + private markdownResult: IMarkdownRenderResult | undefined; + + constructor( + content: IChatThinkingPart, + _context: IChatContentPartRenderContext, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + + this.renderer = instantiationService.createInstance(MarkdownRenderer, {}); + this.currentThinkingValue = this.parseContent(content.value || ''); + + this.domNode = $('.chat-thinking-box'); + this.domNode.tabIndex = 0; + this.renderContent(); + } + + private parseContent(content: string): string { + // Remove <|im_sep|>**** and similar markers + return content + .replace(/<\|im_sep\|>\*{4,}/g, '') + .replace(/<\|lim_sep\|>\*{4,}/g, '') + .trim(); + } + + private renderContent(): void { + this.textContainer = $('.chat-thinking-text.markdown-content'); + this.domNode.appendChild(this.textContainer); + + if (this.currentThinkingValue) { + this.renderMarkdown(this.currentThinkingValue); + } + } + + private renderMarkdown(content: string): void { + if (this.markdownResult) { + this.markdownResult.dispose(); + this.markdownResult = undefined; + } + + const cleanedContent = this.parseContent(content); + if (!cleanedContent) { + return; + } + + clearNode(this.textContainer); + this.markdownResult = this.renderer.render(new MarkdownString(cleanedContent)); + this.textContainer.appendChild(this.markdownResult.element); + } + + hasSameContent(other: any): boolean { + if (other.kind !== 'thinking') { + return false; + } + return true; + } + + override dispose(): void { + if (this.markdownResult) { + this.markdownResult.dispose(); + this.markdownResult = undefined; + } + super.dispose(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8e230a0d26e..a48e9c0e5e3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -49,7 +49,7 @@ import { IChatAgentMetadata } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IChatTextEditGroup } from '../common/chatModel.js'; import { chatSubcommandLeader } from '../common/chatParserTypes.js'; -import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatErrorLevel, IChatChangesSummary, IChatConfirmation, IChatContentReference, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatPullRequestContent, IChatMultiDiffData, IChatTask, IChatTaskSerialized, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop } from '../common/chatService.js'; +import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatErrorLevel, IChatChangesSummary, IChatConfirmation, IChatContentReference, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatMarkdownContent, IChatPullRequestContent, IChatMultiDiffData, IChatTask, IChatTaskSerialized, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatThinkingPart } from '../common/chatService.js'; import { IChatChangesSummaryPart, IChatCodeCitations, IChatErrorDetailsPart, IChatReferences, IChatRendererContent, IChatRequestViewModel, IChatResponseViewModel, IChatViewModel, IChatWorkingProgress, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; import { getNWords } from '../common/chatWordCounter.js'; import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'; @@ -86,6 +86,7 @@ import { alert } from '../../../../base/browser/ui/aria/aria.js'; import { CodiconActionViewItem } from '../../notebook/browser/view/cellParts/cellActionView.js'; import { ChatPullRequestContentPart } from './chatContentParts/chatPullRequestContentPart.js'; import { ChatCheckpointFileChangesSummaryContentPart } from './chatContentParts/chatChangesSummaryPart.js'; +import { ChatThinkingContentPart } from './chatContentParts/chatThinkingContentPart.js'; const $ = dom.$; @@ -1144,6 +1145,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { this.updateItemHeight(templateData); })); diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 687d559d88e..64de3b9de2c 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -2466,3 +2466,21 @@ have to be updated for changes to the rules above, or to support more deeply nes color: var(--vscode-foreground); flex-grow: 1; } + + +.interactive-session .interactive-response .chat-thinking-box { + border: 1px solid var(--vscode-panel-border); + border-radius: 4px; + padding: 0px 8px; + margin: 4px 0; + background-color: var(--vscode-editor-background); + + .chat-thinking-text { + font-size: inherit; + line-height: inherit; + display: flex; + align-items: flex-start; + } +} + + diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 4870d885447..df82c1fb21d 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -26,7 +26,7 @@ import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommo import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; import { IChatEditingService, IChatEditingSession } from './chatEditingService.js'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js'; -import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatClearToPreviousToolInvocation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatMultiDiffData, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext, ChatResponseClearToPreviousToolInvocationReason } from './chatService.js'; +import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatClearToPreviousToolInvocation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatEditingSessionAction, IChatElicitationRequest, IChatExtensionsContent, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatMultiDiffData, IChatNotebookEdit, IChatPrepareToolInvocationPart, IChatProgress, IChatProgressMessage, IChatPullRequestContent, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext, ChatResponseClearToPreviousToolInvocationReason } from './chatService.js'; import { IChatRequestVariableEntry } from './chatVariableEntries.js'; import { ChatAgentLocation, ChatModeKind } from './constants.js'; @@ -118,6 +118,7 @@ export type IChatProgressHistoryResponseContent = | IChatNotebookEditGroup | IChatConfirmation | IChatExtensionsContent + | IChatThinkingPart | IChatPullRequestContent; /** @@ -365,6 +366,7 @@ class AbstractResponse implements IResponse { case 'undoStop': case 'prepareToolInvocation': case 'elicitation': + case 'thinking': case 'multiDiffData': // Ignore continue; @@ -518,6 +520,20 @@ export class Response extends AbstractResponse implements IDisposable { this._responseParts[idx] = { ...lastResponsePart, content: appendMarkdownString(lastResponsePart.content, progress.content) }; } this._updateRepr(quiet); + } else if (progress.kind === 'thinking') { + + // TODO: @justschen merge thinking and markdown handling + const lastResponsePart = this._responseParts + .filter(p => p.kind !== 'textEditGroup') + .at(-1); + + if (!lastResponsePart || lastResponsePart.kind !== 'thinking' || !canMergeMarkdownStrings(new MarkdownString(lastResponsePart.value), new MarkdownString(progress.value))) { + this._responseParts.push(progress); + } else { + const idx = this._responseParts.indexOf(lastResponsePart); + this._responseParts[idx] = { ...lastResponsePart, value: appendMarkdownString(new MarkdownString(lastResponsePart.value), new MarkdownString(progress.value)).value }; + } + this._updateRepr(quiet); } else if (progress.kind === 'textEdit' || progress.kind === 'notebookEdit') { // If the progress.uri is a cell Uri, its possible its part of the inline chat. // Old approach of notebook inline chat would not start and end with notebook Uri, so we need to check for old approach. @@ -1736,6 +1752,13 @@ export class ChatModel extends Disposable implements IChatModel { return item.treeData; } else if (item.kind === 'markdownContent') { return item.content; + } else if (item.kind === 'thinking') { + return { + kind: 'thinking', + value: item.value, + id: item.id, + metadata: item.metadata + }; } else { return item as any; // TODO } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index aa0562a4d72..abb2bf72a68 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -271,6 +271,13 @@ export interface IChatElicitationRequest { onDidRequestHide: Event; } +export interface IChatThinkingPart { + kind: 'thinking'; + value: string; + id?: string; + metadata?: string; +} + export interface IChatTerminalToolInvocationData { kind: 'terminal'; commandLine: { @@ -391,6 +398,7 @@ export type IChatProgress = | IChatPullRequestContent | IChatUndoStop | IChatPrepareToolInvocationPart + | IChatThinkingPart | IChatTaskSerialized | IChatElicitationRequest; diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index 9c6ba1ba5f0..d4678314284 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -123,6 +123,13 @@ export interface IChatResponseToolUsePart { parameters: any; } +export interface IChatResponseThinkingPart { + type: 'thinking'; + value: string; + id?: string; + metadata?: string; +} + export interface IChatResponsePullRequestPart { type: 'pullRequest'; uri: URI; @@ -132,9 +139,9 @@ export interface IChatResponsePullRequestPart { linkTag: string; } -export type IExtendedChatResponsePart = IChatResponsePullRequestPart; +export type IChatResponsePart = IChatResponseTextPart | IChatResponseToolUsePart | IChatResponseDataPart | IChatResponseThinkingPart; -export type IChatResponsePart = IChatResponseTextPart | IChatResponseToolUsePart | IChatResponseDataPart; +export type IExtendedChatResponsePart = IChatResponsePullRequestPart; export interface IChatResponseFragment { index: number; diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index 12495cc0059..9e16ed6c346 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -149,7 +149,7 @@ declare module 'vscode' { constructor(value: ChatResponseDiffEntry[], title: string); } - export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatResponsePullRequestPart | ChatPrepareToolInvocationPart | ChatToolInvocationPart | ChatResponseMultiDiffPart; + export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseNotebookEditPart | ChatResponseConfirmationPart | ChatResponseCodeCitationPart | ChatResponseReferencePart2 | ChatResponseMovePart | ChatResponseExtensionsPart | ChatResponsePullRequestPart | ChatPrepareToolInvocationPart | ChatToolInvocationPart | ChatResponseMultiDiffPart | ChatResponseThinkingProgressPart; export class ChatResponseWarningPart { value: MarkdownString; constructor(value: string | MarkdownString); @@ -161,6 +161,23 @@ declare module 'vscode' { constructor(value: string, task?: (progress: Progress) => Thenable); } + /** + * A specialized progress part for displaying thinking/reasoning steps. + */ + export class ChatResponseThinkingProgressPart extends ChatResponseProgressPart { + value: string; + id?: string; + metadata?: string; + task?: (progress: Progress) => Thenable; + + /** + * Creates a new thinking progress part. + * @param value An initial progress message + * @param task A task that will emit thinking parts during its execution + */ + constructor(value: string, id?: string, metadata?: string, task?: (progress: Progress) => Thenable); + } + export class ChatResponseReferencePart2 { /** * The reference target. @@ -256,6 +273,8 @@ declare module 'vscode' { */ progress(value: string, task?: (progress: Progress) => Thenable): void; + thinkingProgress(value: string, id?: string, metadata?: string): void; + textEdit(target: Uri, edits: TextEdit | TextEdit[]): void; textEdit(target: Uri, isDone: true): void; diff --git a/src/vscode-dts/vscode.proposed.chatProvider.d.ts b/src/vscode-dts/vscode.proposed.chatProvider.d.ts index c6a58e900e9..ac0d1b92143 100644 --- a/src/vscode-dts/vscode.proposed.chatProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.chatProvider.d.ts @@ -139,6 +139,6 @@ declare module 'vscode' { export interface ChatResponseFragment2 { index: number; - part: LanguageModelTextPart | LanguageModelToolCallPart | LanguageModelDataPart; + part: LanguageModelTextPart | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelThinkingPart; } } diff --git a/src/vscode-dts/vscode.proposed.languageModelThinkingPart.d.ts b/src/vscode-dts/vscode.proposed.languageModelThinkingPart.d.ts new file mode 100644 index 00000000000..6abd7ec0ac8 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.languageModelThinkingPart.d.ts @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// version: 1 + +declare module 'vscode' { + + /** + * A language model response part containing thinking/reasoning content. + * Thinking tokens represent the model's internal reasoning process that + * typically streams before the final response. + */ + export class LanguageModelThinkingPart { + /** + * The thinking/reasoning text content. + */ + value: string; + + /** + * Optional unique identifier for this thinking sequence. + * This ID is typically provided at the end of the thinking stream + * and can be used for retrieval or reference purposes. + */ + id?: string; + + /** + * Optional metadata associated with this thinking sequence. + */ + metadata?: string; + + /** + * Construct a thinking part with the given content. + * @param value The thinking text content. + * @param id Optional unique identifier for this thinking sequence. + * @param metadata Optional metadata associated with this thinking sequence. + */ + constructor(value: string, id?: string, metadata?: string); + } + + export interface LanguageModelChatResponse { + /** + * An async iterable that is a stream of text, thinking, and tool-call parts forming the overall response. + * This includes {@link LanguageModelThinkingPart} which represents the model's internal reasoning process. + */ + stream: AsyncIterable; + } +}