@@ -8,22 +8,143 @@ import { DeferredPromise, raceCancellation } from 'vs/base/common/async';
8
8
import { CancellationToken } from 'vs/base/common/cancellation' ;
9
9
import { toErrorMessage } from 'vs/base/common/errorMessage' ;
10
10
import { Emitter } from 'vs/base/common/event' ;
11
- import { IMarkdownString } from 'vs/base/common/htmlContent' ;
11
+ import { IMarkdownString , MarkdownString } from 'vs/base/common/htmlContent' ;
12
12
import { StopWatch } from 'vs/base/common/stopwatch' ;
13
13
import { URI } from 'vs/base/common/uri' ;
14
14
import { localize } from 'vs/nls' ;
15
15
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' ;
16
16
import { ILogService } from 'vs/platform/log/common/log' ;
17
- import { Progress } from 'vs/platform/progress/common/progress' ;
18
17
import { ExtHostChatAgentsShape2 , IChatAgentCompletionItem , IChatAgentHistoryEntryDto , IMainContext , MainContext , MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol' ;
19
18
import { ExtHostChatProvider } from 'vs/workbench/api/common/extHostChatProvider' ;
20
19
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters' ;
21
20
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes' ;
22
21
import { 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' ;
24
23
import { checkProposedApiEnabled , isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions' ;
24
+ import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier' ;
25
25
import type * as vscode from 'vscode' ;
26
26
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
+
27
148
export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
28
149
29
150
private static _idPool = 0 ;
@@ -61,44 +182,17 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
61
182
throw new Error ( `[CHAT](${ handle } ) CANNOT invoke agent because the agent is not registered` ) ;
62
183
}
63
184
64
- let done = false ;
65
- function throwIfDone ( ) {
66
- if ( done ) {
67
- throw new Error ( 'Only valid while executing the command' ) ;
68
- }
69
- }
70
-
71
185
const commandExecution = new DeferredPromise < void > ( ) ;
72
186
token . onCancellationRequested ( ( ) => commandExecution . complete ( ) ) ;
73
187
this . _extHostChatProvider . allowListExtensionWhile ( agent . extension . identifier , commandExecution . p ) ;
74
188
75
- const stopWatch = StopWatch . create ( false ) ;
76
- let firstProgress : number | undefined ;
189
+ const stream = new ChatAgentResponseStream ( agent . extension , request , this . _proxy , this . _logService ) ;
77
190
try {
78
191
const convertedHistory = await this . prepareHistory ( agent , request , context ) ;
79
192
const task = agent . invoke (
80
193
typeConvert . ChatAgentRequest . to ( request ) ,
81
194
{ 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 ,
102
196
token
103
197
) ;
104
198
@@ -112,8 +206,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
112
206
}
113
207
sessionResults . set ( request . requestId , result ) ;
114
208
115
- const timings = { firstProgress : firstProgress , totalElapsed : stopWatch . elapsed ( ) } ;
116
- return { errorDetails : result . errorDetails , timings } ;
209
+ return { errorDetails : result . errorDetails , timings : stream . timings } ;
117
210
} else {
118
211
this . _previousResultMap . delete ( request . sessionId ) ;
119
212
}
@@ -126,7 +219,7 @@ export class ExtHostChatAgents2 implements ExtHostChatAgentsShape2 {
126
219
return { errorDetails : { message : localize ( 'errorResponse' , "Error from provider: {0}" , toErrorMessage ( e ) ) , responseIsIncomplete : true } } ;
127
220
128
221
} finally {
129
- done = true ;
222
+ stream . close ( ) ;
130
223
commandExecution . complete ( ) ;
131
224
}
132
225
}
@@ -514,7 +607,7 @@ class ExtHostChatAgent<TResult extends vscode.ChatAgentResult2> {
514
607
} satisfies vscode . ChatAgent2 < TResult > ;
515
608
}
516
609
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 ) ;
519
612
}
520
613
}
0 commit comments