@@ -21,6 +21,7 @@ import {
21
21
docsRequestChatResult ,
22
22
schemaRequestChatResult ,
23
23
createCancelledRequestChatResult ,
24
+ codeBlockIdentifier ,
24
25
} from './constants' ;
25
26
import { SchemaFormatter } from './schema' ;
26
27
import { getSimplifiedSampleDocuments } from './sampleDocuments' ;
@@ -38,7 +39,8 @@ import {
38
39
} from '../telemetry/telemetryService' ;
39
40
import { DocsChatbotAIService } from './docsChatbotAIService' ;
40
41
import type TelemetryService from '../telemetry/telemetryService' ;
41
- import { IntentPrompt , type PromptIntent } from './prompts/intent' ;
42
+ import { processStreamWithIdentifiers } from './streamParsing' ;
43
+ import type { PromptIntent } from './prompts/intent' ;
42
44
43
45
const log = createLogger ( 'participant' ) ;
44
46
@@ -59,16 +61,6 @@ export type ParticipantCommand = '/query' | '/schema' | '/docs';
59
61
60
62
const MAX_MARKDOWN_LIST_LENGTH = 10 ;
61
63
62
- export function getRunnableContentFromString ( text : string ) : string {
63
- const matchedJSresponseContent = text . match ( / ` ` ` j a v a s c r i p t ( ( .| \n ) * ) ` ` ` / ) ;
64
-
65
- const code =
66
- matchedJSresponseContent && matchedJSresponseContent . length > 1
67
- ? matchedJSresponseContent [ 1 ]
68
- : '' ;
69
- return code . trim ( ) ;
70
- }
71
-
72
64
export default class ParticipantController {
73
65
_participant ?: vscode . ChatParticipant ;
74
66
_connectionController : ConnectionController ;
@@ -171,48 +163,113 @@ export default class ParticipantController {
171
163
} ) ;
172
164
}
173
165
174
- async getChatResponseContent ( {
166
+ async _getChatResponse ( {
175
167
messages,
176
168
token,
177
169
} : {
178
170
messages : vscode . LanguageModelChatMessage [ ] ;
179
171
token : vscode . CancellationToken ;
180
- } ) : Promise < string > {
172
+ } ) : Promise < vscode . LanguageModelChatResponse > {
181
173
const model = await getCopilotModel ( ) ;
182
- let responseContent = '' ;
183
- if ( model ) {
184
- const chatResponse = await model . sendRequest ( messages , { } , token ) ;
185
- for await ( const fragment of chatResponse . text ) {
186
- responseContent += fragment ;
187
- }
174
+
175
+ if ( ! model ) {
176
+ throw new Error ( 'Copilot model not found' ) ;
188
177
}
189
178
190
- return responseContent ;
179
+ return await model . sendRequest ( messages , { } , token ) ;
191
180
}
192
181
193
- _streamRunnableContentActions ( {
194
- responseContent ,
182
+ async streamChatResponse ( {
183
+ messages ,
195
184
stream,
185
+ token,
196
186
} : {
197
- responseContent : string ;
187
+ messages : vscode . LanguageModelChatMessage [ ] ;
188
+ stream : vscode . ChatResponseStream ;
189
+ token : vscode . CancellationToken ;
190
+ } ) : Promise < void > {
191
+ const chatResponse = await this . _getChatResponse ( {
192
+ messages,
193
+ token,
194
+ } ) ;
195
+ for await ( const fragment of chatResponse . text ) {
196
+ stream . markdown ( fragment ) ;
197
+ }
198
+ }
199
+
200
+ _streamCodeBlockActions ( {
201
+ runnableContent,
202
+ stream,
203
+ } : {
204
+ runnableContent : string ;
198
205
stream : vscode . ChatResponseStream ;
199
206
} ) : void {
200
- const runnableContent = getRunnableContentFromString ( responseContent ) ;
201
- if ( runnableContent ) {
202
- const commandArgs : RunParticipantQueryCommandArgs = {
203
- runnableContent,
204
- } ;
205
- stream . button ( {
206
- command : EXTENSION_COMMANDS . RUN_PARTICIPANT_QUERY ,
207
- title : vscode . l10n . t ( '▶️ Run' ) ,
208
- arguments : [ commandArgs ] ,
209
- } ) ;
210
- stream . button ( {
211
- command : EXTENSION_COMMANDS . OPEN_PARTICIPANT_QUERY_IN_PLAYGROUND ,
212
- title : vscode . l10n . t ( 'Open in playground' ) ,
213
- arguments : [ commandArgs ] ,
214
- } ) ;
207
+ runnableContent = runnableContent . trim ( ) ;
208
+
209
+ if ( ! runnableContent ) {
210
+ return ;
215
211
}
212
+
213
+ const commandArgs : RunParticipantQueryCommandArgs = {
214
+ runnableContent,
215
+ } ;
216
+ stream . button ( {
217
+ command : EXTENSION_COMMANDS . RUN_PARTICIPANT_QUERY ,
218
+ title : vscode . l10n . t ( '▶️ Run' ) ,
219
+ arguments : [ commandArgs ] ,
220
+ } ) ;
221
+ stream . button ( {
222
+ command : EXTENSION_COMMANDS . OPEN_PARTICIPANT_QUERY_IN_PLAYGROUND ,
223
+ title : vscode . l10n . t ( 'Open in playground' ) ,
224
+ arguments : [ commandArgs ] ,
225
+ } ) ;
226
+ }
227
+
228
+ async streamChatResponseContentWithCodeActions ( {
229
+ messages,
230
+ stream,
231
+ token,
232
+ } : {
233
+ messages : vscode . LanguageModelChatMessage [ ] ;
234
+ stream : vscode . ChatResponseStream ;
235
+ token : vscode . CancellationToken ;
236
+ } ) : Promise < void > {
237
+ const chatResponse = await this . _getChatResponse ( {
238
+ messages,
239
+ token,
240
+ } ) ;
241
+
242
+ await processStreamWithIdentifiers ( {
243
+ processStreamFragment : ( fragment : string ) => {
244
+ stream . markdown ( fragment ) ;
245
+ } ,
246
+ onStreamIdentifier : ( content : string ) => {
247
+ this . _streamCodeBlockActions ( { runnableContent : content , stream } ) ;
248
+ } ,
249
+ inputIterable : chatResponse . text ,
250
+ identifier : codeBlockIdentifier ,
251
+ } ) ;
252
+ }
253
+
254
+ // This will stream all of the response content and create a string from it.
255
+ // It should only be used when the entire response is needed at one time.
256
+ async getChatResponseContent ( {
257
+ messages,
258
+ token,
259
+ } : {
260
+ messages : vscode . LanguageModelChatMessage [ ] ;
261
+ token : vscode . CancellationToken ;
262
+ } ) : Promise < string > {
263
+ let responseContent = '' ;
264
+ const chatResponse = await this . _getChatResponse ( {
265
+ messages,
266
+ token,
267
+ } ) ;
268
+ for await ( const fragment of chatResponse . text ) {
269
+ responseContent += fragment ;
270
+ }
271
+
272
+ return responseContent ;
216
273
}
217
274
218
275
async _handleRoutedGenericRequest (
@@ -227,14 +284,9 @@ export default class ParticipantController {
227
284
connectionNames : this . _getConnectionNames ( ) ,
228
285
} ) ;
229
286
230
- const responseContent = await this . getChatResponseContent ( {
287
+ await this . streamChatResponseContentWithCodeActions ( {
231
288
messages,
232
289
token,
233
- } ) ;
234
- stream . markdown ( responseContent ) ;
235
-
236
- this . _streamRunnableContentActions ( {
237
- responseContent,
238
290
stream,
239
291
} ) ;
240
292
@@ -293,7 +345,7 @@ export default class ParticipantController {
293
345
token,
294
346
} ) ;
295
347
296
- return IntentPrompt . getIntentFromModelResponse ( responseContent ) ;
348
+ return Prompts . intent . getIntentFromModelResponse ( responseContent ) ;
297
349
}
298
350
299
351
async handleGenericRequest (
@@ -1001,11 +1053,11 @@ export default class ParticipantController {
1001
1053
connectionNames : this . _getConnectionNames ( ) ,
1002
1054
...( sampleDocuments ? { sampleDocuments } : { } ) ,
1003
1055
} ) ;
1004
- const responseContent = await this . getChatResponseContent ( {
1056
+ await this . streamChatResponse ( {
1005
1057
messages,
1058
+ stream,
1006
1059
token,
1007
1060
} ) ;
1008
- stream . markdown ( responseContent ) ;
1009
1061
1010
1062
stream . button ( {
1011
1063
command : EXTENSION_COMMANDS . PARTICIPANT_OPEN_RAW_SCHEMA_OUTPUT ,
@@ -1104,16 +1156,11 @@ export default class ParticipantController {
1104
1156
connectionNames : this . _getConnectionNames ( ) ,
1105
1157
...( sampleDocuments ? { sampleDocuments } : { } ) ,
1106
1158
} ) ;
1107
- const responseContent = await this . getChatResponseContent ( {
1108
- messages,
1109
- token,
1110
- } ) ;
1111
-
1112
- stream . markdown ( responseContent ) ;
1113
1159
1114
- this . _streamRunnableContentActions ( {
1115
- responseContent ,
1160
+ await this . streamChatResponseContentWithCodeActions ( {
1161
+ messages ,
1116
1162
stream,
1163
+ token,
1117
1164
} ) ;
1118
1165
1119
1166
return queryRequestChatResult ( context . history ) ;
@@ -1181,32 +1228,41 @@ export default class ParticipantController {
1181
1228
vscode . ChatResponseStream ,
1182
1229
vscode . CancellationToken
1183
1230
]
1184
- ) : Promise < {
1185
- responseContent : string ;
1186
- responseReferences ?: Reference [ ] ;
1187
- } > {
1188
- const [ request , context , , token ] = args ;
1231
+ ) : Promise < void > {
1232
+ const [ request , context , stream , token ] = args ;
1189
1233
const messages = await Prompts . generic . buildMessages ( {
1190
1234
request,
1191
1235
context,
1192
1236
connectionNames : this . _getConnectionNames ( ) ,
1193
1237
} ) ;
1194
1238
1195
- const responseContent = await this . getChatResponseContent ( {
1239
+ await this . streamChatResponseContentWithCodeActions ( {
1196
1240
messages,
1241
+ stream,
1197
1242
token,
1198
1243
} ) ;
1199
- const responseReferences = [
1200
- {
1244
+
1245
+ this . _streamResponseReference ( {
1246
+ reference : {
1201
1247
url : MONGODB_DOCS_LINK ,
1202
1248
title : 'View MongoDB documentation' ,
1203
1249
} ,
1204
- ] ;
1250
+ stream,
1251
+ } ) ;
1252
+ }
1205
1253
1206
- return {
1207
- responseContent,
1208
- responseReferences,
1209
- } ;
1254
+ _streamResponseReference ( {
1255
+ reference,
1256
+ stream,
1257
+ } : {
1258
+ reference : Reference ;
1259
+ stream : vscode . ChatResponseStream ;
1260
+ } ) : void {
1261
+ const link = new vscode . MarkdownString (
1262
+ `- [${ reference . title } ](${ reference . url } )\n`
1263
+ ) ;
1264
+ link . supportHtml = true ;
1265
+ stream . markdown ( link ) ;
1210
1266
}
1211
1267
1212
1268
async handleDocsRequest (
@@ -1235,6 +1291,19 @@ export default class ParticipantController {
1235
1291
token,
1236
1292
stream,
1237
1293
} ) ;
1294
+
1295
+ if ( docsResult . responseReferences ) {
1296
+ for ( const reference of docsResult . responseReferences ) {
1297
+ this . _streamResponseReference ( {
1298
+ reference,
1299
+ stream,
1300
+ } ) ;
1301
+ }
1302
+ }
1303
+
1304
+ if ( docsResult . responseContent ) {
1305
+ stream . markdown ( docsResult . responseContent ) ;
1306
+ }
1238
1307
} catch ( error ) {
1239
1308
// If the docs chatbot API is not available, fall back to Copilot’s LLM and include
1240
1309
// the MongoDB documentation link for users to go to our documentation site directly.
@@ -1255,25 +1324,7 @@ export default class ParticipantController {
1255
1324
}
1256
1325
) ;
1257
1326
1258
- docsResult = await this . _handleDocsRequestWithCopilot ( ...args ) ;
1259
- }
1260
-
1261
- if ( docsResult . responseContent ) {
1262
- stream . markdown ( docsResult . responseContent ) ;
1263
- this . _streamRunnableContentActions ( {
1264
- responseContent : docsResult . responseContent ,
1265
- stream,
1266
- } ) ;
1267
- }
1268
-
1269
- if ( docsResult . responseReferences ) {
1270
- for ( const ref of docsResult . responseReferences ) {
1271
- const link = new vscode . MarkdownString (
1272
- `- [${ ref . title } ](${ ref . url } )\n`
1273
- ) ;
1274
- link . supportHtml = true ;
1275
- stream . markdown ( link ) ;
1276
- }
1327
+ await this . _handleDocsRequestWithCopilot ( ...args ) ;
1277
1328
}
1278
1329
1279
1330
return docsRequestChatResult ( {
0 commit comments