@theia/ai-core 1.61.0 → 1.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/lib/browser/ai-core-frontend-module.js +2 -2
  2. package/lib/browser/ai-core-frontend-module.js.map +1 -1
  3. package/lib/browser/frontend-language-model-service.d.ts +1 -1
  4. package/lib/browser/frontend-language-model-service.d.ts.map +1 -1
  5. package/lib/browser/frontend-language-model-service.js.map +1 -1
  6. package/lib/browser/frontend-prompt-customization-service.d.ts +142 -48
  7. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
  8. package/lib/browser/frontend-prompt-customization-service.js +452 -153
  9. package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
  10. package/lib/browser/prompttemplate-contribution.d.ts +1 -2
  11. package/lib/browser/prompttemplate-contribution.d.ts.map +1 -1
  12. package/lib/browser/prompttemplate-contribution.js +5 -9
  13. package/lib/browser/prompttemplate-contribution.js.map +1 -1
  14. package/lib/common/agent-service.d.ts.map +1 -1
  15. package/lib/common/agent-service.js +14 -2
  16. package/lib/common/agent-service.js.map +1 -1
  17. package/lib/common/agent.d.ts +8 -3
  18. package/lib/common/agent.d.ts.map +1 -1
  19. package/lib/common/agent.js.map +1 -1
  20. package/lib/common/index.d.ts +0 -1
  21. package/lib/common/index.d.ts.map +1 -1
  22. package/lib/common/index.js +0 -1
  23. package/lib/common/index.js.map +1 -1
  24. package/lib/common/language-model-interaction-model.d.ts +74 -0
  25. package/lib/common/language-model-interaction-model.d.ts.map +1 -0
  26. package/lib/common/language-model-interaction-model.js +3 -0
  27. package/lib/common/language-model-interaction-model.js.map +1 -0
  28. package/lib/common/language-model-service.d.ts +26 -3
  29. package/lib/common/language-model-service.d.ts.map +1 -1
  30. package/lib/common/language-model-service.js +83 -6
  31. package/lib/common/language-model-service.js.map +1 -1
  32. package/lib/common/language-model-util.d.ts +3 -2
  33. package/lib/common/language-model-util.d.ts.map +1 -1
  34. package/lib/common/language-model-util.js +8 -0
  35. package/lib/common/language-model-util.js.map +1 -1
  36. package/lib/common/language-model.d.ts +25 -2
  37. package/lib/common/language-model.d.ts.map +1 -1
  38. package/lib/common/language-model.js +3 -1
  39. package/lib/common/language-model.js.map +1 -1
  40. package/lib/common/prompt-service.d.ts +332 -126
  41. package/lib/common/prompt-service.d.ts.map +1 -1
  42. package/lib/common/prompt-service.js +363 -102
  43. package/lib/common/prompt-service.js.map +1 -1
  44. package/lib/common/prompt-service.spec.js +104 -114
  45. package/lib/common/prompt-service.spec.js.map +1 -1
  46. package/lib/common/prompt-variable-contribution.d.ts +1 -2
  47. package/lib/common/prompt-variable-contribution.d.ts.map +1 -1
  48. package/lib/common/prompt-variable-contribution.js +17 -26
  49. package/lib/common/prompt-variable-contribution.js.map +1 -1
  50. package/package.json +10 -10
  51. package/src/browser/ai-core-frontend-module.ts +4 -4
  52. package/src/browser/frontend-language-model-service.ts +1 -1
  53. package/src/browser/frontend-prompt-customization-service.ts +574 -183
  54. package/src/browser/prompttemplate-contribution.ts +6 -9
  55. package/src/common/agent-service.ts +14 -4
  56. package/src/common/agent.ts +9 -3
  57. package/src/common/index.ts +0 -1
  58. package/src/common/language-model-interaction-model.ts +98 -0
  59. package/src/common/language-model-service.ts +115 -6
  60. package/src/common/language-model-util.ts +10 -2
  61. package/src/common/language-model.ts +28 -2
  62. package/src/common/prompt-service.spec.ts +108 -114
  63. package/src/common/prompt-service.ts +694 -221
  64. package/src/common/prompt-variable-contribution.ts +22 -27
  65. package/lib/common/communication-recording-service.d.ts +0 -30
  66. package/lib/common/communication-recording-service.d.ts.map +0 -1
  67. package/lib/common/communication-recording-service.js +0 -20
  68. package/lib/common/communication-recording-service.js.map +0 -1
  69. package/src/common/communication-recording-service.ts +0 -55
@@ -14,8 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { URI, Event } from '@theia/core';
18
- import { inject, injectable, optional } from '@theia/core/shared/inversify';
17
+ import { Event, Emitter, URI } from '@theia/core';
18
+ import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
19
19
  import { AIVariableArg, AIVariableContext, AIVariableService, createAIResolveVariableCache, ResolvedAIVariable } from './variable-service';
20
20
  import { ToolInvocationRegistry } from './tool-invocation-registry';
21
21
  import { toolRequestToPromptText } from './language-model-util';
@@ -23,189 +23,242 @@ import { ToolRequest } from './language-model';
23
23
  import { matchFunctionsRegEx, matchVariablesRegEx } from './prompt-service-util';
24
24
  import { AISettingsService } from './settings-service';
25
25
 
