Skip to content

Commit c35bf16

Browse files
authored
Support serialized tool call results for generic tools (similar to MCP, display input and outupt) (#292773)
* Support serialized tool call results for generic tools (similar to MCP, display input and outupt) * Updates * Updates
1 parent d982778 commit c35bf16

File tree

8 files changed

+168
-10
lines changed

8 files changed

+168
-10
lines changed

src/vs/workbench/api/common/extHostTypeConverters.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2961,6 +2961,13 @@ export namespace ChatToolInvocationPart {
29612961
status: todoStatusEnumToString(todo.status)
29622962
}))
29632963
};
2964+
} else if ('input' in data && 'output' in data && !Array.isArray(data.output)) {
2965+
// Convert extension API simple tool invocation data to internal format
2966+
return {
2967+
kind: 'simpleToolInvocation',
2968+
input: typeof data.input === 'string' ? data.input : '',
2969+
output: typeof data.output === 'string' ? data.output : ''
2970+
};
29642971
} else if (data && 'values' in data && Array.isArray(data.values)) {
29652972
// Convert extension API resources tool data to internal format
29662973
return {

src/vs/workbench/contrib/chat/browser/accessibility/chatResponseAccessibleView.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { ServicesAccessor } from '../../../../../platform/instantiation/common/i
1616
import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js';
1717
import { migrateLegacyTerminalToolSpecificData } from '../../common/chat.js';
1818
import { ChatContextKeys } from '../../common/actions/chatContextKeys.js';
19-
import { IChatExtensionsContent, IChatPullRequestContent, IChatSubagentToolInvocationData, IChatTerminalToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, IChatToolResourcesInvocationData, ILegacyChatTerminalToolInvocationData, IToolResultOutputDetailsSerialized, isLegacyChatTerminalToolInvocationData } from '../../common/chatService/chatService.js';
19+
import { IChatExtensionsContent, IChatPullRequestContent, IChatSimpleToolInvocationData, IChatSubagentToolInvocationData, IChatTerminalToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, IChatToolResourcesInvocationData, ILegacyChatTerminalToolInvocationData, IToolResultOutputDetailsSerialized, isLegacyChatTerminalToolInvocationData } from '../../common/chatService/chatService.js';
2020
import { isResponseVM } from '../../common/model/chatViewModel.js';
2121
import { IToolResultInputOutputDetails, IToolResultOutputDetails, isToolResultInputOutputDetails, isToolResultOutputDetails, toolContentToA11yString } from '../../common/tools/languageModelToolsService.js';
2222
import { ChatTreeItem, IChatWidget, IChatWidgetService } from '../chat.js';
@@ -48,7 +48,7 @@ export class ChatResponseAccessibleView implements IAccessibleViewImplementation
4848
}
4949
}
5050

51-
type ToolSpecificData = IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatToolResourcesInvocationData;
51+
type ToolSpecificData = IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData | IChatToolResourcesInvocationData;
5252
type ResultDetails = Array<URI | Location> | IToolResultInputOutputDetails | IToolResultOutputDetails | IToolResultOutputDetailsSerialized;
5353

