3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
-
7
- import { IPromptsService } from '../service/promptsService.js' ;
8
- import { ITextModel } from '../../../../../../editor/common/model.js' ;
6
+ import { CancellationToken } from '../../../../../../base/common/cancellation.js' ;
7
+ import { CharCode } from '../../../../../../base/common/charCode.js' ;
9
8
import { Disposable } from '../../../../../../base/common/lifecycle.js' ;
10
- import { ALL_PROMPTS_LANGUAGE_SELECTOR , getPromptsTypeForLanguageId , PromptsType } from '../promptTypes.js' ;
11
9
import { Position } from '../../../../../../editor/common/core/position.js' ;
12
- import { CancellationToken } from '../../../../../../base/common/cancellation.js' ;
13
- import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js' ;
14
- import { CompletionContext , CompletionItem , CompletionItemInsertTextRule , CompletionItemKind , CompletionItemProvider , CompletionList } from '../../../../../../editor/common/languages.js' ;
15
10
import { Range } from '../../../../../../editor/common/core/range.js' ;
11
+ import { CompletionContext , CompletionItem , CompletionItemInsertTextRule , CompletionItemKind , CompletionItemProvider , CompletionList } from '../../../../../../editor/common/languages.js' ;
12
+ import { ITextModel } from '../../../../../../editor/common/model.js' ;
13
+ import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js' ;
16
14
import { ILanguageModelChatMetadata , ILanguageModelsService } from '../../languageModels.js' ;
15
+ import { ILanguageModelToolsService } from '../../languageModelToolsService.js' ;
16
+ import { InstructionsHeader } from '../parsers/promptHeader/instructionsHeader.js' ;
17
+ import { PromptToolsMetadata } from '../parsers/promptHeader/metadata/tools.js' ;
18
+ import { ModeHeader } from '../parsers/promptHeader/modeHeader.js' ;
19
+ import { PromptHeader } from '../parsers/promptHeader/promptHeader.js' ;
20
+ import { ALL_PROMPTS_LANGUAGE_SELECTOR , getPromptsTypeForLanguageId , PromptsType } from '../promptTypes.js' ;
21
+ import { IPromptsService } from '../service/promptsService.js' ;
17
22
18
23
export class PromptHeaderAutocompletion extends Disposable implements CompletionItemProvider {
19
24
/**
@@ -30,7 +35,7 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
30
35
@IPromptsService private readonly promptsService : IPromptsService ,
31
36
@ILanguageFeaturesService private readonly languageService : ILanguageFeaturesService ,
32
37
@ILanguageModelsService private readonly languageModelsService : ILanguageModelsService ,
33
-
38
+ @ ILanguageModelToolsService private readonly languageModelToolsService : ILanguageModelToolsService ,
34
39
) {
35
40
super ( ) ;
36
41
@@ -61,10 +66,12 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
61
66
return undefined ;
62
67
}
63
68
64
- if ( ! parser . header ) {
69
+ const header = parser . header ;
70
+ if ( ! header ) {
65
71
return undefined ;
66
72
}
67
- await parser . header . settled ;
73
+
74
+ await header . settled ;
68
75
69
76
const fullHeaderRange = parser . header . range ;
70
77
const headerRange = new Range ( fullHeaderRange . startLineNumber + 1 , 0 , fullHeaderRange . endLineNumber - 1 , model . getLineMaxColumn ( fullHeaderRange . endLineNumber - 1 ) , ) ;
@@ -81,7 +88,7 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
81
88
if ( ! colonPosition || position . isBeforeOrEqual ( colonPosition ) ) {
82
89
return this . providePropertyCompletions ( model , position , headerRange , colonPosition , promptType ) ;
83
90
} else if ( colonPosition && colonPosition . isBefore ( position ) ) {
84
- return this . provideValueCompletions ( model , position , headerRange , colonPosition , promptType ) ;
91
+ return this . provideValueCompletions ( model , position , header , colonPosition , promptType ) ;
85
92
}
86
93
return undefined ;
87
94
}
@@ -127,7 +134,7 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
127
134
private async provideValueCompletions (
128
135
model : ITextModel ,
129
136
position : Position ,
130
- headerRange : Range ,
137
+ header : PromptHeader | ModeHeader | InstructionsHeader ,
131
138
colonPosition : Position ,
132
139
promptType : string ,
133
140
) : Promise < CompletionList | undefined > {
@@ -139,6 +146,19 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
139
146
if ( ! this . getSupportedProperties ( promptType ) . has ( property ) ) {
140
147
return undefined ;
141
148
}
149
+
150
+ if ( header instanceof PromptHeader || header instanceof ModeHeader ) {
151
+ const tools = header . metadataUtility . tools ;
152
+ if ( tools ) {
153
+ // if the position is inside the tools metadata, we provide tool name completions
154
+ const result = this . provideToolCompletions ( model , position , tools ) ;
155
+ if ( result ) {
156
+ return result ;
157
+ }
158
+ }
159
+ }
160
+
161
+
142
162
const bracketIndex = lineContent . indexOf ( '[' ) ;
143
163
if ( bracketIndex !== - 1 && bracketIndex <= position . column - 1 ) {
144
164
// if the property is already inside a bracket, we don't provide value completions
@@ -211,4 +231,54 @@ export class PromptHeaderAutocompletion extends Disposable implements Completion
211
231
}
212
232
return result ;
213
233
}
234
+
235
+ private provideToolCompletions ( model : ITextModel , position : Position , node : PromptToolsMetadata ) : CompletionList | undefined {
236
+ const tools = node . value ;
237
+ if ( ! tools || ! node . range . containsPosition ( position ) ) {
238
+ return undefined ;
239
+ }
240
+ const getSuggestions = ( toolRange : Range ) => {
241
+ const suggestions : CompletionItem [ ] = [ ] ;
242
+ const addSuggestion = ( toolName : string , toolRange : Range ) => {
243
+ let insertText : string ;
244
+ if ( ! toolRange . isEmpty ( ) ) {
245
+ const firstChar = model . getValueInRange ( toolRange ) . charCodeAt ( 0 ) ;
246
+ insertText = firstChar === CharCode . SingleQuote ? `'${ toolName } '` : firstChar === CharCode . DoubleQuote ? `"${ toolName } "` : toolName ;
247
+ } else {
248
+ insertText = `'${ toolName } '` ;
249
+ }
250
+ suggestions . push ( {
251
+ label : toolName ,
252
+ kind : CompletionItemKind . Value ,
253
+ filterText : insertText ,
254
+ insertText : insertText ,
255
+ range : toolRange ,
256
+ } ) ;
257
+ } ;
258
+ for ( const tool of this . languageModelToolsService . getTools ( ) ) {
259
+ if ( tool . canBeReferencedInPrompt ) {
260
+ addSuggestion ( tool . toolReferenceName ?? tool . displayName , toolRange ) ;
261
+ }
262
+ }
263
+ for ( const toolSet of this . languageModelToolsService . toolSets . get ( ) ) {
264
+ addSuggestion ( toolSet . referenceName , toolRange ) ;
265
+ }
266
+ return { suggestions } ;
267
+ } ;
268
+
269
+ for ( const tool of tools ) {
270
+ const toolRange = node . getToolRange ( tool ) ;
271
+ if ( toolRange ?. containsPosition ( position ) ) {
272
+ // if the position is inside a tool range, we provide tool name completions
273
+ return getSuggestions ( toolRange ) ;
274
+ }
275
+ }
276
+ const prefix = model . getValueInRange ( new Range ( position . lineNumber , 1 , position . lineNumber , position . column ) ) ;
277
+ if ( prefix . match ( / [ , [ ] \s * $ / ) ) {
278
+ // if the position is after a comma or bracket
279
+ return getSuggestions ( new Range ( position . lineNumber , position . column , position . lineNumber , position . column ) ) ;
280
+ }
281
+ return undefined ;
282
+ }
283
+
214
284
}
0 commit comments