@theia/ai-core 1.55.0-next.97 → 1.56.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.
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +7 -2
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts +6 -4
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +49 -11
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/ai-core-preferences.d.ts +15 -0
- package/lib/browser/ai-core-preferences.d.ts.map +1 -1
- package/lib/browser/ai-core-preferences.js +30 -1
- package/lib/browser/ai-core-preferences.js.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.d.ts +1 -0
- package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
- package/lib/browser/frontend-prompt-customization-service.js +3 -0
- package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
- package/lib/common/agent-service.js +1 -1
- package/lib/common/agent-service.js.map +1 -1
- package/lib/common/language-model.d.ts +7 -0
- package/lib/common/language-model.d.ts.map +1 -1
- package/lib/common/language-model.js.map +1 -1
- package/lib/common/prompt-service.d.ts +28 -5
- package/lib/common/prompt-service.d.ts.map +1 -1
- package/lib/common/prompt-service.js +33 -4
- package/lib/common/prompt-service.js.map +1 -1
- package/lib/common/prompt-service.spec.js +83 -36
- package/lib/common/prompt-service.spec.js.map +1 -1
- package/lib/common/settings-service.d.ts +5 -0
- package/lib/common/settings-service.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +24 -7
- package/src/browser/ai-configuration/template-settings-renderer.tsx +105 -16
- package/src/browser/ai-core-preferences.ts +40 -1
- package/src/browser/frontend-prompt-customization-service.ts +4 -0
- package/src/browser/style/index.css +37 -5
- package/src/common/agent-service.ts +1 -1
- package/src/common/language-model.ts +5 -0
- package/src/common/prompt-service.spec.ts +92 -38
- package/src/common/prompt-service.ts +59 -8
- package/src/common/settings-service.ts +5 -0
|
@@ -21,6 +21,7 @@ import { interfaces } from '@theia/core/shared/inversify';
|
|
|
21
21
|
export const AI_CORE_PREFERENCES_TITLE = '✨ AI Features [Experimental]';
|
|
22
22
|
export const PREFERENCE_NAME_ENABLE_EXPERIMENTAL = 'ai-features.AiEnable.enableAI';
|
|
23
23
|
export const PREFERENCE_NAME_PROMPT_TEMPLATES = 'ai-features.promptTemplates.promptTemplatesFolder';
|
|
24
|
+
export const PREFERENCE_NAME_REQUEST_SETTINGS = 'ai-features.modelSettings.requestSettings';
|
|
24
25
|
|
|
25
26
|
export const aiCorePreferenceSchema: PreferenceSchema = {
|
|
26
27
|
type: 'object',
|
|
@@ -55,13 +56,51 @@ export const aiCorePreferenceSchema: PreferenceSchema = {
|
|
|
55
56
|
canSelectMany: false
|
|
56
57
|
}
|
|
57
58
|
},
|
|
58
|
-
|
|
59
|
+
},
|
|
60
|
+
[PREFERENCE_NAME_REQUEST_SETTINGS]: {
|
|
61
|
+
title: 'Custom Request Settings',
|
|
62
|
+
markdownDescription: 'Allows specifying custom request settings for multiple models.\n\
|
|
63
|
+
Each object represents the configuration for a specific model. The `modelId` field specifies the model ID, `requestSettings` defines model-specific settings.\n\
|
|
64
|
+
The `providerId` field is optional and allows you to apply the settings to a specific provider. If not set, the settings will be applied to all providers.\n\
|
|
65
|
+
Example providerIds: huggingface, openai, ollama, llamafile.\n\
|
|
66
|
+
Refer to [our documentation](https://theia-ide.org/docs/user_ai/#custom-request-settings) for more information.',
|
|
67
|
+
type: 'array',
|
|
68
|
+
items: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
modelId: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'The model id'
|
|
74
|
+
},
|
|
75
|
+
requestSettings: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
additionalProperties: true,
|
|
78
|
+
description: 'Settings for the specific model ID.',
|
|
79
|
+
},
|
|
80
|
+
providerId: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'The (optional) provider id to apply the settings to. If not set, the settings will be applied to all providers.',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
default: [],
|
|
59
87
|
}
|
|
60
88
|
}
|
|
61
89
|
};
|
|
62
90
|
export interface AICoreConfiguration {
|
|
63
91
|
[PREFERENCE_NAME_ENABLE_EXPERIMENTAL]: boolean | undefined;
|
|
64
92
|
[PREFERENCE_NAME_PROMPT_TEMPLATES]: string | undefined;
|
|
93
|
+
[PREFERENCE_NAME_REQUEST_SETTINGS]: Array<{
|
|
94
|
+
modelId: string;
|
|
95
|
+
requestSettings?: { [key: string]: unknown };
|
|
96
|
+
providerId?: string;
|
|
97
|
+
}> | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface RequestSetting {
|
|
101
|
+
modelId: string;
|
|
102
|
+
requestSettings?: { [key: string]: unknown };
|
|
103
|
+
providerId?: string;
|
|
65
104
|
}
|
|
66
105
|
|
|
67
106
|
export const AICorePreferences = Symbol('AICorePreferences');
|
|
@@ -169,6 +169,10 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati
|
|
|
169
169
|
return this.templates.get(id);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
getCustomPromptTemplateIDs(): string[] {
|
|
173
|
+
return Array.from(this.templates.keys());
|
|
174
|
+
}
|
|
175
|
+
|
|
172
176
|
async editTemplate(id: string, defaultContent?: string): Promise<void> {
|
|
173
177
|
const editorUri = await this.getTemplateURI(id);
|
|
174
178
|
if (! await this.fileService.exists(editorUri)) {
|
|
@@ -14,12 +14,44 @@
|
|
|
14
14
|
margin-left: var(--theia-ui-padding);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
.theia-settings-container .settings-section-subcategory-title.ai-settings-section-subcategory-title {
|
|
18
|
+
padding-left: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
.ai-templates {
|
|
18
|
-
display:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
gap: 5px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.template-renderer {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
padding: 10px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.template-header {
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.template-controls {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.template-select-label {
|
|
44
|
+
margin-right: 5px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.template-variant-selector {
|
|
48
|
+
min-width: 120px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.template-variant-selector.error {
|
|
52
|
+
border-color: var(--theia-errorForeground);
|
|
53
|
+
background-color: var(--theia-errorBackground, rgba(255, 0, 0, 0.1));
|
|
54
|
+
color: var(--theia-errorForeground);
|
|
23
55
|
}
|
|
24
56
|
|
|
25
57
|
#ai-variable-configuration-container-widget,
|
|
@@ -99,7 +99,7 @@ export class AgentServiceImpl implements AgentService {
|
|
|
99
99
|
registerAgent(agent: Agent): void {
|
|
100
100
|
this._agents.push(agent);
|
|
101
101
|
agent.promptTemplates.forEach(
|
|
102
|
-
template => this.promptService.
|
|
102
|
+
template => this.promptService.storePromptTemplate(template)
|
|
103
103
|
);
|
|
104
104
|
this.onDidChangeAgentsEmitter.fire();
|
|
105
105
|
}
|
|
@@ -107,6 +107,11 @@ export interface LanguageModelMetaData {
|
|
|
107
107
|
readonly family?: string;
|
|
108
108
|
readonly maxInputTokens?: number;
|
|
109
109
|
readonly maxOutputTokens?: number;
|
|
110
|
+
/**
|
|
111
|
+
* Default request settings for the language model. These settings can be set by a user preferences.
|
|
112
|
+
* Settings in a request will override these default settings.
|
|
113
|
+
*/
|
|
114
|
+
readonly defaultRequestSettings?: { [key: string]: unknown };
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
export namespace LanguageModelMetaData {
|
|
@@ -37,10 +37,10 @@ describe('PromptService', () => {
|
|
|
37
37
|
container.bind<AIVariableService>(AIVariableService).toConstantValue(variableService);
|
|
38
38
|
|
|
39
39
|
promptService = container.get<PromptService>(PromptService);
|
|
40
|
-
promptService.
|
|
41
|
-
promptService.
|
|
42
|
-
promptService.
|
|
43
|
-
promptService.
|
|
40
|
+
promptService.storePromptTemplate({ id: '1', template: 'Hello, {{name}}!' });
|
|
41
|
+
promptService.storePromptTemplate({ id: '2', template: 'Goodbye, {{name}}!' });
|
|
42
|
+
promptService.storePromptTemplate({ id: '3', template: 'Ciao, {{invalid}}!' });
|
|
43
|
+
promptService.storePromptTemplate({ id: '8', template: 'Hello, {{{name}}}' });
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it('should initialize prompts from PromptCollectionService', () => {
|
|
@@ -62,7 +62,7 @@ describe('PromptService', () => {
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
it('should store a new prompt', () => {
|
|
65
|
-
promptService.
|
|
65
|
+
promptService.storePromptTemplate({ id: '3', template: 'Welcome, {{name}}!' });
|
|
66
66
|
const newPrompt = promptService.getRawPrompt('3');
|
|
67
67
|
expect(newPrompt?.template).to.equal('Welcome, {{name}}!');
|
|
68
68
|
});
|
|
@@ -88,10 +88,10 @@ describe('PromptService', () => {
|
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
it('should ignore whitespace in variables', async () => {
|
|
91
|
-
promptService.
|
|
92
|
-
promptService.
|
|
93
|
-
promptService.
|
|
94
|
-
promptService.
|
|
91
|
+
promptService.storePromptTemplate({ id: '4', template: 'Hello, {{name }}!' });
|
|
92
|
+
promptService.storePromptTemplate({ id: '5', template: 'Hello, {{ name}}!' });
|
|
93
|
+
promptService.storePromptTemplate({ id: '6', template: 'Hello, {{ name }}!' });
|
|
94
|
+
promptService.storePromptTemplate({ id: '7', template: 'Hello, {{ name }}!' });
|
|
95
95
|
for (let i = 4; i <= 7; i++) {
|
|
96
96
|
const prompt = await promptService.getPrompt(`${i}`, { name: 'John' });
|
|
97
97
|
expect(prompt?.text).to.equal('Hello, John!');
|
|
@@ -109,10 +109,10 @@ describe('PromptService', () => {
|
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
it('should ignore whitespace in variables (three bracket)', async () => {
|
|
112
|
-
promptService.
|
|
113
|
-
promptService.
|
|
114
|
-
promptService.
|
|
115
|
-
promptService.
|
|
112
|
+
promptService.storePromptTemplate({ id: '9', template: 'Hello, {{{name }}}' });
|
|
113
|
+
promptService.storePromptTemplate({ id: '10', template: 'Hello, {{{ name}}}' });
|
|
114
|
+
promptService.storePromptTemplate({ id: '11', template: 'Hello, {{{ name }}}' });
|
|
115
|
+
promptService.storePromptTemplate({ id: '12', template: 'Hello, {{{ name }}}' });
|
|
116
116
|
for (let i = 9; i <= 12; i++) {
|
|
117
117
|
const prompt = await promptService.getPrompt(`${i}`, { name: 'John' });
|
|
118
118
|
expect(prompt?.text).to.equal('Hello, John');
|
|
@@ -120,26 +120,24 @@ describe('PromptService', () => {
|
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
it('should ignore invalid prompts with unmatched brackets', async () => {
|
|
123
|
-
promptService.
|
|
124
|
-
promptService.
|
|
125
|
-
promptService.
|
|
123
|
+
promptService.storePromptTemplate({ id: '9', template: 'Hello, {{name' });
|
|
124
|
+
promptService.storePromptTemplate({ id: '10', template: 'Hello, {{{name' });
|
|
125
|
+
promptService.storePromptTemplate({ id: '11', template: 'Hello, name}}}}' });
|
|
126
126
|
const prompt1 = await promptService.getPrompt('9', { name: 'John' });
|
|
127
127
|
expect(prompt1?.text).to.equal('Hello, {{name'); // Not matching due to missing closing brackets
|
|
128
|
-
|
|
129
128
|
const prompt2 = await promptService.getPrompt('10', { name: 'John' });
|
|
130
129
|
expect(prompt2?.text).to.equal('Hello, {{{name'); // Matches pattern due to valid three-start-two-end brackets
|
|
131
|
-
|
|
132
130
|
const prompt3 = await promptService.getPrompt('11', { name: 'John' });
|
|
133
131
|
expect(prompt3?.text).to.equal('Hello, name}}}}'); // Extra closing bracket, does not match cleanly
|
|
134
132
|
});
|
|
135
133
|
|
|
136
134
|
it('should handle a mixture of two and three brackets correctly', async () => {
|
|
137
|
-
promptService.
|
|
138
|
-
promptService.
|
|
139
|
-
promptService.
|
|
140
|
-
promptService.
|
|
141
|
-
promptService.
|
|
142
|
-
promptService.
|
|
135
|
+
promptService.storePromptTemplate({ id: '12', template: 'Hi, {{name}}}' }); // (invalid)
|
|
136
|
+
promptService.storePromptTemplate({ id: '13', template: 'Hello, {{{name}}' }); // (invalid)
|
|
137
|
+
promptService.storePromptTemplate({ id: '14', template: 'Greetings, {{{name}}}}' }); // (invalid)
|
|
138
|
+
promptService.storePromptTemplate({ id: '15', template: 'Bye, {{{{name}}}' }); // (invalid)
|
|
139
|
+
promptService.storePromptTemplate({ id: '16', template: 'Ciao, {{{{name}}}}' }); // (invalid)
|
|
140
|
+
promptService.storePromptTemplate({ id: '17', template: 'Hi, {{name}}! {{{name}}}' }); // Mixed valid patterns
|
|
143
141
|
|
|
144
142
|
const prompt12 = await promptService.getPrompt('12', { name: 'John' });
|
|
145
143
|
expect(prompt12?.text).to.equal('Hi, {{name}}}');
|
|
@@ -161,87 +159,143 @@ describe('PromptService', () => {
|
|
|
161
159
|
});
|
|
162
160
|
|
|
163
161
|
it('should strip single-line comments at the start of the template', () => {
|
|
164
|
-
promptService.
|
|
162
|
+
promptService.storePromptTemplate({ id: 'comment-basic', template: '{{!-- Comment --}}Hello, {{name}}!' });
|
|
165
163
|
const prompt = promptService.getUnresolvedPrompt('comment-basic');
|
|
166
164
|
expect(prompt?.template).to.equal('Hello, {{name}}!');
|
|
167
165
|
});
|
|
168
166
|
|
|
169
167
|
it('should remove line break after first-line comment', () => {
|
|
170
|
-
promptService.
|
|
168
|
+
promptService.storePromptTemplate({ id: 'comment-line-break', template: '{{!-- Comment --}}\nHello, {{name}}!' });
|
|
171
169
|
const prompt = promptService.getUnresolvedPrompt('comment-line-break');
|
|
172
170
|
expect(prompt?.template).to.equal('Hello, {{name}}!');
|
|
173
171
|
});
|
|
174
172
|
|
|
175
173
|
it('should strip multiline comments at the start of the template', () => {
|
|
176
|
-
promptService.
|
|
174
|
+
promptService.storePromptTemplate({ id: 'comment-multiline', template: '{{!--\nMultiline comment\n--}}\nGoodbye, {{name}}!' });
|
|
177
175
|
const prompt = promptService.getUnresolvedPrompt('comment-multiline');
|
|
178
176
|
expect(prompt?.template).to.equal('Goodbye, {{name}}!');
|
|
179
177
|
});
|
|
180
178
|
|
|
181
179
|
it('should not strip comments not in the first line', () => {
|
|
182
|
-
promptService.
|
|
180
|
+
promptService.storePromptTemplate({ id: 'comment-second-line', template: 'Hello, {{name}}!\n{{!-- Comment --}}' });
|
|
183
181
|
const prompt = promptService.getUnresolvedPrompt('comment-second-line');
|
|
184
182
|
expect(prompt?.template).to.equal('Hello, {{name}}!\n{{!-- Comment --}}');
|
|
185
183
|
});
|
|
186
184
|
|
|
187
185
|
it('should treat unclosed comments as regular text', () => {
|
|
188
|
-
promptService.
|
|
186
|
+
promptService.storePromptTemplate({ id: 'comment-unclosed', template: '{{!-- Unclosed comment' });
|
|
189
187
|
const prompt = promptService.getUnresolvedPrompt('comment-unclosed');
|
|
190
188
|
expect(prompt?.template).to.equal('{{!-- Unclosed comment');
|
|
191
189
|
});
|
|
192
190
|
|
|
193
191
|
it('should treat standalone closing delimiters as regular text', () => {
|
|
194
|
-
promptService.
|
|
192
|
+
promptService.storePromptTemplate({ id: 'comment-standalone', template: '--}} Hello, {{name}}!' });
|
|
195
193
|
const prompt = promptService.getUnresolvedPrompt('comment-standalone');
|
|
196
194
|
expect(prompt?.template).to.equal('--}} Hello, {{name}}!');
|
|
197
195
|
});
|
|
198
196
|
|
|
199
197
|
it('should handle nested comments and stop at the first closing tag', () => {
|
|
200
|
-
promptService.
|
|
198
|
+
promptService.storePromptTemplate({ id: 'nested-comment', template: '{{!-- {{!-- Nested comment --}} --}}text' });
|
|
201
199
|
const prompt = promptService.getUnresolvedPrompt('nested-comment');
|
|
202
200
|
expect(prompt?.template).to.equal('--}}text');
|
|
203
201
|
});
|
|
204
202
|
|
|
205
203
|
it('should handle templates with only comments', () => {
|
|
206
|
-
promptService.
|
|
204
|
+
promptService.storePromptTemplate({ id: 'comment-only', template: '{{!-- Only comments --}}' });
|
|
207
205
|
const prompt = promptService.getUnresolvedPrompt('comment-only');
|
|
208
206
|
expect(prompt?.template).to.equal('');
|
|
209
207
|
});
|
|
210
208
|
|
|
211
209
|
it('should handle mixed delimiters on the same line', () => {
|
|
212
|
-
promptService.
|
|
210
|
+
promptService.storePromptTemplate({ id: 'comment-mixed', template: '{{!-- Unclosed comment --}}' });
|
|
213
211
|
const prompt = promptService.getUnresolvedPrompt('comment-mixed');
|
|
214
212
|
expect(prompt?.template).to.equal('');
|
|
215
213
|
});
|
|
216
214
|
|
|
217
215
|
it('should resolve variables after stripping single-line comments', async () => {
|
|
218
|
-
promptService.
|
|
216
|
+
promptService.storePromptTemplate({ id: 'comment-resolve', template: '{{!-- Comment --}}Hello, {{name}}!' });
|
|
219
217
|
const prompt = await promptService.getPrompt('comment-resolve', { name: 'John' });
|
|
220
218
|
expect(prompt?.text).to.equal('Hello, John!');
|
|
221
219
|
});
|
|
222
220
|
|
|
223
221
|
it('should resolve variables in multiline templates with comments', async () => {
|
|
224
|
-
promptService.
|
|
222
|
+
promptService.storePromptTemplate({ id: 'comment-multiline-vars', template: '{{!--\nMultiline comment\n--}}\nHello, {{name}}!' });
|
|
225
223
|
const prompt = await promptService.getPrompt('comment-multiline-vars', { name: 'John' });
|
|
226
224
|
expect(prompt?.text).to.equal('Hello, John!');
|
|
227
225
|
});
|
|
228
226
|
|
|
229
227
|
it('should resolve variables with standalone closing delimiters', async () => {
|
|
230
|
-
promptService.
|
|
228
|
+
promptService.storePromptTemplate({ id: 'comment-standalone-vars', template: '--}} Hello, {{name}}!' });
|
|
231
229
|
const prompt = await promptService.getPrompt('comment-standalone-vars', { name: 'John' });
|
|
232
230
|
expect(prompt?.text).to.equal('--}} Hello, John!');
|
|
233
231
|
});
|
|
234
232
|
|
|
235
233
|
it('should treat unclosed comments as text and resolve variables', async () => {
|
|
236
|
-
promptService.
|
|
234
|
+
promptService.storePromptTemplate({ id: 'comment-unclosed-vars', template: '{{!-- Unclosed comment\nHello, {{name}}!' });
|
|
237
235
|
const prompt = await promptService.getPrompt('comment-unclosed-vars', { name: 'John' });
|
|
238
236
|
expect(prompt?.text).to.equal('{{!-- Unclosed comment\nHello, John!');
|
|
239
237
|
});
|
|
240
238
|
|
|
241
239
|
it('should handle templates with mixed comments and variables', async () => {
|
|
242
|
-
promptService.
|
|
240
|
+
promptService.storePromptTemplate({ id: 'comment-mixed-vars', template: '{{!-- Comment --}}Hi, {{name}}! {{!-- Another comment --}}' });
|
|
243
241
|
const prompt = await promptService.getPrompt('comment-mixed-vars', { name: 'John' });
|
|
244
242
|
expect(prompt?.text).to.equal('Hi, John! {{!-- Another comment --}}');
|
|
245
243
|
});
|
|
246
244
|
|
|
245
|
+
it('should return all variant IDs of a given prompt', () => {
|
|
246
|
+
promptService.storePromptTemplate({ id: 'main', template: 'Main template' });
|
|
247
|
+
|
|
248
|
+
promptService.storePromptTemplate({
|
|
249
|
+
id: 'variant1',
|
|
250
|
+
template: 'Variant 1',
|
|
251
|
+
variantOf: 'main'
|
|
252
|
+
});
|
|
253
|
+
promptService.storePromptTemplate({
|
|
254
|
+
id: 'variant2',
|
|
255
|
+
template: 'Variant 2',
|
|
256
|
+
variantOf: 'main'
|
|
257
|
+
});
|
|
258
|
+
promptService.storePromptTemplate({
|
|
259
|
+
id: 'variant3',
|
|
260
|
+
template: 'Variant 3',
|
|
261
|
+
variantOf: 'main'
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const variantIds = promptService.getVariantIds('main');
|
|
265
|
+
expect(variantIds).to.deep.equal(['variant1', 'variant2', 'variant3']);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should return an empty array if no variants exist for a given prompt', () => {
|
|
269
|
+
promptService.storePromptTemplate({ id: 'main', template: 'Main template' });
|
|
270
|
+
|
|
271
|
+
const variantIds = promptService.getVariantIds('main');
|
|
272
|
+
expect(variantIds).to.deep.equal([]);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should return an empty array if the main prompt ID does not exist', () => {
|
|
276
|
+
const variantIds = promptService.getVariantIds('nonExistent');
|
|
277
|
+
expect(variantIds).to.deep.equal([]);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should not influence prompts without variants when other prompts have variants', () => {
|
|
281
|
+
promptService.storePromptTemplate({ id: 'mainWithVariants', template: 'Main template with variants' });
|
|
282
|
+
promptService.storePromptTemplate({ id: 'mainWithoutVariants', template: 'Main template without variants' });
|
|
283
|
+
|
|
284
|
+
promptService.storePromptTemplate({
|
|
285
|
+
id: 'variant1',
|
|
286
|
+
template: 'Variant 1',
|
|
287
|
+
variantOf: 'mainWithVariants'
|
|
288
|
+
});
|
|
289
|
+
promptService.storePromptTemplate({
|
|
290
|
+
id: 'variant2',
|
|
291
|
+
template: 'Variant 2',
|
|
292
|
+
variantOf: 'mainWithVariants'
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const variantsForMainWithVariants = promptService.getVariantIds('mainWithVariants');
|
|
296
|
+
const variantsForMainWithoutVariants = promptService.getVariantIds('mainWithoutVariants');
|
|
297
|
+
|
|
298
|
+
expect(variantsForMainWithVariants).to.deep.equal(['variant1', 'variant2']);
|
|
299
|
+
expect(variantsForMainWithoutVariants).to.deep.equal([]);
|
|
300
|
+
});
|
|
247
301
|
});
|
|
@@ -21,10 +21,16 @@ import { ToolInvocationRegistry } from './tool-invocation-registry';
|
|
|
21
21
|
import { toolRequestToPromptText } from './language-model-util';
|
|
22
22
|
import { ToolRequest } from './language-model';
|
|
23
23
|
import { matchFunctionsRegEx, matchVariablesRegEx } from './prompt-service-util';
|
|
24
|
+
import { AISettingsService } from './settings-service';
|
|
24
25
|
|
|
25
26
|
export interface PromptTemplate {
|
|
26
27
|
id: string;
|
|
27
28
|
template: string;
|
|
29
|
+
/**
|
|
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.
|
|
32
|
+
*/
|
|
33
|
+
variantOf?: string;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
export interface PromptMap { [id: string]: PromptTemplate }
|
|
@@ -63,11 +69,10 @@ export interface PromptService {
|
|
|
63
69
|
*/
|
|
64
70
|
getPrompt(id: string, args?: { [key: string]: unknown }): Promise<ResolvedPromptTemplate | undefined>;
|
|
65
71
|
/**
|
|
66
|
-
* Adds a
|
|
67
|
-
* @param
|
|
68
|
-
* @param prompt the prompt template to store
|
|
72
|
+
* Adds a {@link PromptTemplate} to the list of prompts.
|
|
73
|
+
* @param promptTemplate the prompt template to store
|
|
69
74
|
*/
|
|
70
|
-
|
|
75
|
+
storePromptTemplate(promptTemplate: PromptTemplate): void;
|
|
71
76
|
/**
|
|
72
77
|
* Removes a prompt from the list of prompts.
|
|
73
78
|
* @param id the id of the prompt
|
|
@@ -77,6 +82,20 @@ export interface PromptService {
|
|
|
77
82
|
* Return all known prompts as a {@link PromptMap map}.
|
|
78
83
|
*/
|
|
79
84
|
getAllPrompts(): PromptMap;
|
|
85
|
+
/**
|
|
86
|
+
* Retrieve all variant IDs of a given {@link PromptTemplate}.
|
|
87
|
+
* @param id the id of the main {@link PromptTemplate}
|
|
88
|
+
* @returns an array of string IDs representing the variants of the given template
|
|
89
|
+
*/
|
|
90
|
+
getVariantIds(id: string): string[];
|
|
91
|
+
/**
|
|
92
|
+
* Retrieve the currently selected variant ID for a given main prompt ID.
|
|
93
|
+
* If a variant is selected for the main prompt, it will be returned.
|
|
94
|
+
* Otherwise, the main prompt ID will be returned.
|
|
95
|
+
* @param id the id of the main prompt
|
|
96
|
+
* @returns the variant ID if one is selected, or the main prompt ID otherwise
|
|
97
|
+
*/
|
|
98
|
+
getVariantId(id: string): Promise<string>;
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
export interface CustomAgentDescription {
|
|
@@ -117,6 +136,7 @@ export interface PromptCustomizationService {
|
|
|
117
136
|
*/
|
|
118
137
|
getCustomizedPromptTemplate(id: string): string | undefined
|
|
119
138
|
|
|
139
|
+
getCustomPromptTemplateIDs(): string[];
|
|
120
140
|
/**
|
|
121
141
|
* Edit the template. If the content is specified, is will be
|
|
122
142
|
* used to customize the template. Otherwise, the behavior depends
|
|
@@ -163,6 +183,9 @@ export interface PromptCustomizationService {
|
|
|
163
183
|
|
|
164
184
|
@injectable()
|
|
165
185
|
export class PromptServiceImpl implements PromptService {
|
|
186
|
+
@inject(AISettingsService) @optional()
|
|
187
|
+
protected readonly settingsService: AISettingsService | undefined;
|
|
188
|
+
|
|
166
189
|
@inject(PromptCustomizationService) @optional()
|
|
167
190
|
protected readonly customizationService: PromptCustomizationService | undefined;
|
|
168
191
|
|
|
@@ -203,8 +226,22 @@ export class PromptServiceImpl implements PromptService {
|
|
|
203
226
|
return commentRegex.test(template) ? template.replace(commentRegex, '').trimStart() : template;
|
|
204
227
|
}
|
|
205
228
|
|
|
229
|
+
async getVariantId(id: string): Promise<string> {
|
|
230
|
+
if (this.settingsService !== undefined) {
|
|
231
|
+
const agentSettingsMap = await this.settingsService.getSettings();
|
|
232
|
+
|
|
233
|
+
for (const agentSettings of Object.values(agentSettingsMap)) {
|
|
234
|
+
if (agentSettings.selectedVariants && agentSettings.selectedVariants[id]) {
|
|
235
|
+
return agentSettings.selectedVariants[id];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return id;
|
|
240
|
+
}
|
|
241
|
+
|
|
206
242
|
async getPrompt(id: string, args?: { [key: string]: unknown }): Promise<ResolvedPromptTemplate | undefined> {
|
|
207
|
-
const
|
|
243
|
+
const variantId = await this.getVariantId(id);
|
|
244
|
+
const prompt = this.getUnresolvedPrompt(variantId);
|
|
208
245
|
if (prompt === undefined) {
|
|
209
246
|
return undefined;
|
|
210
247
|
}
|
|
@@ -274,10 +311,24 @@ export class PromptServiceImpl implements PromptService {
|
|
|
274
311
|
return { ...this._prompts };
|
|
275
312
|
}
|
|
276
313
|
}
|
|
277
|
-
storePrompt(id: string, prompt: string): void {
|
|
278
|
-
this._prompts[id] = { id, template: prompt };
|
|
279
|
-
}
|
|
280
314
|
removePrompt(id: string): void {
|
|
281
315
|
delete this._prompts[id];
|
|
282
316
|
}
|
|
317
|
+
getVariantIds(id: string): string[] {
|
|
318
|
+
const allCustomPromptTemplateIds = this.customizationService?.getCustomPromptTemplateIDs() || [];
|
|
319
|
+
const knownPromptIds = Object.keys(this._prompts);
|
|
320
|
+
|
|
321
|
+
// 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
|
|
322
|
+
const customVariantIds = allCustomPromptTemplateIds.filter(customId =>
|
|
323
|
+
!knownPromptIds.includes(customId) && customId.startsWith(id)
|
|
324
|
+
);
|
|
325
|
+
const variantIds = Object.values(this._prompts)
|
|
326
|
+
.filter(prompt => prompt.variantOf === id)
|
|
327
|
+
.map(variant => variant.id);
|
|
328
|
+
|
|
329
|
+
return [...variantIds, ...customVariantIds];
|
|
330
|
+
}
|
|
331
|
+
storePromptTemplate(promptTemplate: PromptTemplate): void {
|
|
332
|
+
this._prompts[promptTemplate.id] = promptTemplate;
|
|
333
|
+
}
|
|
283
334
|
}
|
|
@@ -30,4 +30,9 @@ export type AISettings = Record<string, AgentSettings>;
|
|
|
30
30
|
export interface AgentSettings {
|
|
31
31
|
languageModelRequirements?: LanguageModelRequirement[];
|
|
32
32
|
enable?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* A mapping of main template IDs to their selected variant IDs.
|
|
35
|
+
* If a main template is not present in this mapping, it means the main template is used.
|
|
36
|
+
*/
|
|
37
|
+
selectedVariants?: Record<string, string>;
|
|
33
38
|
}
|