5454
function isOutputDetailsSerialized(obj: unknown): obj is IToolResultOutputDetailsSerialized {
@@ -118,6 +118,11 @@ export function getToolSpecificDataDescription(toolSpecificData: ToolSpecificDat
118118
}).join(', ');
119119
return localize('resourcesList', "Resources: {0}", paths);
120120
}
121+
case 'simpleToolInvocation': {
122+
const inputText = toolSpecificData.input;
123+
const outputText = toolSpecificData.output;
124+
return localize('simpleToolInvocation', "Input: {0}, Output: {1}", inputText, outputText);
125+
}
121126
default:
122127
return '';
123128
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { ProgressBar } from '../../../../../../../base/browser/ui/progressbar/progressbar.js';
7+
import { IMarkdownString } from '../../../../../../../base/common/htmlContent.js';
8+
import { Lazy } from '../../../../../../../base/common/lazy.js';
9+
import { toDisposable } from '../../../../../../../base/common/lifecycle.js';
10+
import { autorun } from '../../../../../../../base/common/observable.js';
11+
import { ILanguageService } from '../../../../../../../editor/common/languages/language.js';
12+
import { IModelService } from '../../../../../../../editor/common/services/model.js';
13+
import { IConfigurationService } from '../../../../../../../platform/configuration/common/configuration.js';
14+
import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js';
15+
import { ChatConfiguration } from '../../../../common/constants.js';
16+
import { IChatSimpleToolInvocationData, IChatToolInvocation, IChatToolInvocationSerialized } from '../../../../common/chatService/chatService.js';
17+
import { IChatCodeBlockInfo } from '../../../chat.js';
18+
import { IChatContentPartRenderContext } from '../chatContentParts.js';
19+
import { ChatCollapsibleInputOutputContentPart, ChatCollapsibleIOPart, IChatCollapsibleIOCodePart } from '../chatToolInputOutputContentPart.js';
20+
import { BaseChatToolInvocationSubPart } from './chatToolInvocationSubPart.js';
21+
import { getToolApprovalMessage } from './chatToolPartUtilities.js';
22+
23+
export class ChatSimpleToolProgressPart extends BaseChatToolInvocationSubPart {
24+
/** Remembers expanded tool parts on re-render */
25+
private static readonly _expandedByDefault = new WeakMap<IChatToolInvocation | IChatToolInvocationSerialized, boolean>();
26+
27+
public readonly domNode: HTMLElement;
28+
private readonly collapsibleListPart: ChatCollapsibleInputOutputContentPart;
29+
30+
public get codeblocks(): IChatCodeBlockInfo[] {
31+
return this.collapsibleListPart.codeblocks;
32+
}
33+
34+
constructor(
35+
toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized,
36+
context: IChatContentPartRenderContext,
37+
codeBlockStartIndex: number,
38+
message: string | IMarkdownString,
39+
subtitle: string | IMarkdownString | undefined,
40+
data: IChatSimpleToolInvocationData,
41+
isError: boolean,
42+
@IInstantiationService instantiationService: IInstantiationService,
43+
@IModelService modelService: IModelService,
44+
@ILanguageService languageService: ILanguageService,
45+
@IConfigurationService configurationService: IConfigurationService,
46+
) {
47+
super(toolInvocation);
48+
49+
let codeBlockIndex = codeBlockStartIndex;
50+
51+
// Helper to convert string or MarkdownString to a collapsible part
52+
const createIOPart = (content: string, label: string): IChatCollapsibleIOCodePart | ChatCollapsibleIOPart => {
53+
return {
54+
kind: 'code',
55+
data: content,
56+
languageId: 'plaintext',
57+
codeBlockIndex: codeBlockIndex++,
58+
ownerMarkdownPartId: this.codeblocksPartId,
59+
options: {
60+
hideToolbar: true,
61+
reserveWidth: 19,
62+
maxHeightInLines: 13,
63+
verticalPadding: 5,
64+
editorOptions: {
65+
wordWrap: 'on'
66+
}
67+
}
68+
};
69+
};
70+
71+
const inputPart = createIOPart(data.input, 'Input') as IChatCollapsibleIOCodePart;
72+
const outputParts = data.output ? [createIOPart(data.output, 'Output')] : undefined;
73+
74+
const collapsibleListPart = this.collapsibleListPart = this._register(instantiationService.createInstance(
75+
ChatCollapsibleInputOutputContentPart,
76+
message,
77+
subtitle,
78+
this.getAutoApprovalMessageContent(),
79+
context,
80+
inputPart,
81+
outputParts ? { parts: outputParts } : undefined,
82+
isError,
83+
// Expand by default when there's an error (if setting enabled),
84+
// otherwise use the stored expanded state (defaulting to false)
85+
(isError && configurationService.getValue<boolean>(ChatConfiguration.AutoExpandToolFailures)) ||
86+
(ChatSimpleToolProgressPart._expandedByDefault.get(toolInvocation) ?? false),
87+
));
88+
this._register(toDisposable(() => ChatSimpleToolProgressPart._expandedByDefault.set(toolInvocation, collapsibleListPart.expanded)));
89+
90+
const progressObservable = toolInvocation.kind === 'toolInvocation' ? toolInvocation.state.map((s, r) => s.type === IChatToolInvocation.StateKind.Executing ? s.progress.read(r) : undefined) : undefined;
91+
const progressBar = new Lazy(() => this._register(new ProgressBar(collapsibleListPart.domNode)));
92+
if (progressObservable) {
93+
this._register(autorun(reader => {
94+
const progress = progressObservable?.read(reader);
95+
if (progress?.message) {
96+
collapsibleListPart.title = progress.message;
97+
}
98+
if (progress?.progress && !IChatToolInvocation.isComplete(toolInvocation, reader)) {
99+
progressBar.value.setWorked(progress.progress * 100);
100+
}
101+
}));
102+
}
103+
104+
this.domNode = collapsibleListPart.domNode;
105+
}
106+
107+
private getAutoApprovalMessageContent() {
108+
return getToolApprovalMessage(this.toolInvocation);
109+
}
110+
}

