3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
- import { assertDefined } from '../../../../../base/common/types.js' ;
7
6
import { localize2 } from '../../../../../nls.js' ;
8
7
import { Action2 , registerAction2 } from '../../../../../platform/actions/common/actions.js' ;
9
- import { ICommandService } from '../../../../../platform/commands/common/commands.js' ;
8
+
10
9
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js' ;
11
- import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js' ;
10
+ import { IInstantiationService , ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js' ;
12
11
import { ILogService } from '../../../../../platform/log/common/log.js' ;
13
12
import { PromptsConfig } from '../../common/promptSyntax/config/config.js' ;
14
- import { IEditorPane } from '../../../../common/editor.js' ;
15
13
import { IEditorService } from '../../../../services/editor/common/editorService.js' ;
16
14
import { ChatContextKeys } from '../../common/chatContextKeys.js' ;
17
15
import { chatSubcommandLeader , IParsedChatRequest } from '../../common/chatParserTypes.js' ;
18
- import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js' ;
19
16
import { PROMPT_LANGUAGE_ID } from '../../common/promptSyntax/promptTypes.js' ;
20
17
import { CHAT_CATEGORY } from '../actions/chatActions.js' ;
21
18
import { IChatWidget } from '../chat.js' ;
22
-
19
+ import { ChatModeKind } from '../../common/constants.js' ;
20
+ import { PromptFileRewriter } from './promptFileRewriter.js' ;
21
+ import { ILanguageModelChatMetadata } from '../../common/languageModels.js' ;
22
+ import { URI } from '../../../../../base/common/uri.js' ;
23
+ import { Schemas } from '../../../../../base/common/network.js' ;
23
24
24
25
/**
25
26
* Action ID for the `Save Prompt` action.
26
27
*/
27
- const SAVE_TO_PROMPT_ACTION_ID = 'workbench.action.chat.save-to-prompt' ;
28
+ export const SAVE_TO_PROMPT_ACTION_ID = 'workbench.action.chat.save-to-prompt' ;
28
29
29
30
/**
30
31
* Name of the in-chat slash command associated with this action.
@@ -38,7 +39,7 @@ interface ISaveToPromptActionOptions {
38
39
/**
39
40
* Chat widget reference to save session of.
40
41
*/
41
- chat : IChatWidget ;
42
+ readonly chat : IChatWidget ;
42
43
}
43
44
44
45
/**
@@ -61,77 +62,69 @@ class SaveToPromptAction extends Action2 {
61
62
public async run (
62
63
accessor : ServicesAccessor ,
63
64
options : ISaveToPromptActionOptions ,
64
- ) : Promise < IEditorPane > {
65
+ ) : Promise < void > {
65
66
const logService = accessor . get ( ILogService ) ;
66
67
const editorService = accessor . get ( IEditorService ) ;
67
- const toolsService = accessor . get ( ILanguageModelToolsService ) ;
68
+ const rewriter = accessor . get ( IInstantiationService ) . createInstance ( PromptFileRewriter ) ;
68
69
69
70
const logPrefix = 'save to prompt' ;
70
- const { chat } = options ;
71
-
72
- const { viewModel } = chat ;
73
- assertDefined (
74
- viewModel ,
75
- 'No view model found on currently the active chat widget.' ,
76
- ) ;
77
-
78
- const { model } = viewModel ;
79
-
80
- const turns : ITurn [ ] = [ ] ;
81
- for ( const request of model . getRequests ( ) ) {
82
- const { message, response : responseModel } = request ;
83
-
84
- if ( isSaveToPromptSlashCommand ( message ) ) {
85
- continue ;
86
- }
87
-
88
- if ( responseModel === undefined ) {
89
- logService . warn (
90
- `[${ logPrefix } ]: skipping request '${ request . id } ' with no response` ,
91
- ) ;
71
+ const chatWidget = options . chat ;
72
+ const mode = chatWidget . input . currentModeObs . get ( ) ;
73
+ const model = chatWidget . input . selectedLanguageModel ;
74
+
75
+ const toolAndToolsetMap = chatWidget . input . selectedToolsModel . entriesMap . get ( ) ;
76
+
77
+ const output = [ ] ;
78
+ output . push ( '---' ) ;
79
+ output . push ( `description: New prompt created from chat session` ) ;
80
+ output . push ( `mode: ${ mode . kind } ` ) ;
81
+ if ( mode . kind === ChatModeKind . Agent ) {
82
+ output . push ( `tools: ${ rewriter . getNewValueString ( toolAndToolsetMap ) } ` ) ;
83
+ }
84
+ if ( model ) {
85
+ output . push ( `model: ${ ILanguageModelChatMetadata . asQualifiedName ( model . metadata ) } ` ) ;
86
+ }
87
+ output . push ( '---' ) ;
92
88
93
- continue ;
94
- }
89
+ const viewModel = chatWidget . viewModel ;
90
+ if ( viewModel ) {
95
91
96
- const { response } = responseModel ;
92
+ for ( const request of viewModel . model . getRequests ( ) ) {
93
+ const { message, response : responseModel } = request ;
97
94
98
- const tools = new Set < string > ( ) ;
99
- for ( const record of response . value ) {
100
- if ( ( 'toolId' in record === false ) || ! record . toolId ) {
95
+ if ( isSaveToPromptSlashCommand ( message ) ) {
101
96
continue ;
102
97
}
103
98
104
- const tool = toolsService . getTool ( record . toolId ) ;
105
- if ( ( tool === undefined ) || ( ! tool . toolReferenceName ) ) {
99
+ if ( responseModel === undefined ) {
100
+ logService . warn ( `[ ${ logPrefix } ]: skipping request ' ${ request . id } ' with no response` ) ;
106
101
continue ;
107
102
}
108
103
109
- tools . add ( tool . toolReferenceName ) ;
110
- }
111
-
112
- turns . push ( {
113
- request : message . text ,
114
- response : response . getMarkdown ( ) ,
115
- tools,
116
- } ) ;
117
- }
104
+ const { response } = responseModel ;
118
105
119
- const promptText = renderPrompt ( turns ) ;
120
-
121
- const editor = await editorService . openEditor ( {
122
- resource : undefined ,
123
- contents : promptText ,
124
- languageId : PROMPT_LANGUAGE_ID ,
125
- } ) ;
106
+ output . push ( `<user>` ) ;
107
+ output . push ( request . message . text ) ;
108
+ output . push ( `</user>` ) ;
109
+ output . push ( ) ;
110
+ output . push ( `<assistant>` ) ;
111
+ output . push ( response . getMarkdown ( ) ) ;
112
+ output . push ( `</assistant>` ) ;
113
+ output . push ( ) ;
114
+ }
115
+ const promptText = output . join ( '\n' ) ;
126
116
127
- assertDefined (
128
- editor ,
129
- 'Failed to open untitled editor for the prompt.' ,
130
- ) ;
117
+ const untitledPath = 'new.prompt.md' ;
118
+ const untitledResource = URI . from ( { scheme : Schemas . untitled , path : untitledPath } ) ;
131
119
132
- editor . focus ( ) ;
120
+ const editor = await editorService . openEditor ( {
121
+ resource : untitledResource ,
122
+ contents : promptText ,
123
+ languageId : PROMPT_LANGUAGE_ID ,
124
+ } ) ;
133
125
134
- return editor ;
126
+ editor ?. focus ( ) ;
127
+ }
135
128
}
136
129
}
137
130
@@ -157,121 +150,6 @@ function isSaveToPromptSlashCommand(message: IParsedChatRequest): boolean {
157
150
return true ;
158
151
}
159
152
160
- /**
161
- * Render the response part of a `request`/`response` turn pair.
162
- */
163
- function renderResponse ( response : string ) : string {
164
- // if response starts with a code block, add an extra new line
165
- // before it, to prevent full blockquote from being be broken
166
- const delimiter = ( response . startsWith ( '```' ) )
167
- ? '\n>'
168
- : ' ' ;
169
-
170
- // add `>` to the beginning of each line of the response
171
- // so it looks like a blockquote citing Copilot
172
- const quotedResponse = response . replaceAll ( '\n' , '\n> ' ) ;
173
-
174
- return `> Copilot:${ delimiter } ${ quotedResponse } ` ;
175
- }
176
-
177
- /**
178
- * Render a single `request`/`response` turn of the chat session.
179
- */
180
- function renderTurn ( turn : ITurn ) : string {
181
- const { request, response } = turn ;
182
-
183
- return `\n${ request } \n\n${ renderResponse ( response ) } ` ;
184
- }
185
-
186
- /**
187
- * Render the entire chat session as a markdown prompt.
188
- */
189
- function renderPrompt ( turns : readonly ITurn [ ] ) : string {
190
- const content : string [ ] = [ ] ;
191
- const allTools = new Set < string > ( ) ;
192
-
193
- // render each turn and collect tool names
194
- // that were used in the each turn
195
- for ( const turn of turns ) {
196
- content . push ( renderTurn ( turn ) ) ;
197
-
198
- // collect all used tools into a set of strings
199
- for ( const tool of turn . tools ) {
200
- allTools . add ( tool ) ;
201
- }
202
- }
203
-
204
- const result = [ ] ;
205
-
206
- // add prompt header
207
- if ( allTools . size !== 0 ) {
208
- result . push ( renderHeader ( allTools ) ) ;
209
- }
210
-
211
- // add chat request/response turns
212
- result . push (
213
- content . join ( '\n' ) ,
214
- ) ;
215
-
216
- // add trailing empty line
217
- result . push ( '' ) ;
218
-
219
- return result . join ( '\n' ) ;
220
- }
221
-
222
-
223
- /**
224
- * Render the `tools` metadata inside prompt header.
225
- */
226
- function renderTools ( tools : Set < string > ) : string {
227
- const toolStrings = [ ...tools ] . map ( ( tool ) => {
228
- return `'${ tool } '` ;
229
- } ) ;
230
-
231
- return `tools: [${ toolStrings . join ( ', ' ) } ]` ;
232
- }
233
-
234
- /**
235
- * Render prompt header.
236
- */
237
- function renderHeader ( tools : Set < string > ) : string {
238
- // skip rendering the header if no tools provided
239
- if ( tools . size === 0 ) {
240
- return '' ;
241
- }
242
-
243
- return [
244
- '---' ,
245
- renderTools ( tools ) ,
246
- '---' ,
247
- ] . join ( '\n' ) ;
248
- }
249
-
250
- /**
251
- * Interface for a single `request`/`response` turn
252
- * of a chat session.
253
- */
254
- interface ITurn {
255
- request : string ;
256
- response : string ;
257
- tools : Set < string > ;
258
- }
259
-
260
- /**
261
- * Runs the `Save To Prompt` action with provided options. We export this
262
- * function instead of {@link SAVE_TO_PROMPT_ACTION_ID} directly to
263
- * encapsulate/enforce the correct options to be passed to the action.
264
- */
265
- export function runSaveToPromptAction (
266
- options : ISaveToPromptActionOptions ,
267
- commandService : ICommandService ,
268
- ) {
269
- return commandService . executeCommand (
270
- SAVE_TO_PROMPT_ACTION_ID ,
271
- options ,
272
- ) ;
273
- }
274
-
275
153
/**
276
154
* Helper to register all the `Save Prompt` actions.
277
155
*/
0 commit comments