@@ -8,22 +8,140 @@ import { DeferredPromise, raceCancellation } from 'vs/base/common/async';
88import { CancellationToken } from 'vs/base/common/cancellation' ;
99import { toErrorMessage } from 'vs/base/common/errorMessage' ;
1010import { Emitter } from 'vs/base/common/event' ;
11- import { IMarkdownString } from 'vs/base/common/htmlContent' ;
11+ import { IMarkdownString , MarkdownString } from 'vs/base/common/htmlContent' ;
1212import { StopWatch } from 'vs/base/common/stopwatch' ;
1313import { URI } from 'vs/base/common/uri' ;
1414import { localize } from 'vs/nls' ;
1515import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' ;
1616import { ILogService } from 'vs/platform/log/common/log' ;
17- import { Progress } from 'vs/platform/progress/common/progress' ;
1817import { ExtHostChatAgentsShape2 , IChatAgentCompletionItem , IChatAgentHistoryEntryDto , IMainContext , MainContext , MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol' ;
1918import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider' ;
2019import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters' ;
2120import * as extHostTypes from 'vs/workbench/api/common/extHostTypes' ;
2221import { IChatAgentCommand , IChatAgentRequest , IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents' ;
23- import { IChatFollowup , IChatReplyFollowup , IChatUserActionEvent , InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService' ;
22+ import { IChatFollowup , IChatProgress , IChatReplyFollowup , IChatUserActionEvent , InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService' ;
2423import { checkProposedApiEnabled , isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions' ;
24+ import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier' ;
2525import type * as vscode from 'vscode' ;
2626
27+ class ChatAgentResponseStream {
28+
29+ private _stopWatch = StopWatch . create ( false ) ;
30+ private _isClosed : boolean = false ;
31+ private _firstProgress : number | undefined ;
32+ private _apiObject : vscode . ChatAgentExtendedResponseStream | undefined ;
33+
34+ constructor (
35+ private readonly _extension : IExtensionDescription ,
36+ private readonly _request : IChatAgentRequest ,
37+ private readonly _proxy : MainThreadChatAgentsShape2 ,
38+ @ILogService private readonly _logService : ILogService ,
39+ ) { }
40+
41+ close ( ) {
42+ this . _isClosed = true ;
43+ }
44+
45+ get timings ( ) {
46+ return {
47+ firstProgress : this . _firstProgress ,
48+ totalElapsed : this . _stopWatch . elapsed ( )
49+ } ;
50+ }
51+
52+ get apiObject ( ) {
53+
54+ if ( ! this . _apiObject ) {
55+
56+ const that = this ;
57+ this . _stopWatch . reset ( ) ;
58+
59+ function throwIfDone ( source : Function | undefined ) {
60+ if ( that . _isClosed ) {
61+ const err = new Error ( 'Response stream has been closed' ) ;
62+ Error . captureStackTrace ( err , source ) ;
63+ throw err ;
64+ }
65+ }
66+
67+ const _report = ( progress : Dto < IChatProgress > ) => {
68+ // Measure the time to the first progress update with real markdown content
69+ if ( typeof this . _firstProgress === 'undefined' && 'content' in progress ) {
70+ this . _firstProgress = this . _stopWatch . elapsed ( ) ;
71+ }
72+ this . _proxy . $handleProgressChunk ( this . _request . requestId , progress ) ;
73+ } ;
74+
75+ this . _apiObject = {
76+ markdown ( value , meta ) {
77+ throwIfDone ( this . markdown ) ;
78+ _report ( {
79+ kind : 'markdownContent' ,
80+ content : typeConvert . MarkdownString . from ( value )
81+ } ) ;
82+ return this ;
83+ } ,
84+ text ( value , meta ) {
85+ throwIfDone ( this . text ) ;
86+ this . markdown ( new MarkdownString ( ) . appendText ( value ) , meta ) ;
87+ return this ;
88+ } ,
89+ files ( value , meta ) {
90+ throwIfDone ( this . files ) ;
91+ _report ( {
92+ kind : 'treeData' ,
93+ treeData : value
94+ } ) ;
95+ return this ;
96+ } ,
97+ anchor ( value , meta ) {
98+ throwIfDone ( this . anchor ) ;
99+ _report ( {
100+ kind : 'inlineReference' ,
101+ name : meta ?. title ,
102+ inlineReference : ! URI . isUri ( value ) ? typeConvert . Location . from ( < vscode . Location > value ) : value
103+ } ) ;
104+ return this ;
105+ } ,
106+ progress ( value ) {
107+ throwIfDone ( this . progress ) ;
108+ _report ( {
109+ kind : 'progressMessage' ,
110+ content : new MarkdownString ( value )
111+ } ) ;
112+ return this ;
113+ } ,
114+ reference ( value ) {
115+ throwIfDone ( this . reference ) ;
116+ _report ( {
117+ kind : 'reference' ,
118+ reference : ! URI . isUri ( value ) ? typeConvert . Location . from ( < vscode . Location > value ) : value
119+ } ) ;
120+ return this ;
121+ } ,
122+ report ( progress ) {
123+ throwIfDone ( this . report ) ;
124+ if ( 'placeholder' in progress && 'resolvedContent' in progress ) {
125+ // Ignore for now, this is the deleted Task type
126+ return ;
127+ }
128+
129+ const value = typeConvert . ChatResponseProgress . from ( that . _extension , progress ) ;
130+ if ( ! value ) {
131+ that . _logService . error ( 'Unknown progress type: ' + JSON . stringify ( progress ) ) ;
132+ return ;
133+ }
134+
135+ _report ( value ) ;
136+ return this ;
137+ }
138+ } ;
139+ }
140+
141+ return this . _apiObject ;
142+ }
143+ }
144+
27145export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
28146
29147 private static _idPool = 0 ;
@@ -61,44 +179,17 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
61179 throw new Error ( `[CHAT](${ handle } ) CANNOT invoke agent because the agent is not registered` ) ;
62180 }
63181
64- let done = false ;
65- function throwIfDone ( ) {
66- if ( done ) {
67- throw new Error ( 'Only valid while executing the command' ) ;
68- }
69- }
70-
71182 const commandExecution = new DeferredPromise < void > ( ) ;
72183 token . onCancellationRequested ( ( ) => commandExecution . complete ( ) ) ;
73184 this . _extHostChatProvider . allowListExtensionWhile ( agent . extension . identifier , commandExecution . p ) ;
74185
75- const stopWatch = StopWatch . create ( false ) ;
76- let firstProgress : number | undefined ;
186+ const stream = new ChatAgentResponseStream ( agent . extension , request , this . _proxy , this . _logService ) ;
77187 try {
78188 const convertedHistory = await this . prepareHistory ( agent , request , context ) ;
79189 const task = agent . invoke (
80190 typeConvert . ChatAgentRequest . to ( request ) ,
81191 { history : convertedHistory } ,
82- new Progress < vscode . ChatAgentExtendedProgress > ( progress => {
83- throwIfDone ( ) ;
84-
85- // Measure the time to the first progress update with real markdown content
86- if ( typeof firstProgress === 'undefined' && 'content' in progress ) {
87- firstProgress = stopWatch . elapsed ( ) ;
88- }
89-
90- const convertedProgress = typeConvert . ChatResponseProgress . from ( agent . extension , progress ) ;
91- if ( ! convertedProgress ) {
92- this . _logService . error ( 'Unknown progress type: ' + JSON . stringify ( progress ) ) ;
93- return ;
94- }
95-
96- if ( 'placeholder' in progress && 'resolvedContent' in progress ) {
97- // Ignore for now, this is the deleted Task type
98- } else {
99- this . _proxy . $handleProgressChunk ( request . requestId , convertedProgress ) ;
100- }
101- } ) ,
192+ stream . apiObject ,
102193 token
103194 ) ;
104195
@@ -112,8 +203,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
112203 }
113204 sessionResults . set ( request . requestId , result ) ;
114205
115- const timings = { firstProgress : firstProgress , totalElapsed : stopWatch . elapsed ( ) } ;
116- return { errorDetails : result . errorDetails , timings } ;
206+ return { errorDetails : result . errorDetails , timings : stream . timings } ;
117207 } else {
118208 this . _previousResultMap . delete ( request . sessionId ) ;
119209 }
@@ -126,7 +216,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
126216 return { errorDetails : { message : localize ( 'errorResponse' , "Error from provider: {0}" , toErrorMessage ( e ) ) , responseIsIncomplete : true } } ;
127217
128218 } finally {
129- done = true ;
219+ stream . close ( ) ;
130220 commandExecution . complete ( ) ;
131221 }
132222 }
@@ -514,7 +604,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
514604 } satisfies vscode . ChatAgent2 < TResult > ;
515605 }
516606
517- invoke ( request : vscode . ChatAgentRequest , context : vscode . ChatAgentContext , progress : Progress < vscode . ChatAgentExtendedProgress > , token : CancellationToken ) : vscode . ProviderResult < vscode . ChatAgentResult2 > {
518- return this . _callback ( request , context , progress , token ) ;
607+ invoke ( request : vscode . ChatAgentRequest , context : vscode . ChatAgentContext , response : vscode . ChatAgentExtendedResponseStream , token : CancellationToken ) : vscode . ProviderResult < vscode . ChatAgentResult2 > {
608+ return this . _callback ( request , context , response , token ) ;
519609 }
520610}
0 commit comments