src/vs/workbench/contrib/chat/browser/widget/chatContentParts/toolInvocationParts/chatToolInvocationPart.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { ExtensionsInstallConfirmationWidgetSubPart } from './chatExtensionsInst
2222
import { ChatInputOutputMarkdownProgressPart } from './chatInputOutputMarkdownProgressPart.js';
2323
import { ChatMcpAppSubPart, IMcpAppRenderData } from './chatMcpAppSubPart.js';
2424
import { ChatResultListSubPart } from './chatResultListSubPart.js';
25+
import { ChatSimpleToolProgressPart } from './chatSimpleToolProgressPart.js';
2526
import { ChatTerminalToolConfirmationSubPart } from './chatTerminalToolConfirmationSubPart.js';
2627
import { ChatTerminalToolProgressPart } from './chatTerminalToolProgressPart.js';
2728
import { ToolConfirmationSubPart } from './chatToolConfirmationSubPart.js';
@@ -189,6 +190,20 @@ export class ChatToolInvocationPart extends Disposable implements IChatContentPa
189190
return this.instantiationService.createInstance(ChatResultListSubPart, this.toolInvocation, this.context, this.toolInvocation.pastTenseMessage ?? this.toolInvocation.invocationMessage, this.toolInvocation.toolSpecificData.values, this.listPool);
190191
}
191192