26
- export interface PromptTemplate {
26
+ /**
27
+ * Represents a basic prompt fragment with an ID and template content.
28
+ */
29
+ export interface BasePromptFragment {
30
+ /** Unique identifier for this prompt fragment */
27
31
  id: string;
32
+
33
+ /** The template content, which may contain variables and function references */
28
34
  template: string;
35
+ }
36
+
37
+ /**
38
+ * Represents a customized prompt fragment with an assigned customization ID and priority.
39
+ */
40
+ export interface CustomizedPromptFragment extends BasePromptFragment {
29
41
  /**
30
- * (Optional) The ID of the main template for which this template is a variant.
31
- * If present, this indicates that the current template represents an alternative version of the specified main template.
42
+ * Unique identifier for this customization
32
43
  */
33
- variantOf?: string;
44
+ customizationId: string;
45
+
46
+ /**
47
+ * The order/priority of this customization, higher values indicate higher priority
48
+ * when multiple customizations exist for the same fragment
49
+ */
50
+ priority: number;
51
+ }
52
+
53
+ /**
54
+ * Union type representing either a built-in or customized prompt fragment
55
+ */
56
+ export type PromptFragment = BasePromptFragment | CustomizedPromptFragment;
57
+
58
+ /**
59
+ * Type guard to check if a PromptFragment is a built-in fragment (not customized)
60
+ * @param fragment The fragment to check
61
+ * @returns True if the fragment is a basic BasePromptFragment (not customized)
62
+ */
63
+ export function isBasePromptFragment(fragment: PromptFragment): fragment is BasePromptFragment {
64
+ return !('customizationId' in fragment && 'priority' in fragment);
34
65
  }
35
66
 
36
- export interface PromptMap { [id: string]: PromptTemplate }
67
+ /**
68
+ * Type guard to check if a PromptFragment is a CustomizedPromptFragment
69
+ * @param fragment The fragment to check
70
+ * @returns True if the fragment is a CustomizedPromptFragment
71
+ */
72
+ export function isCustomizedPromptFragment(fragment: PromptFragment): fragment is CustomizedPromptFragment {
73
+ return 'customizationId' in fragment && 'priority' in fragment;
74
+ }
37
75
 
38
- export interface ResolvedPromptTemplate {
76
+ /**
77
+ * Map of prompt fragment IDs to prompt fragments
78
+ */
79
+ export interface PromptMap { [id: string]: PromptFragment }
80
+
81
+ /**
82
+ * Represents a prompt fragment with all variables and function references resolved
83
+ */
84
+ export interface ResolvedPromptFragment {
85
+ /** The fragment ID */
39
86
  id: string;
40
- /** The resolved prompt text with variables and function requests being replaced. */
87
+
88
+ /** The resolved prompt text with variables and function requests being replaced */
41
89
  text: string;
42
- /** All functions referenced in the prompt template. */
90
+
91
+ /** All functions referenced in the prompt fragment */
43
92
  functionDescriptions?: Map<string, ToolRequest>;
44
- /** All variables resolved in the prompt template */
93
+
94
+ /** All variables resolved in the prompt fragment */
45
95
  variables?: ResolvedAIVariable[];
46
96
  }
47
97
 
48
- export const PromptService = Symbol('PromptService');
49
- export interface PromptService {
98
+ /**
99
+ * Describes a custom agent with its properties
100
+ */
101
+ export interface CustomAgentDescription {
102
+ /** Unique identifier for this agent */
103
+ id: string;
104
+
105
+ /** Display name for the agent */
106
+ name: string;
107
+
108
+ /** Description of the agent's purpose and capabilities */
109
+ description: string;
110
+
111
+ /** The prompt text for this agent */
112
+ prompt: string;
113
+
114
+ /** The default large language model to use with this agent */
115
+ defaultLLM: string;
116
+ }
117
+
118
+ export namespace CustomAgentDescription {
50
119
  /**
51
- * Retrieve the raw {@link PromptTemplate} object (unresolved variables, functions and including comments).
52
- * @param id the id of the {@link PromptTemplate}
120
+ * Type guard to check if an object is a CustomAgentDescription
53
121
  */
54
- getRawPrompt(id: string): PromptTemplate | undefined;
122
+ export function is(entry: unknown): entry is CustomAgentDescription {
123
+ // eslint-disable-next-line no-null/no-null
124
+ return typeof entry === 'object' && entry !== null
125
+ && 'id' in entry && typeof entry.id === 'string'
126
+ && 'name' in entry && typeof entry.name === 'string'
127
+ && 'description' in entry && typeof entry.description === 'string'
128
+ && 'prompt' in entry && typeof entry.prompt === 'string'
129
+ && 'defaultLLM' in entry && typeof entry.defaultLLM === 'string';
130
+ }
131
+
55
132
  /**
56
- * Retrieve the unresolved {@link PromptTemplate} object (unresolved variables, functions, excluding comments)
57
- * @param id the id of the {@link PromptTemplate}
133
+ * Compares two CustomAgentDescription objects for equality
58
134
  */
59
- getUnresolvedPrompt(id: string): PromptTemplate | undefined;
135
+ export function equals(a: CustomAgentDescription, b: CustomAgentDescription): boolean {
136
+ return a.id === b.id && a.name === b.name && a.description === b.description && a.prompt === b.prompt && a.defaultLLM === b.defaultLLM;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Service responsible for customizing prompt fragments
142
+ */
143
+ export const PromptFragmentCustomizationService = Symbol('PromptFragmentCustomizationService');
144
+ export interface PromptFragmentCustomizationService {
60
145
  /**
61
- * Retrieve the default raw {@link PromptTemplate} object.
62
- * @param id the id of the {@link PromptTemplate}
146
+ * Event fired when a prompt fragment is changed
63
147
  */
64
- getDefaultRawPrompt(id: string): PromptTemplate | undefined;
148
+ readonly onDidChangePromptFragmentCustomization: Event<string[]>;
149
+
65
150
  /**
66
- * Allows to directly replace placeholders in the prompt. The supported format is 'Hi {{name}}!'.
67
- * The placeholder is then searched inside the args object and replaced.
68
- * Function references are also supported via format '~{functionId}'.
69
- *
70
- * All placeholders are replaced before function references are resolved.
71
- * This allows to resolve function references contained in placeholders.
72
- *
73
- * @param id the id of the prompt
74
- * @param args the object with placeholders, mapping the placeholder key to the value
151
+ * Event fired when custom agents are modified
75
152
  */
76
- getPrompt(id: string, args?: { [key: string]: unknown }, context?: AIVariableContext): Promise<ResolvedPromptTemplate | undefined>;
153
+ readonly onDidChangeCustomAgents: Event<void>;
77
154
 
78
155
  /**
79
- * Allows to directly replace placeholders in the prompt. The supported format is 'Hi {{name}}!'.
80
- * The placeholder is then searched inside the args object and replaced.
81
- *
82
- * In contrast to {@link getPrompt}, this method does not resolve function references but leaves them as is.
83
- * This allows resolving them later as part of the prompt or chat message containing the fragment.
84
- *
85
- * @param id the id of the prompt
86
- * @param args the object with placeholders, mapping the placeholder key to the value
87
- * @param context the {@link AIVariableContext} to use during variable resolution
88
- * @param resolveVariable the variable resolving method. Fall back to using the {@link AIVariableService} if not given.
156
+ * Checks if a prompt fragment has customizations
157
+ * @param fragmentId The prompt fragment ID
158
+ * @returns Whether the fragment has any customizations
89
159
  */
90
- getPromptFragment(
91
- id: string,
92
- args?: { [key: string]: unknown },
93
- context?: AIVariableContext,
94
- resolveVariable?: (variable: AIVariableArg) => Promise<ResolvedAIVariable | undefined>
95
- ): Promise<Omit<ResolvedPromptTemplate, 'functionDescriptions'> | undefined>;
160
+ isPromptFragmentCustomized(fragmentId: string): boolean;
96
161
 
97
162
  /**
98
- * Adds a {@link PromptTemplate} to the list of prompts.
99
- * @param promptTemplate the prompt template to store
163
+ * Gets the active customized prompt fragment for a given ID
164
+ * @param fragmentId The prompt fragment ID
165
+ * @returns The active customized fragment or undefined if none exists
100
166
  */
101
- storePromptTemplate(promptTemplate: PromptTemplate): void;
167
+ getActivePromptFragmentCustomization(fragmentId: string): CustomizedPromptFragment | undefined;
168
+
102
169
  /**
103
- * Removes a prompt from the list of prompts.
104
- * @param id the id of the prompt
170
+ * Gets all customizations for a prompt fragment ordered by priority
171
+ * @param fragmentId The prompt fragment ID
172
+ * @returns Array of customized fragments ordered by priority (highest first)
105
173
  */
106
- removePrompt(id: string): void;
174
+ getAllCustomizations(fragmentId: string): CustomizedPromptFragment[];
175
+
107
176
  /**
108
- * Return all known prompts as a {@link PromptMap map}.
177
+ * Gets the IDs of all prompt fragments that have customizations
178
+ * @returns Array of prompt fragment IDs
109
179
  */
110
- getAllPrompts(): PromptMap;
180
+ getCustomizedPromptFragmentIds(): string[];
181
+
111
182
  /**
112
- * Retrieve all variant IDs of a given {@link PromptTemplate}.
113
- * @param id the id of the main {@link PromptTemplate}
114
- * @returns an array of string IDs representing the variants of the given template
183
+ * Creates a new customization for a prompt fragment
184
+ * @param fragmentId The fragment ID to customize
185
+ * @param defaultContent Optional default content for the customization
115
186
  */
116
- getVariantIds(id: string): string[];
187
+ createPromptFragmentCustomization(fragmentId: string, defaultContent?: string): Promise<void>;
188
+
117
189
  /**
118
- * Retrieve the currently selected variant ID for a given main prompt ID.
119
- * If a variant is selected for the main prompt, it will be returned.
120
- * Otherwise, the main prompt ID will be returned.
121
- * @param id the id of the main prompt
122
- * @returns the variant ID if one is selected, or the main prompt ID otherwise
190
+ * Creates a customization based on a built-in fragment
191
+ * @param fragmentId The ID of the built-in fragment to customize
192
+ * @param defaultContent Optional default content for the customization
123
193
  */
124
- getVariantId(id: string): Promise<string>;
125
- }
194
+ createBuiltInPromptFragmentCustomization(fragmentId: string, defaultContent?: string): Promise<void>;
126
195
 
127
- export interface CustomAgentDescription {
128
- id: string;
129
- name: string;
130
- description: string;
131
- prompt: string;
132
- defaultLLM: string;
133
- }
134
- export namespace CustomAgentDescription {
135
- export function is(entry: unknown): entry is CustomAgentDescription {
136
- // eslint-disable-next-line no-null/no-null
137
- return typeof entry === 'object' && entry !== null
138
- && 'id' in entry && typeof entry.id === 'string'
139
- && 'name' in entry && typeof entry.name === 'string'
140
- && 'description' in entry && typeof entry.description === 'string'
141
- && 'prompt' in entry
142
- && typeof entry.prompt === 'string'
143
- && 'defaultLLM' in entry
144
- && typeof entry.defaultLLM === 'string';
145
- }
146
- export function equals(a: CustomAgentDescription, b: CustomAgentDescription): boolean {
147
- return a.id === b.id && a.name === b.name && a.description === b.description && a.prompt === b.prompt && a.defaultLLM === b.defaultLLM;
148
- }
149
- }
196
+ /**
197
+ * Edits a specific customization of a prompt fragment
198
+ * @param fragmentId The prompt fragment ID
199
+ * @param customizationId The customization ID to edit
200
+ */
201
+ editPromptFragmentCustomization(fragmentId: string, customizationId: string): Promise<void>;
150
202
 
151
- export const PromptCustomizationService = Symbol('PromptCustomizationService');
152
- export interface PromptCustomizationService {
153
203
  /**
154
- * Whether there is a customization for a {@link PromptTemplate} object
155
- * @param id the id of the {@link PromptTemplate} to check
204
+ * Edits the built-in customization of a prompt fragment
205
+ * @param fragmentId The prompt fragment ID to edit
206
+ * @param defaultContent Optional default content for the customization
156
207
  */
157
- isPromptTemplateCustomized(id: string): boolean;
208
+ editBuiltInPromptFragmentCustomization(fragmentId: string, defaultContent?: string): Promise<void>;
158
209
 
159
210
  /**
160
- * Returns the customization of {@link PromptTemplate} object or undefined if there is none
161
- * @param id the id of the {@link PromptTemplate} to check
211
+ * Removes a specific customization of a prompt fragment
212
+ * @param fragmentId The prompt fragment ID
213
+ * @param customizationId The customization ID to remove
162
214
  */
163
- getCustomizedPromptTemplate(id: string): string | undefined
215
+ removePromptFragmentCustomization(fragmentId: string, customizationId: string): Promise<void>;
164
216
 
165
- getCustomPromptTemplateIDs(): string[];
166
217
  /**
167
- * Edit the template. If the content is specified, is will be
168
- * used to customize the template. Otherwise, the behavior depends
169
- * on the implementation. Implementation may for example decide to
170
- * open an editor, or request more information from the user, ...
171
- * @param id the template id.
172
- * @param content optional default content to initialize the template
218
+ * Resets a fragment to its built-in version by removing all customizations
219
+ * @param fragmentId The fragment ID to reset
173
220
  */
174
- editTemplate(id: string, defaultContent?: string): void;
221
+ removeAllPromptFragmentCustomizations(fragmentId: string): Promise<void>;
175
222
 
176
223
  /**
177
- * Reset the template to its default value.
178
- * @param id the template id.
224
+ * Resets to a specific customization by removing higher-priority customizations
225
+ * @param fragmentId The fragment ID
226
+ * @param customizationId The customization ID to reset to
179
227
  */
180
- resetTemplate(id: string): void;
228
+ resetToCustomization(fragmentId: string, customizationId: string): Promise<void>;
181
229
 
182
230
  /**
183
- * Return the template id for a given template file.
184
- * @param uri the uri of the template file
231
+ * Gets information about the description of a customization
232
+ * @param fragmentId The fragment ID
233
+ * @param customizationId The customization ID
234
+ * @returns Description of the customization
185
235
  */
186
- getTemplateIDFromURI(uri: URI): string | undefined;
236
+ getPromptFragmentCustomizationDescription(fragmentId: string, customizationId: string): Promise<string | undefined>;
187
237
 
188
238
  /**
189
- * Event which is fired when the prompt template is changed.
239
+ * Gets information about the source/type of a customization
240
+ * @param fragmentId The fragment ID
241
+ * @param customizationId The customization ID
242
+ * @returns Type of the customization source
190
243
  */
191
- readonly onDidChangePrompt: Event<string>;
244
+ getPromptFragmentCustomizationType(fragmentId: string, customizationId: string): Promise<string | undefined>;
192
245
 
193
246
  /**
194
- * Return all custom agents.
195
- * @returns all custom agents
247
+ * Gets the fragment ID from a resource identifier
248
+ * @param resourceId Resource identifier (implementation specific)
249
+ * @returns Fragment ID or undefined if not found
196
250
  */
197
- getCustomAgents(): Promise<CustomAgentDescription[]>;
251
+ getPromptFragmentIDFromResource(resourceId: unknown): string | undefined;
198
252
 
199
253
  /**
200
- * Event which is fired when custom agents are modified.
254
+ * Gets all custom agent descriptions
255
+ * @returns Array of custom agent descriptions
201
256
  */
202
- readonly onDidChangeCustomAgents: Event<void>;
257
+ getCustomAgents(): Promise<CustomAgentDescription[]>;
203
258
 
204
259
  /**
205
- * Returns all locations of existing customAgents.yml files and potential locations where
206
- * new customAgents.yml files could be created.
207
- *
208
- * @returns An array of objects containing the URI and whether the file exists
260
+ * Gets the locations of custom agent configuration files
261
+ * @returns Array of URIs and existence status
209
262
  */
210
263
  getCustomAgentsLocations(): Promise<{ uri: URI, exists: boolean }[]>;
211
264
 
@@ -217,13 +270,149 @@ export interface PromptCustomizationService {
217
270
  openCustomAgentYaml(uri: URI): Promise<void>;
218
271
  }
219
272
 
273
+ /**
274
+ * Service for managing and resolving prompt fragments
275
+ */
276
+ export const PromptService = Symbol('PromptService');
277
+ export interface PromptService {
278
+ /**
279
+ * Event fired when the prompts change
280
+ */
281
+ readonly onPromptsChange: Event<void>;
282
+
283
+ /**
284
+ * Event fired when the selected variant for a prompt variant set changes
285
+ */
286
+ readonly onSelectedVariantChange: Event<{ promptVariantSetId: string, variantId: string }>;
287
+
288
+ /**
289
+ * Gets the raw prompt fragment with comments
290
+ * @param fragmentId The prompt fragment ID
291
+ * @returns The raw prompt fragment or undefined if not found
292
+ */
293
+ getRawPromptFragment(fragmentId: string): PromptFragment | undefined;
294
+
295
+ /**
296
+ * Gets the raw prompt fragment without comments
297
+ * @param fragmentId The prompt fragment ID
298
+ * @returns The raw prompt fragment or undefined if not found
299
+ */
300
+ getPromptFragment(fragmentId: string): PromptFragment | undefined;
301
+
302
+ /**
303
+ * Gets the built-in raw prompt fragment (before any customizations)
304
+ * @param fragmentId The prompt fragment ID
305
+ * @returns The built-in fragment or undefined if not found
306
+ */
307
+ getBuiltInRawPrompt(fragmentId: string): PromptFragment | undefined;
308
+
309
+ /**
310
+ * Resolves a prompt fragment by replacing variables and function references
311
+ * @param fragmentId The prompt fragment ID
312
+ * @param args Optional object with values for variable replacement
313
+ * @param context Optional context for variable resolution
314
+ * @returns The resolved prompt fragment or undefined if not found
315
+ */
316
+ getResolvedPromptFragment(fragmentId: string, args?: { [key: string]: unknown }, context?: AIVariableContext): Promise<ResolvedPromptFragment | undefined>;
317
+
318
+ /**
319
+ * Resolves a prompt fragment by replacing variables but preserving function references
320
+ * @param fragmentId The prompt fragment ID
321
+ * @param args Optional object with values for variable replacement
322
+ * @param context Optional context for variable resolution
323
+ * @param resolveVariable Optional custom variable resolution function
324
+ * @returns The partially resolved prompt fragment or undefined if not found
325
+ */
326
+ getResolvedPromptFragmentWithoutFunctions(
327
+ fragmentId: string,
328
+ args?: { [key: string]: unknown },
329
+ context?: AIVariableContext,
330
+ resolveVariable?: (variable: AIVariableArg) => Promise<ResolvedAIVariable | undefined>
331
+ ): Promise<Omit<ResolvedPromptFragment, 'functionDescriptions'> | undefined>;
332
+
333
+ /**
334
+ * Adds a prompt fragment to the service
335
+ * @param promptFragment The fragment to store
336
+ * @param promptVariantSetId Optional ID of the prompt variant set this is a variant of
337
+ */
338
+ addBuiltInPromptFragment(promptFragment: BasePromptFragment, promptVariantSetId?: string, isDefault?: boolean): void;
339
+
340
+ /**
341
+ * Removes a prompt fragment from the service
342
+ * @param fragmentId The fragment ID to remove
343
+ */
344
+ removePromptFragment(fragmentId: string): void;
345
+
346
+ /**
347
+ * Gets all known prompts, including variants and customizations
348
+ * @returns Map of fragment IDs to arrays of fragments
349
+ */
350
+ getAllPromptFragments(): Map<string, PromptFragment[]>;
351
+
352
+ /**
353
+ * Gets all active prompts (highest priority version of each fragment)
354
+ * @returns Array of active prompt fragments
355
+ */
356
+ getActivePromptFragments(): PromptFragment[];
357
+
358
+ /**
359
+ * Returns all IDs of all prompt fragments of the given set
360
+ * @param promptVariantSetId The prompt variant set id
361
+ * @returns Array of variant IDs
362
+ */
363
+ getVariantIds(promptVariantSetId: string): string[];
364
+
365
+ /**
366
+ * Gets the currently selected variant ID of the given set
367
+ * @param promptVariantSetId The prompt variant set id
368
+ * @returns The selected variant ID or the main ID if no variant is selected
369
+ */
370
+ getSelectedVariantId(promptVariantSetId: string): Promise<string | undefined>;
371
+
372
+ /**
373
+ * Gets the default variant ID of the given set
374
+ * @param promptVariantSetId The prompt variant set id
375
+ * @returns The default variant ID or undefined if no default is set
376
+ */
377
+ getDefaultVariantId(promptVariantSetId: string): string | undefined;
378
+
379
+ /**
380
+ * Updates the selected variant for a prompt variant set
381
+ * @param agentId The ID of the agent to update
382
+ * @param promptVariantSetId The prompt variant set ID
383
+ * @param newVariant The new variant ID to set as selected
384
+ */
385
+ updateSelectedVariantId(agentId: string, promptVariantSetId: string, newVariant: string): Promise<void>;
386
+
387
+ /**
388
+ * Gets all prompt variant sets and their variants
389
+ * @returns Map of prompt variant set IDs to arrays of variant IDs
390
+ */
391
+ getPromptVariantSets(): Map<string, string[]>;
392
+
393
+ /**
394
+ * The following methods delegate to the PromptFragmentCustomizationService
395
+ */
396
+ createCustomization(fragmentId: string): Promise<void>;
397
+ createBuiltInCustomization(fragmentId: string): Promise<void>;
398
+ editBuiltInCustomization(fragmentId: string): Promise<void>;
399
+ editCustomization(fragmentId: string, customizationId: string): Promise<void>;
400
+ removeCustomization(fragmentId: string, customizationId: string): Promise<void>;
401
+ resetAllToBuiltIn(): Promise<void>;
402
+ resetToBuiltIn(fragmentId: string): Promise<void>;
403
+ resetToCustomization(fragmentId: string, customizationId: string): Promise<void>;
404
+ getCustomizationDescription(fragmentId: string, customizationId: string): Promise<string | undefined>;
405
+ getCustomizationType(fragmentId: string, customizationId: string): Promise<string | undefined>;
406
+ getTemplateIDFromResource(resourceId: unknown): string | undefined;
407
+ }
408
+
220
409
  @injectable()
221
410
  export class PromptServiceImpl implements PromptService {
222
411
  @inject(AISettingsService) @optional()
223
412
  protected readonly settingsService: AISettingsService | undefined;
224
413
 
225
- @inject(PromptCustomizationService) @optional()
226
- protected readonly customizationService: PromptCustomizationService | undefined;
414
+ @inject(PromptFragmentCustomizationService) @optional()
415
+ protected readonly customizationService: PromptFragmentCustomizationService | undefined;
227
416
 
228
417
  @inject(AIVariableService) @optional()
229
418
  protected readonly variableService: AIVariableService | undefined;
@@ -231,199 +420,483 @@ export class PromptServiceImpl implements PromptService {
231
420
  @inject(ToolInvocationRegistry) @optional()
232
421
  protected readonly toolInvocationRegistry: ToolInvocationRegistry | undefined;
233
422
 
234
- protected _prompts: PromptMap = {};
423
+ // Collection of built-in prompt fragments
424
+ protected _builtInFragments: BasePromptFragment[] = [];
425
+
426
+ // Map to store prompt variants sets (key: promptVariantSetId, value: array of variantIds)
427
+ protected _promptVariantSetsMap = new Map<string, string[]>();
428
+
429
+ // Map to store default variant for each prompt variant set (key: promptVariantSetId, value: variantId)
430
+ protected _defaultVariantsMap = new Map<string, string>();
431
+
432
+ // Event emitter for prompt changes
433
+ protected _onPromptsChangeEmitter = new Emitter<void>();
434
+ readonly onPromptsChange = this._onPromptsChangeEmitter.event;
435
+
436
+ // Event emitter for selected variant changes
437
+ protected _onSelectedVariantChangeEmitter = new Emitter<{ promptVariantSetId: string, variantId: string }>();
438
+ readonly onSelectedVariantChange = this._onSelectedVariantChangeEmitter.event;
439
+
440
+ @postConstruct()
441
+ protected init(): void {
442
+ if (this.customizationService) {
443
+ this.customizationService.onDidChangePromptFragmentCustomization(() => {
444
+ this._onPromptsChangeEmitter.fire();
445
+ });
446
+ this.customizationService.onDidChangeCustomAgents(() => {
447
+ this._onPromptsChangeEmitter.fire();
448
+ });
449
+ }
450
+ }
451
+
452
+ // ===== Fragment Retrieval Methods =====
453
+
454
+ /**
455
+ * Finds a built-in fragment by its ID
456
+ * @param fragmentId The ID of the fragment to find
457
+ * @returns The built-in fragment or undefined if not found
458
+ */
459
+ protected findBuiltInFragmentById(fragmentId: string): BasePromptFragment | undefined {
460
+ return this._builtInFragments.find(fragment => fragment.id === fragmentId);
461
+ }
235
462
 
236
- getRawPrompt(id: string): PromptTemplate | undefined {
237
- if (this.customizationService !== undefined && this.customizationService.isPromptTemplateCustomized(id)) {
238
- const template = this.customizationService.getCustomizedPromptTemplate(id);
239
- if (template !== undefined) {
240
- return { id, template };
463
+ getRawPromptFragment(fragmentId: string): PromptFragment | undefined {
464
+ if (this.customizationService?.isPromptFragmentCustomized(fragmentId)) {
465
+ const customizedFragment = this.customizationService.getActivePromptFragmentCustomization(fragmentId);
466
+ if (customizedFragment !== undefined) {
467
+ return customizedFragment;
241
468
  }
242
469
  }
243
- return this.getDefaultRawPrompt(id);
470
+ return this.getBuiltInRawPrompt(fragmentId);
244
471
  }
245
- getDefaultRawPrompt(id: string): PromptTemplate | undefined {
246
- return this._prompts[id];
472
+
473
+ getBuiltInRawPrompt(fragmentId: string): PromptFragment | undefined {
474
+ return this.findBuiltInFragmentById(fragmentId);
247
475
  }
248
476
 
249
- getUnresolvedPrompt(id: string): PromptTemplate | undefined {
250
- const rawPrompt = this.getRawPrompt(id);
251
- if (!rawPrompt) {
477
+ getPromptFragment(fragmentId: string): PromptFragment | undefined {
478
+ const rawFragment = this.getRawPromptFragment(fragmentId);
479
+ if (!rawFragment) {
252
480
  return undefined;
253
481
  }
254
482
  return {
255
- id: rawPrompt.id,
256
- template: this.stripComments(rawPrompt.template)
483
+ ...rawFragment,
484
+ template: this.stripComments(rawFragment.template)
257
485
  };
258
486
  }
259
487
 
260
- protected stripComments(template: string): string {
488
+ /**
489
+ * Strips comments from a template string
490
+ * @param templateText The template text to process
491
+ * @returns Template text with comments removed
492
+ */
493
+ protected stripComments(templateText: string): string {
261
494
  const commentRegex = /^\s*{{!--[\s\S]*?--}}\s*\n?/;
262
- return commentRegex.test(template) ? template.replace(commentRegex, '').trimStart() : template;
495
+ return commentRegex.test(templateText) ? templateText.replace(commentRegex, '').trimStart() : templateText;
263
496
  }
264
497
 
265
- async getVariantId(id: string): Promise<string> {
266
- if (this.settingsService !== undefined) {
498
+ async getSelectedVariantId(fragmentId: string): Promise<string | undefined> {
499
+ if (this.settingsService) {
267
500
  const agentSettingsMap = await this.settingsService.getSettings();
268
501
 
269
502
  for (const agentSettings of Object.values(agentSettingsMap)) {
270
- if (agentSettings.selectedVariants && agentSettings.selectedVariants[id]) {
271
- return agentSettings.selectedVariants[id];
503
+ if (agentSettings.selectedVariants && agentSettings.selectedVariants[fragmentId]) {
504
+ return agentSettings.selectedVariants[fragmentId];
272
505
  }
273
506
  }
274
507
  }
275
- return id;
508
+ return this.getDefaultVariantId(fragmentId);
276
509
  }
277
510
 
278
- async getPrompt(id: string, args?: { [key: string]: unknown }, context?: AIVariableContext): Promise<ResolvedPromptTemplate | undefined> {
279
- const variantId = await this.getVariantId(id);
280
- const prompt = this.getUnresolvedPrompt(variantId);
281
- if (prompt === undefined) {
511
+ protected async resolvePotentialSystemPrompt(promptFragmentId: string): Promise<PromptFragment | undefined> {
512
+ if (this._promptVariantSetsMap.has(promptFragmentId)) {
513
+ // This is a systemPrompt find the selected variant
514
+ const selectedVariantId = await this.getSelectedVariantId(promptFragmentId);
515
+ if (selectedVariantId === undefined) {
516
+ return undefined;
517
+ }
518
+ return this.getPromptFragment(selectedVariantId);
519
+ }
520
+ return this.getPromptFragment(promptFragmentId);
521
+ }
522
+
523
+ // ===== Fragment Resolution Methods =====
524
+
525
+ async getResolvedPromptFragment(systemOrFragmentId: string, args?: { [key: string]: unknown }, context?: AIVariableContext): Promise<ResolvedPromptFragment | undefined> {
526
+ const promptFragment = await this.resolvePotentialSystemPrompt(systemOrFragmentId);
527
+ if (promptFragment === undefined) {
282
528
  return undefined;
283
529
  }
284
530
 
285
531
  // First resolve variables and arguments
286
- let resolvedTemplate = prompt.template;
287
- const variableAndArgReplacements = await this.getVariableAndArgReplacements(prompt.template, args, context);
288
- variableAndArgReplacements.replacements.forEach(replacement => resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
532
+ let resolvedTemplate = promptFragment.template;
533
+ const variableAndArgResolutions = await this.resolveVariablesAndArgs(promptFragment.template, args, context);
534
+ variableAndArgResolutions.replacements.forEach(replacement =>
535
+ resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
289
536
 
290
537
  // Then resolve function references with already resolved variables and arguments
291
538
  // This allows to resolve function references contained in resolved variables (e.g. prompt fragments)
292
539
  const functionMatches = matchFunctionsRegEx(resolvedTemplate);
293
- const functions = new Map<string, ToolRequest>();
540
+ const functionMap = new Map<string, ToolRequest>();
294
541
  const functionReplacements = functionMatches.map(match => {
295
542
  const completeText = match[0];
296
543
  const functionId = match[1];
297
544
  const toolRequest = this.toolInvocationRegistry?.getFunction(functionId);
298
545
  if (toolRequest) {
299
- functions.set(toolRequest.id, toolRequest);
546
+ functionMap.set(toolRequest.id, toolRequest);
300
547
  }
301
548
  return {
302
549
  placeholder: completeText,
303
550
  value: toolRequest ? toolRequestToPromptText(toolRequest) : completeText
304
551
  };
305
552
  });
306
- functionReplacements.forEach(replacement => resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
553
+ functionReplacements.forEach(replacement =>
554
+ resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
307
555
 
308
556
  return {
309
- id,
557
+ id: systemOrFragmentId,
310
558
  text: resolvedTemplate,
311
- functionDescriptions: functions.size > 0 ? functions : undefined,
312
- variables: variableAndArgReplacements.resolvedVariables
559
+ functionDescriptions: functionMap.size > 0 ? functionMap : undefined,
560
+ variables: variableAndArgResolutions.resolvedVariables
313
561
  };
314
562
  }
315
563
 
316
- async getPromptFragment(
317
- id: string,
564
+ async getResolvedPromptFragmentWithoutFunctions(
565
+ systemOrFragmentId: string,
318
566
  args?: { [key: string]: unknown },
319
567
  context?: AIVariableContext,
320
568
  resolveVariable?: (variable: AIVariableArg) => Promise<ResolvedAIVariable | undefined>
321
- ): Promise<Omit<ResolvedPromptTemplate, 'functionDescriptions'> | undefined> {
322
- const variantId = await this.getVariantId(id);
323
- const prompt = this.getUnresolvedPrompt(variantId);
324
- if (prompt === undefined) {
569
+ ): Promise<Omit<ResolvedPromptFragment, 'functionDescriptions'> | undefined> {
570
+ const promptFragment = await this.resolvePotentialSystemPrompt(systemOrFragmentId);
571
+ if (promptFragment === undefined) {
325
572
  return undefined;
326
573
  }
327
574
 
328
- const replacements = await this.getVariableAndArgReplacements(prompt.template, args, context, resolveVariable);
329
- let resolvedTemplate = prompt.template;
330
- replacements.replacements.forEach(replacement => resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
575
+ const resolutions = await this.resolveVariablesAndArgs(promptFragment.template, args, context, resolveVariable);
576
+ let resolvedTemplate = promptFragment.template;
577
+ resolutions.replacements.forEach(replacement =>
578
+ resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
331
579
 
332
580
  return {
333
- id,
581
+ id: systemOrFragmentId,
334
582
  text: resolvedTemplate,
335
- variables: replacements.resolvedVariables
583
+ variables: resolutions.resolvedVariables
336
584
  };
337
585
  }
338
586
 
339
587
  /**
340
588
  * Calculates all variable and argument replacements for an unresolved template.
341
589
  *
342
- * @param template the unresolved template text
590
+ * @param templateText the unresolved template text
343
591
  * @param args the object with placeholders, mapping the placeholder key to the value
344
592
  * @param context the {@link AIVariableContext} to use during variable resolution
345
593
  * @param resolveVariable the variable resolving method. Fall back to using the {@link AIVariableService} if not given.
594
+ * @returns Object containing replacements and resolved variables
346
595
  */
347
- protected async getVariableAndArgReplacements(
348
- template: string,
596
+ protected async resolveVariablesAndArgs(
597
+ templateText: string,
349
598
  args?: { [key: string]: unknown },
350
599
  context?: AIVariableContext,
351
600
  resolveVariable?: (variable: AIVariableArg) => Promise<ResolvedAIVariable | undefined>
352
- ): Promise<{ replacements: { placeholder: string; value: string }[], resolvedVariables: ResolvedAIVariable[] }> {
353
- const matches = matchVariablesRegEx(template);
601
+ ): Promise<{
602
+ replacements: { placeholder: string; value: string }[],
603
+ resolvedVariables: ResolvedAIVariable[]
604
+ }> {
605
+ const variableMatches = matchVariablesRegEx(templateText);
354
606
  const variableCache = createAIResolveVariableCache();
355
- const variableAndArgReplacements: { placeholder: string; value: string }[] = [];
356
- const resolvedVariables: Set<ResolvedAIVariable> = new Set();
357
- for (const match of matches) {
358
- const completeText = match[0];
607
+ const replacementsList: { placeholder: string; value: string }[] = [];
608
+ const resolvedVariablesSet: Set<ResolvedAIVariable> = new Set();
609
+
610
+ for (const match of variableMatches) {
611
+ const placeholderText = match[0];
359
612
  const variableAndArg = match[1];
360
613
  let variableName = variableAndArg;
361
614
  let argument: string | undefined;
615
+
362
616
  const parts = variableAndArg.split(':', 2);
363
617
  if (parts.length > 1) {
364
618
  variableName = parts[0];
365
619
  argument = parts[1];
366
620
  }
367
- let value: string;
621
+
622
+ let replacementValue: string;
368
623
  if (args && args[variableAndArg] !== undefined) {
369
- value = String(args[variableAndArg]);
624
+ replacementValue = String(args[variableAndArg]);
370
625
  } else {
371
- const toResolve = { variable: variableName, arg: argument };
372
- const resolved = resolveVariable
373
- ? await resolveVariable(toResolve)
374
- : await this.variableService?.resolveVariable(toResolve, context ?? {}, variableCache);
626
+ const variableToResolve = { variable: variableName, arg: argument };
627
+ const resolvedVariable = resolveVariable
628
+ ? await resolveVariable(variableToResolve)
629
+ : await this.variableService?.resolveVariable(variableToResolve, context ?? {}, variableCache);
630
+
375
631
  // Track resolved variable and its dependencies in all resolved variables
376
- if (resolved) {
377
- resolvedVariables.add(resolved);
378
- resolved.allResolvedDependencies?.forEach(v => resolvedVariables.add(v));
632
+ if (resolvedVariable) {
633
+ resolvedVariablesSet.add(resolvedVariable);
634
+ resolvedVariable.allResolvedDependencies?.forEach(v => resolvedVariablesSet.add(v));
635
+ }
636
+ replacementValue = String(resolvedVariable?.value ?? placeholderText);
637
+ }
638
+ replacementsList.push({ placeholder: placeholderText, value: replacementValue });
639
+ }
640
+
641
+ return {
642
+ replacements: replacementsList,
643
+ resolvedVariables: Array.from(resolvedVariablesSet)
644
+ };
645
+ }
646
+
647
+ // ===== Fragment Collection Management Methods =====
648
+
649
+ getAllPromptFragments(): Map<string, PromptFragment[]> {
650
+ const fragmentsMap = new Map<string, PromptFragment[]>();
651
+
652
+ if (this.customizationService) {
653
+ const customizationIds = this.customizationService.getCustomizedPromptFragmentIds();
654
+ customizationIds.forEach(fragmentId => {
655
+ const customizations = this.customizationService!.getAllCustomizations(fragmentId);
656
+ if (customizations.length > 0) {
657
+ fragmentsMap.set(fragmentId, customizations);
379
658
  }
380
- value = String(resolved?.value ?? completeText);
659
+ });
660
+ }
661
+
662
+ // Add all built-in fragments
663
+ for (const fragment of this._builtInFragments) {
664
+ if (fragmentsMap.has(fragment.id)) {
665
+ fragmentsMap.get(fragment.id)!.push(fragment);
666
+ } else {
667
+ fragmentsMap.set(fragment.id, [fragment]);
381
668
  }
382
- variableAndArgReplacements.push({ placeholder: completeText, value });
383
669
  }
384
670
 
385
- return { replacements: variableAndArgReplacements, resolvedVariables: Array.from(resolvedVariables) };
671
+ return fragmentsMap;
386
672
  }
387
673
 
388
- getAllPrompts(): PromptMap {
389
- if (this.customizationService !== undefined) {
390
- const myCustomization = this.customizationService;
391
- const result: PromptMap = {};
392
- Object.keys(this._prompts).forEach(id => {
393
- if (myCustomization.isPromptTemplateCustomized(id)) {
394
- const template = myCustomization.getCustomizedPromptTemplate(id);
395
- if (template !== undefined) {
396
- result[id] = { id, template };
674
+ getActivePromptFragments(): PromptFragment[] {
675
+ const activeFragments: PromptFragment[] = [...this._builtInFragments];
676
+
677
+ if (this.customizationService) {
678
+ // Fetch all customized fragment IDs once
679
+ const customizedIds = this.customizationService.getCustomizedPromptFragmentIds();
680
+
681
+ // For each customized ID, get the active customization
682
+ for (const fragmentId of customizedIds) {
683
+ const customFragment = this.customizationService?.getActivePromptFragmentCustomization(fragmentId);
684
+ if (customFragment) {
685
+ // Find and replace existing entry with the same ID instead of just adding
686
+ const existingIndex = activeFragments.findIndex(fragment => fragment.id === fragmentId);
687
+ if (existingIndex !== -1) {
688
+ // Replace existing fragment
689
+ activeFragments[existingIndex] = customFragment;
397
690
  } else {
398
- result[id] = { ...this._prompts[id] };
691
+ // Add new fragment if no existing one found
692
+ activeFragments.push(customFragment);
399
693
  }
400
- } else {
401
- result[id] = { ...this._prompts[id] };
402
694
  }
403
- });
404
- return result;
695
+ }
696
+ }
697
+ return activeFragments;
698
+ }
699
+
700
+ removePromptFragment(fragmentId: string): void {
701
+ const index = this._builtInFragments.findIndex(fragment => fragment.id === fragmentId);
702
+ if (index !== -1) {
703
+ this._builtInFragments.splice(index, 1);
704
+ }
705
+
706
+ // Remove any variant references
707
+ for (const [promptVariantSetId, variants] of this._promptVariantSetsMap.entries()) {
708
+ if (variants.includes(fragmentId)) {
709
+ this.removeFragmentVariant(promptVariantSetId, fragmentId);
710
+ }
711
+ }
712
+
713
+ // Clean up default variants map if needed
714
+ if (this._defaultVariantsMap.has(fragmentId)) {
715
+ this._defaultVariantsMap.delete(fragmentId);
716
+ }
717
+
718
+ // Look for this fragmentId as a variant in default variants and remove if found
719
+ for (const [promptVariantSetId, defaultVariantId] of this._defaultVariantsMap.entries()) {
720
+ if (defaultVariantId === fragmentId) {
721
+ this._defaultVariantsMap.delete(promptVariantSetId);
722
+ }
723
+ }
724
+
725
+ this._onPromptsChangeEmitter.fire();
726
+ }
727
+
728
+ getVariantIds(fragmentId: string): string[] {
729
+ return this._promptVariantSetsMap.get(fragmentId) || [];
730
+ }
731
+
732
+ getDefaultVariantId(promptVariantSetId: string): string | undefined {
733
+ return this._defaultVariantsMap.get(promptVariantSetId);
734
+ }
735
+
736
+ getPromptVariantSets(): Map<string, string[]> {
737
+ return new Map(this._promptVariantSetsMap);
738
+ }
739
+
740
+ addBuiltInPromptFragment(promptFragment: BasePromptFragment, promptVariantSetId?: string, isDefault: boolean = false): void {
741
+ const existingIndex = this._builtInFragments.findIndex(fragment => fragment.id === promptFragment.id);
742
+ if (existingIndex !== -1) {
743
+ // Replace existing fragment with the same ID
744
+ this._builtInFragments[existingIndex] = promptFragment;
405
745
  } else {
406
- return { ...this._prompts };
746
+ // Add new fragment
747
+ this._builtInFragments.push(promptFragment);
407
748
  }
749
+
750
+ // If this is a variant of a prompt variant set, record it in the variants map
751
+ if (promptVariantSetId) {
752
+ this.addFragmentVariant(promptVariantSetId, promptFragment.id, isDefault);
753
+ }
754
+
755
+ this._onPromptsChangeEmitter.fire();
408
756
  }
409
- removePrompt(id: string): void {
410
- delete this._prompts[id];
757
+
758
+ // ===== Variant Management Methods =====
759
+
760
+ /**
761
+ * Adds a variant ID to the fragment variants map
762
+ * @param promptVariantSetId The prompt variant set id
763
+ * @param variantId The variant ID to add
764
+ * @param isDefault Whether this variant should be the default for the prompt variant set (defaults to false)
765
+ */
766
+ protected addFragmentVariant(promptVariantSetId: string, variantId: string, isDefault: boolean = false): void {
767
+ if (!this._promptVariantSetsMap.has(promptVariantSetId)) {
768
+ this._promptVariantSetsMap.set(promptVariantSetId, []);
769
+ }
770
+
771
+ const variants = this._promptVariantSetsMap.get(promptVariantSetId)!;
772
+ if (!variants.includes(variantId)) {
773
+ variants.push(variantId);
774
+ }
775
+
776
+ if (isDefault) {
777
+ this._defaultVariantsMap.set(promptVariantSetId, variantId);
778
+ }
779
+ }
780
+
781
+ /**
782
+ * Removes a variant ID from the fragment variants map
783
+ * @param promptVariantSetId The prompt variant set id
784
+ * @param variantId The variant ID to remove
785
+ */
786
+ protected removeFragmentVariant(promptVariantSetId: string, variantId: string): void {
787
+ if (!this._promptVariantSetsMap.has(promptVariantSetId)) {
788
+ return;
789
+ }
790
+
791
+ const variants = this._promptVariantSetsMap.get(promptVariantSetId)!;
792
+ const index = variants.indexOf(variantId);
793
+
794
+ if (index !== -1) {
795
+ variants.splice(index, 1);
796
+
797
+ // Remove the key if no variants left
798
+ if (variants.length === 0) {
799
+ this._promptVariantSetsMap.delete(promptVariantSetId);
800
+ }
801
+ }
411
802
  }
412
- getVariantIds(id: string): string[] {
413
- const allCustomPromptTemplateIds = this.customizationService?.getCustomPromptTemplateIDs() || [];
414
- const knownPromptIds = Object.keys(this._prompts);
415
803
 
416
- // We filter out known IDs from the custom prompt template IDs, these are no variants, but customizations. Then we retain IDs that start with the main ID
417
- const customVariantIds = allCustomPromptTemplateIds.filter(customId =>
418
- !knownPromptIds.includes(customId) && customId.startsWith(id)
419
- );
420
- const variantIds = Object.values(this._prompts)
421
- .filter(prompt => prompt.variantOf === id)
422
- .map(variant => variant.id);
804
+ async updateSelectedVariantId(agentId: string, promptVariantSetId: string, newVariant: string): Promise<void> {
805
+ if (!this.settingsService) {
806
+ return;
807
+ }
808
+
809
+ const defaultVariantId = this.getDefaultVariantId(promptVariantSetId);
810
+ const agentSettings = await this.settingsService.getAgentSettings(agentId);
811
+ const selectedVariants = agentSettings?.selectedVariants || {};
423
812
 
424
- return [...variantIds, ...customVariantIds];
813
+ const updatedVariants = { ...selectedVariants };
814
+ if (newVariant === defaultVariantId) {
815
+ delete updatedVariants[promptVariantSetId];
816
+ } else {
817
+ updatedVariants[promptVariantSetId] = newVariant;
818
+ }
819
+
820
+ await this.settingsService.updateAgentSettings(agentId, {
821
+ selectedVariants: updatedVariants,
822
+ });
823
+
824
+ // Emit the selected variant change event
825
+ this._onSelectedVariantChangeEmitter.fire({ promptVariantSetId, variantId: newVariant });
425
826
  }
426
- storePromptTemplate(promptTemplate: PromptTemplate): void {
427
- this._prompts[promptTemplate.id] = promptTemplate;
827
+
828
+ // ===== Customization Service Delegation Methods =====
829
+
830
+ async createCustomization(fragmentId: string): Promise<void> {
831
+ if (this.customizationService) {
832
+ await this.customizationService.createPromptFragmentCustomization(fragmentId);
833
+ }
834
+ }
835
+
836
+ async createBuiltInCustomization(fragmentId: string): Promise<void> {
837
+ if (this.customizationService) {
838
+ const builtInTemplate = this.findBuiltInFragmentById(fragmentId);
839
+ await this.customizationService.createBuiltInPromptFragmentCustomization(fragmentId, builtInTemplate?.template);
840
+ }
841
+ }
842
+
843
+ async editCustomization(fragmentId: string, customizationId: string): Promise<void> {
844
+ if (this.customizationService) {
845
+ await this.customizationService.editPromptFragmentCustomization(fragmentId, customizationId);
846
+ }
847
+ }
848
+
849
+ async removeCustomization(fragmentId: string, customizationId: string): Promise<void> {
850
+ if (this.customizationService) {
851
+ await this.customizationService.removePromptFragmentCustomization(fragmentId, customizationId);
852
+ }
853
+ }
854
+
855
+ async resetAllToBuiltIn(): Promise<void> {
856
+ if (this.customizationService) {
857
+ for (const fragment of this._builtInFragments) {
858
+ await this.customizationService.removeAllPromptFragmentCustomizations(fragment.id);
859
+ }
860
+ }
861
+ }
862
+
863
+ async resetToBuiltIn(fragmentId: string): Promise<void> {
864
+ if (this.customizationService) {
865
+ await this.customizationService.removeAllPromptFragmentCustomizations(fragmentId);
866
+ }
867
+ }
868
+
869
+ async resetToCustomization(fragmentId: string, customizationId: string): Promise<void> {
870
+ if (this.customizationService) {
871
+ await this.customizationService.resetToCustomization(fragmentId, customizationId);
872
+ }
873
+ }
874
+
875
+ async getCustomizationDescription(fragmentId: string, customizationId: string): Promise<string | undefined> {
876
+ if (!this.customizationService) {
877
+ return undefined;
878
+ }
879
+ return await this.customizationService.getPromptFragmentCustomizationDescription(fragmentId, customizationId);
880
+ }
881
+
882
+ async getCustomizationType(fragmentId: string, customizationId: string): Promise<string | undefined> {
883
+ if (!this.customizationService) {
884
+ return undefined;
885
+ }
886
+ return await this.customizationService.getPromptFragmentCustomizationType(fragmentId, customizationId);
887
+ }
888
+
889
+ getTemplateIDFromResource(resourceId: unknown): string | undefined {
890
+ if (this.customizationService) {
891
+ return this.customizationService.getPromptFragmentIDFromResource(resourceId);
892
+ }
893
+ return undefined;
894
+ }
895
+
896
+ async editBuiltInCustomization(fragmentId: string): Promise<void> {
897
+ if (this.customizationService) {
898
+ const builtInTemplate = this.findBuiltInFragmentById(fragmentId);
899
+ await this.customizationService.editBuiltInPromptFragmentCustomization(fragmentId, builtInTemplate?.template);
900
+ }
428
901
  }
429
902
  }