diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts index 48d97ddffd0..0d19a3f98d8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInputOutputContentPart.ts @@ -33,6 +33,7 @@ export interface IChatCollapsibleIOCodePart { languageId: string; options: ICodeBlockRenderOptions; codeBlockInfo: IChatCodeBlockInfo; + title?: string | IMarkdownString; } export interface IChatCollapsibleIODataPart { diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolOutputContentSubPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolOutputContentSubPart.ts index 59db2546781..9d10743b613 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolOutputContentSubPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolOutputContentSubPart.ts @@ -25,6 +25,8 @@ import { IProgressService, ProgressLocation } from '../../../../../platform/prog import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { REVEAL_IN_EXPLORER_COMMAND_ID } from '../../../files/browser/fileConstants.js'; import { getAttachableImageExtension } from '../../common/chatModel.js'; +import { IMarkdownString, MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { IMarkdownRendererService } from '../../../../../platform/markdown/browser/markdownRenderer.js'; import { IChatRequestVariableEntry } from '../../common/chatVariableEntries.js'; import { IChatCodeBlockInfo } from '../chat.js'; import { CodeBlockPart, ICodeBlockData } from '../codeBlockPart.js'; @@ -44,22 +46,29 @@ export class ChatToolOutputContentSubPart extends Disposable { private _currentWidth: number = 0; private readonly _editorReferences: IDisposableReference[] = []; public readonly domNode: HTMLElement; - readonly codeblocks: IChatCodeBlockInfo[] = []; constructor( private readonly context: IChatContentPartRenderContext, private readonly parts: ChatCollapsibleIOPart[], - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IFileService private readonly _fileService: IFileService, + @IMarkdownRendererService private readonly _markdownRendererService: IMarkdownRendererService, ) { super(); this.domNode = this.createOutputContents(); this._currentWidth = context.currentWidth(); } + private toMdString(value: string | IMarkdownString): MarkdownString { + if (typeof value === 'string') { + return new MarkdownString('').appendText(value); + } + return new MarkdownString(value.value, { isTrusted: value.isTrusted }); + } + private createOutputContents(): HTMLElement { const container = dom.$('div'); @@ -145,6 +154,13 @@ export class ChatToolOutputContentSubPart extends Disposable { } private addCodeBlock(part: IChatCollapsibleIOCodePart, container: HTMLElement) { + if (part.title) { + const title = dom.$('div.chat-confirmation-widget-title'); + const renderedTitle = this._register(this._markdownRendererService.render(this.toMdString(part.title))); + title.appendChild(renderedTitle.element); + container.appendChild(title); + } + const data: ICodeBlockData = { languageId: part.languageId, textModel: Promise.resolve(part.textModel), diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolPostExecuteConfirmationPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolPostExecuteConfirmationPart.ts index 89e0c04609a..afe76061708 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolPostExecuteConfirmationPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/toolInvocationParts/chatToolPostExecuteConfirmationPart.ts @@ -120,6 +120,7 @@ export class ChatToolPostExecuteConfirmationPart extends AbstractToolConfirmatio parts.push({ kind: 'code', + title: part.title, textModel: model, languageId: model.getLanguageId(), options: { diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 92368fb0020..7ce9ecfea10 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -240,6 +240,7 @@ export interface IToolResultTextPart { kind: 'text'; value: string; audience?: LanguageModelPartAudience[]; + title?: string; } export interface IToolResultDataPart { @@ -249,6 +250,7 @@ export interface IToolResultDataPart { data: VSBuffer; }; audience?: LanguageModelPartAudience[]; + title?: string; } export interface IToolConfirmationMessages { diff --git a/src/vs/workbench/contrib/chat/electron-browser/tools/fetchPageTool.ts b/src/vs/workbench/contrib/chat/electron-browser/tools/fetchPageTool.ts index 72763be5bdb..d4291864b8c 100644 --- a/src/vs/workbench/contrib/chat/electron-browser/tools/fetchPageTool.ts +++ b/src/vs/workbench/contrib/chat/electron-browser/tools/fetchPageTool.ts @@ -144,7 +144,7 @@ export class FetchWebPageTool implements IToolImpl { const actuallyValidUris = [...webUris.values(), ...successfulFileUris]; return { - content: this._getPromptPartsForResults(results), + content: this._getPromptPartsForResults(urls, results), toolResultDetails: actuallyValidUris, confirmResults, }; @@ -278,28 +278,31 @@ export class FetchWebPageTool implements IToolImpl { return { webUris, fileUris, invalidUris }; } - private _getPromptPartsForResults(results: ResultType[]): (IToolResultTextPart | IToolResultDataPart)[] { - return results.map(value => { + private _getPromptPartsForResults(urls: string[], results: ResultType[]): (IToolResultTextPart | IToolResultDataPart)[] { + return results.map((value, i) => { + const title = results.length > 1 ? localize('fetchWebPage.fetchedFrom', 'Fetched from {0}', urls[i]) : undefined; if (!value) { return { kind: 'text', + title, value: localize('fetchWebPage.invalidUrl', 'Invalid URL') }; } else if (typeof value === 'string') { return { kind: 'text', + title, value: value }; } else if (value.type === 'tooldata') { - return value.value; + return { ...value.value, title }; } else if (value.type === 'extracted') { switch (value.value.status) { case 'ok': - return { kind: 'text', value: value.value.result }; + return { kind: 'text', title, value: value.value.result }; case 'redirect': - return { kind: 'text', value: `The webpage has redirected to "${value.value.toURI.toString(true)}". Use the ${InternalFetchWebPageToolId} again to get its contents.` }; + return { kind: 'text', title, value: `The webpage has redirected to "${value.value.toURI.toString(true)}". Use the ${InternalFetchWebPageToolId} again to get its contents.` }; case 'error': - return { kind: 'text', value: `An error occurred retrieving the fetch result: ${value.value.error}` }; + return { kind: 'text', title, value: `An error occurred retrieving the fetch result: ${value.value.error}` }; default: assertNever(value.value); }