193+
if (this.toolInvocation.toolSpecificData?.kind === 'simpleToolInvocation') {
194+
return this.instantiationService.createInstance(
195+
ChatSimpleToolProgressPart,
196+
this.toolInvocation,
197+
this.context,
198+
this.codeBlockStartIndex,
199+
this.toolInvocation.pastTenseMessage ?? this.toolInvocation.invocationMessage,
200+
this.toolInvocation.originMessage,
201+
this.toolInvocation.toolSpecificData,
202+
false,
203+
);
204+
}
205+
206+
192207
const resultDetails = IChatToolInvocation.resultDetails(this.toolInvocation);
193208
if (Array.isArray(resultDetails) && resultDetails.length) {
194209
return this.instantiationService.createInstance(ChatResultListSubPart, this.toolInvocation, this.context, this.toolInvocation.pastTenseMessage ?? this.toolInvocation.invocationMessage, resultDetails, this.listPool);

src/vs/workbench/contrib/chat/common/chatService/chatService.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ export type ConfirmedReason =
535535

536536
export interface IChatToolInvocation {
537537
readonly presentation: IPreparedToolInvocation['presentation'];
538-
readonly toolSpecificData?: IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatToolResourcesInvocationData;
538+
readonly toolSpecificData?: IChatTerminalToolInvocationData | ILegacyChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData | IChatToolResourcesInvocationData;
539539
readonly originMessage: string | IMarkdownString | undefined;
540540
readonly invocationMessage: string | IMarkdownString;
541541
readonly pastTenseMessage: string | IMarkdownString | undefined;
@@ -795,7 +795,7 @@ export interface IToolResultOutputDetailsSerialized {
795795
*/
796796
export interface IChatToolInvocationSerialized {
797797
presentation: IPreparedToolInvocation['presentation'];
798-
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatToolResourcesInvocationData;
798+
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatPullRequestContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData | IChatToolResourcesInvocationData;
799799
invocationMessage: string | IMarkdownString;
800800
originMessage: string | IMarkdownString | undefined;
801801
pastTenseMessage: string | IMarkdownString | undefined;
@@ -842,6 +842,12 @@ export interface IChatTodoListContent {
842842
}>;
843843
}
844844

845+
export interface IChatSimpleToolInvocationData {
846+
kind: 'simpleToolInvocation';
847+
input: string;
848+
output: string;
849+
}
850+
845851
export interface IChatToolResourcesInvocationData {
846852
readonly kind: 'resources';
847853
readonly values: Array<URI | Location>;

src/vs/workbench/contrib/chat/common/model/chatProgressTypes/chatToolInvocation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { encodeBase64 } from '../../../../../../base/common/buffer.js';
77
import { IMarkdownString } from '../../../../../../base/common/htmlContent.js';
88
import { IObservable, ISettableObservable, observableValue } from '../../../../../../base/common/observable.js';
99
import { localize } from '../../../../../../nls.js';
10-
import { ConfirmedReason, IChatExtensionsContent, IChatSubagentToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, IChatToolInvocationSerialized, ToolConfirmKind, type IChatTerminalToolInvocationData } from '../../chatService/chatService.js';
10+
import { ConfirmedReason, IChatExtensionsContent, IChatSimpleToolInvocationData, IChatSubagentToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, IChatToolInvocationSerialized, ToolConfirmKind, type IChatTerminalToolInvocationData } from '../../chatService/chatService.js';
1111
import { IPreparedToolInvocation, isToolResultOutputDetails, IToolConfirmationMessages, IToolData, IToolProgressStep, IToolResult, ToolDataSource } from '../../tools/languageModelToolsService.js';
1212

1313
export interface IStreamingToolCallOptions {
@@ -33,7 +33,7 @@ export class ChatToolInvocation implements IChatToolInvocation {
3333
public generatedTitle?: string;
3434
public readonly chatRequestId?: string;
3535

36-
public toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData;
36+
public toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData;
3737

3838
private readonly _progress = observableValue<{ message?: string | IMarkdownString; progress: number | undefined }>(this, { progress: 0 });
3939
private readonly _state: ISettableObservable<IChatToolInvocation.State>;

src/vs/workbench/contrib/chat/common/tools/languageModelToolsService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { createDecorator } from '../../../../../platform/instantiation/common/in
2424
import { IProgress } from '../../../../../platform/progress/common/progress.js';
2525
import { ChatRequestToolReferenceEntry } from '../attachments/chatVariableEntries.js';
2626
import { IVariableReference } from '../chatModes.js';
27-
import { IChatExtensionsContent, IChatSubagentToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, type IChatTerminalToolInvocationData } from '../chatService/chatService.js';
27+
import { IChatExtensionsContent, IChatSimpleToolInvocationData, IChatSubagentToolInvocationData, IChatTodoListContent, IChatToolInputInvocationData, IChatToolInvocation, type IChatTerminalToolInvocationData } from '../chatService/chatService.js';
2828
import { ILanguageModelChatMetadata, LanguageModelPartAudience } from '../languageModels.js';
2929
import { UserSelectedTools } from '../participants/chatAgents.js';
3030
import { PromptElementJSON, stringifyPromptElementJSON } from './promptTsxTypes.js';
@@ -179,7 +179,7 @@ export interface IToolInvocation {
179179
* Lets us add some nicer UI to toolcalls that came from a sub-agent, but in the long run, this should probably just be rendered in a similar way to thinking text + tool call groups
180180
*/
181181
subAgentInvocationId?: string;
182-
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData;
182+
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData;
183183
modelId?: string;
184184
userSelectedTools?: UserSelectedTools;
185185
}
@@ -349,7 +349,7 @@ export interface IPreparedToolInvocation {
349349
originMessage?: string | IMarkdownString;
350350
confirmationMessages?: IToolConfirmationMessages;
351351
presentation?: ToolInvocationPresentation;
352-
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData;
352+
toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData | IChatExtensionsContent | IChatTodoListContent | IChatSubagentToolInvocationData | IChatSimpleToolInvocationData;
353353
}
354354

355355
export interface IToolImpl {

src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,21 @@ declare module 'vscode' {
280280
}>;
281281
}
282282

283+
/**
284+
* Generic tool result data that displays input and output in collapsible sections.
285+
*/
286+
export interface ChatSimpleToolResultData {
287+
/**
288+
* The input to display.
289+
*/
290+
input: string;
291+
/**
292+
* The output to display.
293+
*/
294+
output: string;
295+
}
296+
297+
283298
export interface ChatToolResourcesInvocationData {
284299
/**
285300
* Array of file URIs or locations to display as a collapsible list
@@ -296,7 +311,7 @@ declare module 'vscode' {
296311
pastTenseMessage?: string | MarkdownString;
297312
isConfirmed?: boolean;
298313
isComplete?: boolean;
299-
toolSpecificData?: ChatTerminalToolInvocationData | ChatMcpToolInvocationData | ChatTodoToolInvocationData | ChatToolResourcesInvocationData;
314+
toolSpecificData?: ChatTerminalToolInvocationData | ChatMcpToolInvocationData | ChatTodoToolInvocationData | ChatSimpleToolResultData | ChatToolResourcesInvocationData;
300315
subAgentInvocationId?: string;
301316
presentation?: 'hidden' | 'hiddenAfterComplete' | undefined;
302317

0 commit comments

Comments
 (0)