diff --git a/src/vs/workbench/api/common/extHostCodeMapper.ts b/src/vs/workbench/api/common/extHostCodeMapper.ts index 055d52b3d4b49..0f6b9d1292238 100644 --- a/src/vs/workbench/api/common/extHostCodeMapper.ts +++ b/src/vs/workbench/api/common/extHostCodeMapper.ts @@ -8,7 +8,7 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ICodeMapperResult } from '../../contrib/chat/common/chatCodeMapperService.js'; import * as extHostProtocol from './extHost.protocol.js'; -import { TextEdit } from './extHostTypeConverters.js'; +import { ChatAgentResult, DocumentContextItem, TextEdit } from './extHostTypeConverters.js'; import { URI } from '../../../base/common/uri.js'; export class ExtHostCodeMapper implements extHostProtocol.ExtHostCodeMapperShape { @@ -45,10 +45,25 @@ export class ExtHostCodeMapper implements extHostProtocol.ExtHostCodeMapperShape codeBlocks: internalRequest.codeBlocks.map(block => { return { code: block.code, - resource: URI.revive(block.resource) + resource: URI.revive(block.resource), + markdownBeforeBlock: block.markdownBeforeBlock }; }), - conversation: internalRequest.conversation + conversation: internalRequest.conversation.map(item => { + if (item.type === 'request') { + return { + type: 'request', + message: item.message + } satisfies vscode.ConversationRequest; + } else { + return { + type: 'response', + message: item.message, + result: item.result ? ChatAgentResult.to(item.result) : undefined, + references: item.references?.map(DocumentContextItem.to) + } satisfies vscode.ConversationResponse; + } + }) }; const result = await provider.provideMappedEdits(request, stream, token); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 2eeed81cbec9e..14f182efbe17b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1629,7 +1629,7 @@ export namespace MappedEditsContext { ); } - export function from(extContext: vscode.MappedEditsContext): languages.MappedEditsContext { + export function from(extContext: vscode.MappedEditsContext): Dto { return { documents: extContext.documents.map((subArray) => subArray.map(DocumentContextItem.from) @@ -1643,6 +1643,7 @@ export namespace MappedEditsContext { { type: 'response', message: item.message, + result: item.result ? ChatAgentResult.from(item.result) : undefined, references: item.references?.map(DocumentContextItem.from) } )) @@ -1663,13 +1664,21 @@ export namespace DocumentContextItem { ); } - export function from(item: vscode.DocumentContextItem): languages.DocumentContextItem { + export function from(item: vscode.DocumentContextItem): Dto { return { - uri: URI.from(item.uri), + uri: item.uri, version: item.version, ranges: item.ranges.map(r => Range.from(r)), }; } + + export function to(item: Dto): vscode.DocumentContextItem { + return { + uri: URI.revive(item.uri), + version: item.version, + ranges: item.ranges.map(r => Range.to(r)), + }; + } } export namespace NotebookRange { @@ -2831,6 +2840,13 @@ export namespace ChatAgentResult { nextQuestion: result.nextQuestion, }; } + export function from(result: vscode.ChatResult): Dto { + return { + errorDetails: result.errorDetails, + metadata: result.metadata, + nextQuestion: result.nextQuestion, + }; + } } export namespace ChatAgentUserActionEvent { diff --git a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts index e4d58e6fdea9e..161860a4efc64 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts @@ -8,15 +8,13 @@ import { VSBuffer } from '../../../../../base/common/buffer.js'; import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { CharCode } from '../../../../../base/common/charCode.js'; import { isCancellationError } from '../../../../../base/common/errors.js'; -import { ResourceMap } from '../../../../../base/common/map.js'; import { isEqual } from '../../../../../base/common/resources.js'; import * as strings from '../../../../../base/common/strings.js'; -import { URI } from '../../../../../base/common/uri.js'; import { IActiveCodeEditor, isCodeEditor, isDiffEditor } from '../../../../../editor/browser/editorBrowser.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; import { Range } from '../../../../../editor/common/core/range.js'; -import { ConversationRequest, ConversationResponse, DocumentContextItem, isLocation, IWorkspaceFileEdit, IWorkspaceTextEdit } from '../../../../../editor/common/languages.js'; +import { ConversationRequest, ConversationResponse, DocumentContextItem, IWorkspaceFileEdit, IWorkspaceTextEdit } from '../../../../../editor/common/languages.js'; import { ILanguageService } from '../../../../../editor/common/languages/language.js'; import { ITextModel } from '../../../../../editor/common/model.js'; import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; @@ -31,7 +29,8 @@ import { InlineChatController } from '../../../inlineChat/browser/inlineChatCont import { insertCell } from '../../../notebook/browser/controller/cellOperations.js'; import { IActiveNotebookEditor, INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; import { CellKind, NOTEBOOK_EDITOR_ID } from '../../../notebook/common/notebookCommon.js'; -import { ChatUserAction, IChatContentReference, IChatService } from '../../common/chatService.js'; +import { getReferencesAsDocumentContext } from '../../common/chatCodeMapperService.js'; +import { ChatUserAction, IChatService } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; import { ICodeBlockActionContext } from '../codeBlockPart.js'; @@ -404,32 +403,6 @@ function getChatConversation(context: ICodeBlockActionContext): (ConversationReq } } -function getReferencesAsDocumentContext(res: readonly IChatContentReference[]): DocumentContextItem[] { - const map = new ResourceMap(); - for (const r of res) { - let uri; - let range; - if (URI.isUri(r.reference)) { - uri = r.reference; - } else if (isLocation(r.reference)) { - uri = r.reference.uri; - range = r.reference.range; - } - if (uri) { - const item = map.get(uri); - if (item) { - if (range) { - item.ranges.push(range); - } - } else { - map.set(uri, { uri, version: -1, ranges: range ? [range] : [] }); - } - } - } - return [...map.values()]; -} - - function reindent(codeBlockContent: string, model: ITextModel, seletionStartLine: number): string { const newContent = strings.splitLines(codeBlockContent); if (newContent.length === 0) { diff --git a/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts b/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts index 150cda7efd83e..bb6b812551f85 100644 --- a/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts +++ b/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts @@ -6,12 +6,15 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { CharCode } from '../../../../base/common/charCode.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../base/common/map.js'; import { splitLinesIncludeSeparators } from '../../../../base/common/strings.js'; import { isString } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; +import { DocumentContextItem, isLocation, TextEdit } from '../../../../editor/common/languages.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { IChatAgentResult } from './chatAgents.js'; import { IChatResponseModel } from './chatModel.js'; +import { IChatContentReference } from './chatService.js'; export interface ICodeMapperResponse { @@ -19,8 +22,9 @@ export interface ICodeMapperResponse { } export interface ICodeMapperCodeBlock { - code: string; - resource: URI; + readonly code: string; + readonly resource: URI; + readonly markdownBeforeBlock?: string; } export interface ConversationRequest { @@ -31,16 +35,17 @@ export interface ConversationRequest { export interface ConversationResponse { readonly type: 'response'; readonly message: string; - // readonly references?: DocumentContextItem[]; + readonly result?: IChatAgentResult; + readonly references?: DocumentContextItem[]; } export interface ICodeMapperRequest { - codeBlocks: ICodeMapperCodeBlock[]; - conversation: (ConversationRequest | ConversationResponse)[]; + readonly codeBlocks: ICodeMapperCodeBlock[]; + readonly conversation: (ConversationResponse | ConversationRequest)[]; } export interface ICodeMapperResult { - errorMessage?: string; + readonly errorMessage?: string; } export interface ICodeMapperProvider { @@ -103,7 +108,7 @@ export class CodeMapperService implements ICodeMapperService { fence = undefined; if (currentBlockUri) { // report the code block if we have a URI - codeBlocks.push({ code: currentBlock.join(''), resource: currentBlockUri }); + codeBlocks.push({ code: currentBlock.join(''), resource: currentBlockUri, markdownBeforeBlock: markdownBeforeBlock.join('') }); currentBlock.length = 0; markdownBeforeBlock.length = 0; currentBlockUri = undefined; @@ -123,7 +128,24 @@ export class CodeMapperService implements ICodeMapperService { currentBlockUri = lineOrUri; } } - return this.mapCode({ codeBlocks, conversation: [] }, response, token); + const conversation: (ConversationRequest | ConversationResponse)[] = []; + for (const request of responseModel.session.getRequests()) { + const response = request.response; + if (!response || response === responseModel) { + break; + } + conversation.push({ + type: 'request', + message: request.message.text + }); + conversation.push({ + type: 'response', + message: response.response.toMarkdown(), + result: response.result, + references: getReferencesAsDocumentContext(response.contentReferences) + }); + } + return this.mapCode({ codeBlocks, conversation }, response, token); } } @@ -155,3 +177,29 @@ function isLineIncomplete(line: string) { const lastChar = line.charCodeAt(line.length - 1); return lastChar !== CharCode.LineFeed && lastChar !== CharCode.CarriageReturn; } + + +export function getReferencesAsDocumentContext(res: readonly IChatContentReference[]): DocumentContextItem[] { + const map = new ResourceMap(); + for (const r of res) { + let uri; + let range; + if (URI.isUri(r.reference)) { + uri = r.reference; + } else if (isLocation(r.reference)) { + uri = r.reference.uri; + range = r.reference.range; + } + if (uri) { + const item = map.get(uri); + if (item) { + if (range) { + item.ranges.push(range); + } + } else { + map.set(uri, { uri, version: -1, ranges: range ? [range] : [] }); + } + } + } + return [...map.values()]; +} diff --git a/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts index 1d71b61768a91..69061d64ffd1e 100644 --- a/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts @@ -19,6 +19,7 @@ declare module 'vscode' { export interface ConversationResponse { readonly type: 'response'; readonly message: string; + readonly result?: ChatResult; readonly references?: DocumentContextItem[]; } @@ -53,7 +54,7 @@ declare module 'vscode' { } export interface MappedEditsRequest { - readonly codeBlocks: { code: string; resource: Uri }[]; + readonly codeBlocks: { code: string; resource: Uri; markdownBeforeBlock?: string }[]; readonly conversation: (ConversationRequest | ConversationResponse)[]; // for every prior response that contains codeblocks, make sure we pass the code as well as the resources based on the reported codemapper URIs }