@@ -8,22 +8,143 @@ 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 ) {
77+ throwIfDone ( this . markdown ) ;
78+ _report ( {
79+ kind : 'markdownContent' ,
80+ content : typeConvert . MarkdownString . from ( value )
81+ } ) ;
82+ return this ;
83+ } ,
84+ text ( value ) {
85+ throwIfDone ( this . text ) ;
86+ this . markdown ( new MarkdownString ( ) . appendText ( value ) ) ;
87+ return this ;
88+ } ,
89+ files ( value ) {
90+ throwIfDone ( this . files ) ;
91+ _report ( {
92+ kind : 'treeData' ,
93+ treeData : value
94+ } ) ;
95+ return this ;
96+ } ,
97+ anchor ( value ) {
98+ throwIfDone ( this . anchor ) ;
99+ _report ( {
100+ kind : 'inlineReference' ,
101+ inlineReference : ! URI . isUri ( value ) ? typeConvert . Location . from ( < vscode . Location > value ) : value
102+ } ) ;
103+ return this ;
104+ } ,
105+ progress ( value ) {
106+ throwIfDone ( this . progress ) ;
107+ _report ( {
108+ kind : 'progressMessage' ,
109+ content : new MarkdownString ( value )
110+ } ) ;
111+ return this ;
112+ } ,
113+ reference ( value ) {
114+ throwIfDone ( this . reference ) ;
115+ _report ( {
116+ kind : 'reference' ,
117+ reference : ! URI . isUri ( value ) ? typeConvert . Location . from ( < vscode . Location > value ) : value
118+ } ) ;
119+ return this ;
120+ } ,
121+ // annotation(value) {
122+ // _report(value);
123+ // return this;
124+ // },
125+ report ( progress ) {
126+ throwIfDone ( this . report ) ;
127+ if ( 'placeholder' in progress && 'resolvedContent' in progress ) {
128+ // Ignore for now, this is the deleted Task type
129+ return ;
130+ }
131+
132+ const value = typeConvert . ChatResponseProgress . from ( that . _extension , progress ) ;
133+ if ( ! value ) {
134+ that . _logService . error ( 'Unknown progress type: ' + JSON . stringify ( progress ) ) ;
135+ return ;
136+ }
137+
138+ _report ( value ) ;
139+ return this ;
140+ }
141+ } ;
142+ }
143+
144+ return this . _apiObject ;
145+ }
146+ }
147+
27148export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
28149
29150 private static _idPool = 0 ;
@@ -61,44 +182,17 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
61182 throw new Error ( `[CHAT](${ handle } ) CANNOT invoke agent because the agent is not registered` ) ;
62183 }
63184
64- let done = false ;
65- function throwIfDone ( ) {
66- if ( done ) {
67- throw new Error ( 'Only valid while executing the command' ) ;
68- }
69- }
70-
71185 const commandExecution = new DeferredPromise < void > ( ) ;
72186 token . onCancellationRequested ( ( ) => commandExecution . complete ( ) ) ;
73187 this . _extHostChatProvider . allowListExtensionWhile ( agent . extension . identifier , commandExecution . p ) ;
74188
75- const stopWatch = StopWatch . create ( false ) ;
76- let firstProgress : number | undefined ;
189+ const stream = new ChatAgentResponseStream ( agent . extension , request , this . _proxy , this . _logService ) ;
77190 try {
78191 const convertedHistory = await this . prepareHistory ( agent , request , context ) ;
79192 const task = agent . invoke (
80193 typeConvert . ChatAgentRequest . to ( request ) ,
81194 { 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- } ) ,
195+ stream . apiObject ,
102196 token
103197 ) ;
104198
@@ -112,8 +206,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
112206 }
113207 sessionResults . set ( request . requestId , result ) ;
114208
115- const timings = { firstProgress : firstProgress , totalElapsed : stopWatch . elapsed ( ) } ;
116- return { errorDetails : result . errorDetails , timings } ;
209+ return { errorDetails : result . errorDetails , timings : stream . timings } ;
117210 } else {
118211 this . _previousResultMap . delete ( request . sessionId ) ;
119212 }
@@ -126,7 +219,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
126219 return { errorDetails : { message : localize ( 'errorResponse' , "Error from provider: {0}" , toErrorMessage ( e ) ) , responseIsIncomplete : true } } ;
127220
128221 } finally {
129- done = true ;
222+ stream . close ( ) ;
130223 commandExecution . complete ( ) ;
131224 }
132225 }
@@ -514,7 +607,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
514607 } satisfies vscode . ChatAgent2 < TResult > ;
515608 }
516609
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 ) ;
610+ invoke ( request : vscode . ChatAgentRequest , context : vscode . ChatAgentContext , response : vscode . ChatAgentExtendedResponseStream , token : CancellationToken ) : vscode . ProviderResult < vscode . ChatAgentResult2 > {
611+ return this . _callback ( request , context , response , token ) ;
519612 }
520613}
0 commit comments