@theia/ai-core 1.54.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 (209) hide show
  1. package/README.md +30 -0
  2. package/data/prompttemplate.tmLanguage.json +52 -0
  3. package/lib/browser/ai-activation-service.d.ts +17 -0
  4. package/lib/browser/ai-activation-service.d.ts.map +1 -0
  5. package/lib/browser/ai-activation-service.js +63 -0
  6. package/lib/browser/ai-activation-service.js.map +1 -0
  7. package/lib/browser/ai-command-handler-factory.d.ts +4 -0
  8. package/lib/browser/ai-command-handler-factory.d.ts.map +1 -0
  9. package/lib/browser/ai-command-handler-factory.js +20 -0
  10. package/lib/browser/ai-command-handler-factory.js.map +1 -0
  11. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts +27 -0
  12. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -0
  13. package/lib/browser/ai-configuration/agent-configuration-widget.js +231 -0
  14. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -0
  15. package/lib/browser/ai-configuration/ai-configuration-service.d.ts +13 -0
  16. package/lib/browser/ai-configuration/ai-configuration-service.d.ts.map +1 -0
  17. package/lib/browser/ai-configuration/ai-configuration-service.js +44 -0
  18. package/lib/browser/ai-configuration/ai-configuration-service.js.map +1 -0
  19. package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts +12 -0
  20. package/lib/browser/ai-configuration/ai-configuration-view-contribution.d.ts.map +1 -0
  21. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +41 -0
  22. package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -0
  23. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +20 -0
  24. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -0
  25. package/lib/browser/ai-configuration/ai-configuration-widget.js +88 -0
  26. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -0
  27. package/lib/browser/ai-configuration/language-model-renderer.d.ts +13 -0
  28. package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -0
  29. package/lib/browser/ai-configuration/language-model-renderer.js +104 -0
  30. package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -0
  31. package/lib/browser/ai-configuration/template-settings-renderer.d.ts +11 -0
  32. package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -0
  33. package/lib/browser/ai-configuration/template-settings-renderer.js +33 -0
  34. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -0
  35. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +20 -0
  36. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -0
  37. package/lib/browser/ai-configuration/variable-configuration-widget.js +99 -0
  38. package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -0
  39. package/lib/browser/ai-core-frontend-application-contribution.d.ts +8 -0
  40. package/lib/browser/ai-core-frontend-application-contribution.d.ts.map +1 -0
  41. package/lib/browser/ai-core-frontend-application-contribution.js +46 -0
  42. package/lib/browser/ai-core-frontend-application-contribution.js.map +1 -0
  43. package/lib/browser/ai-core-frontend-module.d.ts +4 -0
  44. package/lib/browser/ai-core-frontend-module.d.ts.map +1 -0
  45. package/lib/browser/ai-core-frontend-module.js +122 -0
  46. package/lib/browser/ai-core-frontend-module.js.map +1 -0
  47. package/lib/browser/ai-core-preferences.d.ts +14 -0
  48. package/lib/browser/ai-core-preferences.d.ts.map +1 -0
  49. package/lib/browser/ai-core-preferences.js +69 -0
  50. package/lib/browser/ai-core-preferences.js.map +1 -0
  51. package/lib/browser/ai-settings-service.d.ts +14 -0
  52. package/lib/browser/ai-settings-service.d.ts.map +1 -0
  53. package/lib/browser/ai-settings-service.js +56 -0
  54. package/lib/browser/ai-settings-service.js.map +1 -0
  55. package/lib/browser/ai-view-contribution.d.ts +14 -0
  56. package/lib/browser/ai-view-contribution.d.ts.map +1 -0
  57. package/lib/browser/ai-view-contribution.js +71 -0
  58. package/lib/browser/ai-view-contribution.js.map +1 -0
  59. package/lib/browser/frontend-language-model-registry.d.ts +39 -0
  60. package/lib/browser/frontend-language-model-registry.d.ts.map +1 -0
  61. package/lib/browser/frontend-language-model-registry.js +321 -0
  62. package/lib/browser/frontend-language-model-registry.js.map +1 -0
  63. package/lib/browser/frontend-prompt-customization-service.d.ts +31 -0
  64. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -0
  65. package/lib/browser/frontend-prompt-customization-service.js +194 -0
  66. package/lib/browser/frontend-prompt-customization-service.js.map +1 -0
  67. package/lib/browser/frontend-variable-service.d.ts +6 -0
  68. package/lib/browser/frontend-variable-service.d.ts.map +1 -0
  69. package/lib/browser/frontend-variable-service.js +31 -0
  70. package/lib/browser/frontend-variable-service.js.map +1 -0
  71. package/lib/browser/index.d.ts +11 -0
  72. package/lib/browser/index.d.ts.map +1 -0
  73. package/lib/browser/index.js +29 -0
  74. package/lib/browser/index.js.map +1 -0
  75. package/lib/browser/prompttemplate-contribution.d.ts +28 -0
  76. package/lib/browser/prompttemplate-contribution.d.ts.map +1 -0
  77. package/lib/browser/prompttemplate-contribution.js +207 -0
  78. package/lib/browser/prompttemplate-contribution.js.map +1 -0
  79. package/lib/browser/theia-variable-contribution.d.ts +13 -0
  80. package/lib/browser/theia-variable-contribution.d.ts.map +1 -0
  81. package/lib/browser/theia-variable-contribution.js +64 -0
  82. package/lib/browser/theia-variable-contribution.js.map +1 -0
  83. package/lib/common/agent-service.d.ts +48 -0
  84. package/lib/common/agent-service.d.ts.map +1 -0
  85. package/lib/common/agent-service.js +88 -0
  86. package/lib/common/agent-service.js.map +1 -0
  87. package/lib/common/agent.d.ts +45 -0
  88. package/lib/common/agent.d.ts.map +1 -0
  89. package/lib/common/agent.js +20 -0
  90. package/lib/common/agent.js.map +1 -0
  91. package/lib/common/agents-variable-contribution.d.ts +19 -0
  92. package/lib/common/agents-variable-contribution.d.ts.map +1 -0
  93. package/lib/common/agents-variable-contribution.js +56 -0
  94. package/lib/common/agents-variable-contribution.js.map +1 -0
  95. package/lib/common/communication-recording-service.d.ts +23 -0
  96. package/lib/common/communication-recording-service.d.ts.map +1 -0
  97. package/lib/common/communication-recording-service.js +20 -0
  98. package/lib/common/communication-recording-service.js.map +1 -0
  99. package/lib/common/index.d.ts +16 -0
  100. package/lib/common/index.d.ts.map +1 -0
  101. package/lib/common/index.js +34 -0
  102. package/lib/common/index.js.map +1 -0
  103. package/lib/common/language-model-delegate.d.ts +24 -0
  104. package/lib/common/language-model-delegate.d.ts.map +1 -0
  105. package/lib/common/language-model-delegate.js +26 -0
  106. package/lib/common/language-model-delegate.js.map +1 -0
  107. package/lib/common/language-model-util.d.ts +5 -0
  108. package/lib/common/language-model-util.d.ts.map +1 -0
  109. package/lib/common/language-model-util.js +75 -0
  110. package/lib/common/language-model-util.js.map +1 -0
  111. package/lib/common/language-model.d.ts +137 -0
  112. package/lib/common/language-model.d.ts.map +1 -0
  113. package/lib/common/language-model.js +142 -0
  114. package/lib/common/language-model.js.map +1 -0
  115. package/lib/common/language-model.spec.d.ts +2 -0
  116. package/lib/common/language-model.spec.d.ts.map +1 -0
  117. package/lib/common/language-model.spec.js +62 -0
  118. package/lib/common/language-model.spec.js.map +1 -0
  119. package/lib/common/prompt-service-util.d.ts +5 -0
  120. package/lib/common/prompt-service-util.d.ts.map +1 -0
  121. package/lib/common/prompt-service-util.js +23 -0
  122. package/lib/common/prompt-service-util.js.map +1 -0
  123. package/lib/common/prompt-service.d.ts +101 -0
  124. package/lib/common/prompt-service.d.ts.map +1 -0
  125. package/lib/common/prompt-service.js +139 -0
  126. package/lib/common/prompt-service.js.map +1 -0
  127. package/lib/common/prompt-service.spec.d.ts +2 -0
  128. package/lib/common/prompt-service.spec.d.ts.map +1 -0
  129. package/lib/common/prompt-service.spec.js +86 -0
  130. package/lib/common/prompt-service.spec.js.map +1 -0
  131. package/lib/common/protocol.d.ts +7 -0
  132. package/lib/common/protocol.d.ts.map +1 -0
  133. package/lib/common/protocol.js +20 -0
  134. package/lib/common/protocol.js.map +1 -0
  135. package/lib/common/settings-service.d.ts +18 -0
  136. package/lib/common/settings-service.d.ts.map +1 -0
  137. package/lib/common/settings-service.js +5 -0
  138. package/lib/common/settings-service.js.map +1 -0
  139. package/lib/common/today-variable-contribution.d.ts +17 -0
  140. package/lib/common/today-variable-contribution.d.ts.map +1 -0
  141. package/lib/common/today-variable-contribution.js +48 -0
  142. package/lib/common/today-variable-contribution.js.map +1 -0
  143. package/lib/common/tomorrow-variable-contribution.d.ts +17 -0
  144. package/lib/common/tomorrow-variable-contribution.d.ts.map +1 -0
  145. package/lib/common/tomorrow-variable-contribution.js +48 -0
  146. package/lib/common/tomorrow-variable-contribution.js.map +1 -0
  147. package/lib/common/tool-invocation-registry.d.ts +23 -0
  148. package/lib/common/tool-invocation-registry.d.ts.map +1 -0
  149. package/lib/common/tool-invocation-registry.js +72 -0
  150. package/lib/common/tool-invocation-registry.js.map +1 -0
  151. package/lib/common/variable-service.d.ts +70 -0
  152. package/lib/common/variable-service.d.ts.map +1 -0
  153. package/lib/common/variable-service.js +122 -0
  154. package/lib/common/variable-service.js.map +1 -0
  155. package/lib/node/ai-core-backend-module.d.ts +4 -0
  156. package/lib/node/ai-core-backend-module.d.ts.map +1 -0
  157. package/lib/node/ai-core-backend-module.js +47 -0
  158. package/lib/node/ai-core-backend-module.js.map +1 -0
  159. package/lib/node/backend-language-model-registry.d.ts +12 -0
  160. package/lib/node/backend-language-model-registry.d.ts.map +1 -0
  161. package/lib/node/backend-language-model-registry.js +61 -0
  162. package/lib/node/backend-language-model-registry.js.map +1 -0
  163. package/lib/node/language-model-frontend-delegate.d.ts +18 -0
  164. package/lib/node/language-model-frontend-delegate.d.ts.map +1 -0
  165. package/lib/node/language-model-frontend-delegate.js +100 -0
  166. package/lib/node/language-model-frontend-delegate.js.map +1 -0
  167. package/package.json +59 -0
  168. package/src/browser/ai-activation-service.ts +56 -0
  169. package/src/browser/ai-command-handler-factory.ts +20 -0
  170. package/src/browser/ai-configuration/agent-configuration-widget.tsx +299 -0
  171. package/src/browser/ai-configuration/ai-configuration-service.ts +43 -0
  172. package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +54 -0
  173. package/src/browser/ai-configuration/ai-configuration-widget.tsx +80 -0
  174. package/src/browser/ai-configuration/language-model-renderer.tsx +113 -0
  175. package/src/browser/ai-configuration/template-settings-renderer.tsx +39 -0
  176. package/src/browser/ai-configuration/variable-configuration-widget.tsx +110 -0
  177. package/src/browser/ai-core-frontend-application-contribution.ts +40 -0
  178. package/src/browser/ai-core-frontend-module.ts +161 -0
  179. package/src/browser/ai-core-preferences.ts +76 -0
  180. package/src/browser/ai-settings-service.ts +50 -0
  181. package/src/browser/ai-view-contribution.ts +77 -0
  182. package/src/browser/frontend-language-model-registry.ts +405 -0
  183. package/src/browser/frontend-prompt-customization-service.ts +197 -0
  184. package/src/browser/frontend-variable-service.ts +26 -0
  185. package/src/browser/index.ts +26 -0
  186. package/src/browser/prompttemplate-contribution.ts +250 -0
  187. package/src/browser/style/index.css +90 -0
  188. package/src/browser/theia-variable-contribution.ts +58 -0
  189. package/src/common/agent-service.ts +108 -0
  190. package/src/common/agent.ts +70 -0
  191. package/src/common/agents-variable-contribution.ts +64 -0
  192. package/src/common/communication-recording-service.ts +44 -0
  193. package/src/common/index.ts +30 -0
  194. package/src/common/language-model-delegate.ts +45 -0
  195. package/src/common/language-model-util.ts +67 -0
  196. package/src/common/language-model.spec.ts +86 -0
  197. package/src/common/language-model.ts +237 -0
  198. package/src/common/prompt-service-util.ts +21 -0
  199. package/src/common/prompt-service.spec.ts +98 -0
  200. package/src/common/prompt-service.ts +213 -0
  201. package/src/common/protocol.ts +23 -0
  202. package/src/common/settings-service.ts +33 -0
  203. package/src/common/today-variable-contribution.ts +67 -0
  204. package/src/common/tomorrow-variable-contribution.ts +66 -0
  205. package/src/common/tool-invocation-registry.ts +79 -0
  206. package/src/common/variable-service.ts +177 -0
  207. package/src/node/ai-core-backend-module.ts +83 -0
  208. package/src/node/backend-language-model-registry.ts +59 -0
  209. package/src/node/language-model-frontend-delegate.ts +116 -0
@@ -0,0 +1,237 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ContributionProvider, ILogger, isFunction, isObject, Event, Emitter, CancellationToken } from '@theia/core';
18
+ import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
19
+
20
+ export type MessageActor = 'user' | 'ai' | 'system';
21
+
22
+ export interface LanguageModelRequestMessage {
23
+ actor: MessageActor;
24
+ type: 'text';
25
+ query: string;
26
+ }
27
+ export const isLanguageModelRequestMessage = (obj: unknown): obj is LanguageModelRequestMessage =>
28
+ !!(obj && typeof obj === 'object' &&
29
+ 'type' in obj &&
30
+ typeof (obj as { type: unknown }).type === 'string' &&
31
+ (obj as { type: unknown }).type === 'text' &&
32
+ 'query' in obj &&
33
+ typeof (obj as { query: unknown }).query === 'string'
34
+ );
35
+ export interface ToolRequest {
36
+ id: string;
37
+ name: string;
38
+ parameters?: { type?: 'object', properties: Record<string, { type: string, [key: string]: unknown }> };
39
+ description?: string;
40
+ handler: (arg_string: string) => Promise<unknown>;
41
+ }
42
+ export interface LanguageModelRequest {
43
+ messages: LanguageModelRequestMessage[],
44
+ tools?: ToolRequest[];
45
+ response_format?: { type: 'text' } | { type: 'json_object' } | ResponseFormatJsonSchema;
46
+ settings?: { [key: string]: unknown };
47
+ }
48
+ export interface ResponseFormatJsonSchema {
49
+ type: 'json_schema';
50
+ json_schema: {
51
+ name: string,
52
+ description?: string,
53
+ schema?: Record<string, unknown>,
54
+ strict?: boolean | null
55
+ };
56
+ }
57
+
58
+ export interface LanguageModelTextResponse {
59
+ text: string;
60
+ }
61
+ export const isLanguageModelTextResponse = (obj: unknown): obj is LanguageModelTextResponse =>
62
+ !!(obj && typeof obj === 'object' && 'text' in obj && typeof (obj as { text: unknown }).text === 'string');
63
+
64
+ export interface LanguageModelStreamResponsePart {
65
+ content?: string | null;
66
+ tool_calls?: ToolCall[];
67
+ }
68
+
69
+ export interface ToolCall {
70
+ id?: string;
71
+ function?: {
72
+ arguments?: string;
73
+ name?: string;
74
+ },
75
+ finished?: boolean;
76
+ result?: string;
77
+ }
78
+
79
+ export interface LanguageModelStreamResponse {
80
+ stream: AsyncIterable<LanguageModelStreamResponsePart>;
81
+ }
82
+ export const isLanguageModelStreamResponse = (obj: unknown): obj is LanguageModelStreamResponse =>
83
+ !!(obj && typeof obj === 'object' && 'stream' in obj);
84
+
85
+ export interface LanguageModelParsedResponse {
86
+ parsed: unknown;
87
+ content: string;
88
+ }
89
+ export const isLanguageModelParsedResponse = (obj: unknown): obj is LanguageModelParsedResponse =>
90
+ !!(obj && typeof obj === 'object' && 'parsed' in obj && 'content' in obj);
91
+
92
+ export type LanguageModelResponse = LanguageModelTextResponse | LanguageModelStreamResponse | LanguageModelParsedResponse;
93
+
94
+ ///////////////////////////////////////////
95
+ // Language Model Provider
96
+ ///////////////////////////////////////////
97
+
98
+ export const LanguageModelProvider = Symbol('LanguageModelProvider');
99
+ export type LanguageModelProvider = () => Promise<LanguageModel[]>;
100
+
101
+ // See also VS Code `ILanguageModelChatMetadata`
102
+ export interface LanguageModelMetaData {
103
+ readonly id: string;
104
+ readonly name?: string;
105
+ readonly vendor?: string;
106
+ readonly version?: string;
107
+ readonly family?: string;
108
+ readonly maxInputTokens?: number;
109
+ readonly maxOutputTokens?: number;
110
+ }
111
+
112
+ export namespace LanguageModelMetaData {
113
+ export function is(arg: unknown): arg is LanguageModelMetaData {
114
+ return isObject(arg) && 'id' in arg;
115
+ }
116
+ }
117
+
118
+ export interface LanguageModel extends LanguageModelMetaData {
119
+ request(request: LanguageModelRequest, cancellationToken?: CancellationToken): Promise<LanguageModelResponse>;
120
+ }
121
+
122
+ export namespace LanguageModel {
123
+ export function is(arg: unknown): arg is LanguageModel {
124
+ return isObject(arg) && 'id' in arg && isFunction(arg.request);
125
+ }
126
+ }
127
+
128
+ // See also VS Code `ILanguageModelChatSelector`
129
+ interface VsCodeLanguageModelSelector {
130
+ readonly identifier?: string;
131
+ readonly name?: string;
132
+ readonly vendor?: string;
133
+ readonly version?: string;
134
+ readonly family?: string;
135
+ readonly tokens?: number;
136
+ }
137
+
138
+ export interface LanguageModelSelector extends VsCodeLanguageModelSelector {
139
+ readonly agent: string;
140
+ readonly purpose: string;
141
+ }
142
+
143
+ export type LanguageModelRequirement = Omit<LanguageModelSelector, 'agent'>;
144
+
145
+ export const LanguageModelRegistry = Symbol('LanguageModelRegistry');
146
+ export interface LanguageModelRegistry {
147
+ onChange: Event<{ models: LanguageModel[] }>;
148
+ addLanguageModels(models: LanguageModel[]): void;
149
+ getLanguageModels(): Promise<LanguageModel[]>;
150
+ getLanguageModel(id: string): Promise<LanguageModel | undefined>;
151
+ removeLanguageModels(id: string[]): void;
152
+ selectLanguageModel(request: LanguageModelSelector): Promise<LanguageModel | undefined>;
153
+ selectLanguageModels(request: LanguageModelSelector): Promise<LanguageModel[]>;
154
+ }
155
+
156
+ @injectable()
157
+ export class DefaultLanguageModelRegistryImpl implements LanguageModelRegistry {
158
+ @inject(ILogger)
159
+ protected logger: ILogger;
160
+ @inject(ContributionProvider) @named(LanguageModelProvider)
161
+ protected readonly languageModelContributions: ContributionProvider<LanguageModelProvider>;
162
+
163
+ protected languageModels: LanguageModel[] = [];
164
+
165
+ protected markInitialized: () => void;
166
+ protected initialized: Promise<void> = new Promise(resolve => { this.markInitialized = resolve; });
167
+
168
+ protected changeEmitter = new Emitter<{ models: LanguageModel[] }>();
169
+ onChange = this.changeEmitter.event;
170
+
171
+ @postConstruct()
172
+ protected init(): void {
173
+ const contributions = this.languageModelContributions.getContributions();
174
+ const promises = contributions.map(provider => provider());
175
+ Promise.allSettled(promises).then(results => {
176
+ for (const result of results) {
177
+ if (result.status === 'fulfilled') {
178
+ this.languageModels.push(...result.value);
179
+ } else {
180
+ this.logger.error('Failed to add some language models:', result.reason);
181
+ }
182
+ }
183
+ this.markInitialized();
184
+ });
185
+ }
186
+
187
+ addLanguageModels(models: LanguageModel[]): void {
188
+ models.forEach(model => {
189
+ if (this.languageModels.find(lm => lm.id === model.id)) {
190
+ console.warn(`Tried to add already existing language model with id ${model.id}. The new model will be ignored.`);
191
+ return;
192
+ }
193
+ this.languageModels.push(model);
194
+ this.changeEmitter.fire({ models: this.languageModels });
195
+ });
196
+ }
197
+
198
+ async getLanguageModels(): Promise<LanguageModel[]> {
199
+ await this.initialized;
200
+ return this.languageModels;
201
+ }
202
+
203
+ async getLanguageModel(id: string): Promise<LanguageModel | undefined> {
204
+ await this.initialized;
205
+ return this.languageModels.find(model => model.id === id);
206
+ }
207
+
208
+ removeLanguageModels(ids: string[]): void {
209
+ ids.forEach(id => {
210
+ const index = this.languageModels.findIndex(model => model.id === id);
211
+ if (index !== -1) {
212
+ this.languageModels.splice(index, 1);
213
+ this.changeEmitter.fire({ models: this.languageModels });
214
+ } else {
215
+ console.warn(`Language model with id ${id} was requested to be removed, however it does not exist`);
216
+ }
217
+ });
218
+ }
219
+
220
+ async selectLanguageModels(request: LanguageModelSelector): Promise<LanguageModel[]> {
221
+ await this.initialized;
222
+ // TODO check for actor and purpose against settings
223
+ return this.languageModels.filter(model => isModelMatching(request, model));
224
+ }
225
+
226
+ async selectLanguageModel(request: LanguageModelSelector): Promise<LanguageModel | undefined> {
227
+ return (await this.selectLanguageModels(request))[0];
228
+ }
229
+ }
230
+
231
+ export function isModelMatching(request: LanguageModelSelector, model: LanguageModel): boolean {
232
+ return (!request.identifier || model.id === request.identifier) &&
233
+ (!request.name || model.name === request.name) &&
234
+ (!request.vendor || model.vendor === request.vendor) &&
235
+ (!request.version || model.version === request.version) &&
236
+ (!request.family || model.family === request.family);
237
+ }
@@ -0,0 +1,21 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ /** Should match the one from VariableResolverService. The format is `{{variableName:arg}}`. */
18
+ export const PROMPT_VARIABLE_REGEX = /\{\{\s*(.*?)\s*\}\}/g;
19
+
20
+ /** Match function/tool references in the prompt. The format is `~{functionId}`. */
21
+ export const PROMPT_FUNCTION_REGEX = /\~\{\s*(.*?)\s*\}/g;
@@ -0,0 +1,98 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import 'reflect-metadata';
18
+
19
+ import { expect } from 'chai';
20
+ import { Container } from 'inversify';
21
+ import { PromptService, PromptServiceImpl } from './prompt-service';
22
+ import { DefaultAIVariableService, AIVariableService } from './variable-service';
23
+
24
+ describe('PromptService', () => {
25
+ let promptService: PromptService;
26
+
27
+ beforeEach(() => {
28
+ const container = new Container();
29
+ container.bind<PromptService>(PromptService).to(PromptServiceImpl).inSingletonScope();
30
+
31
+ const variableService = new DefaultAIVariableService({ getContributions: () => [] });
32
+ const nameVariable = { id: 'test', name: 'name', description: 'Test name ' };
33
+ variableService.registerResolver(nameVariable, {
34
+ canResolve: () => 100,
35
+ resolve: async () => ({ variable: nameVariable, value: 'Jane' })
36
+ });
37
+ container.bind<AIVariableService>(AIVariableService).toConstantValue(variableService);
38
+
39
+ promptService = container.get<PromptService>(PromptService);
40
+ promptService.storePrompt('1', 'Hello, {{name}}!');
41
+ promptService.storePrompt('2', 'Goodbye, {{name}}!');
42
+ promptService.storePrompt('3', 'Ciao, {{invalid}}!');
43
+ });
44
+
45
+ it('should initialize prompts from PromptCollectionService', () => {
46
+ const allPrompts = promptService.getAllPrompts();
47
+ expect(allPrompts['1'].template).to.equal('Hello, {{name}}!');
48
+ expect(allPrompts['2'].template).to.equal('Goodbye, {{name}}!');
49
+ expect(allPrompts['3'].template).to.equal('Ciao, {{invalid}}!');
50
+ });
51
+
52
+ it('should retrieve raw prompt by id', () => {
53
+ const rawPrompt = promptService.getRawPrompt('1');
54
+ expect(rawPrompt?.template).to.equal('Hello, {{name}}!');
55
+ });
56
+
57
+ it('should format prompt with provided arguments', async () => {
58
+ const formattedPrompt = await promptService.getPrompt('1', { name: 'John' });
59
+ expect(formattedPrompt?.text).to.equal('Hello, John!');
60
+ });
61
+
62
+ it('should store a new prompt', () => {
63
+ promptService.storePrompt('3', 'Welcome, {{name}}!');
64
+ const newPrompt = promptService.getRawPrompt('3');
65
+ expect(newPrompt?.template).to.equal('Welcome, {{name}}!');
66
+ });
67
+
68
+ it('should replace placeholders with provided arguments', async () => {
69
+ const prompt = await promptService.getPrompt('1', { name: 'John' });
70
+ expect(prompt?.text).to.equal('Hello, John!');
71
+ });
72
+
73
+ it('should use variable service to resolve placeholders if argument value is not provided', async () => {
74
+ const prompt = await promptService.getPrompt('1');
75
+ expect(prompt?.text).to.equal('Hello, Jane!');
76
+ });
77
+
78
+ it('should return the prompt even if there are no replacements', async () => {
79
+ const prompt = await promptService.getPrompt('3');
80
+ expect(prompt?.text).to.equal('Ciao, {{invalid}}!');
81
+ });
82
+
83
+ it('should return undefined if the prompt id is not found', async () => {
84
+ const prompt = await promptService.getPrompt('4');
85
+ expect(prompt).to.be.undefined;
86
+ });
87
+
88
+ it('should ignore whitespace in variables', async () => {
89
+ promptService.storePrompt('4', 'Hello, {{name }}!');
90
+ promptService.storePrompt('5', 'Hello, {{ name}}!');
91
+ promptService.storePrompt('6', 'Hello, {{ name }}!');
92
+ promptService.storePrompt('7', 'Hello, {{ name }}!');
93
+ for (let i = 4; i <= 7; i++) {
94
+ const prompt = await promptService.getPrompt(`${i}`, { name: 'John' });
95
+ expect(prompt?.text).to.equal('Hello, John!');
96
+ }
97
+ });
98
+ });
@@ -0,0 +1,213 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { URI, Event } from '@theia/core';
18
+ import { inject, injectable, optional } from '@theia/core/shared/inversify';
19
+ import { AIVariableService } from './variable-service';
20
+ import { ToolInvocationRegistry } from './tool-invocation-registry';
21
+ import { toolRequestToPromptText } from './language-model-util';
22
+ import { ToolRequest } from './language-model';
23
+ import { PROMPT_VARIABLE_REGEX, PROMPT_FUNCTION_REGEX } from './prompt-service-util';
24
+
25
+ export interface PromptTemplate {
26
+ id: string;
27
+ template: string;
28
+ }
29
+
30
+ export interface PromptMap { [id: string]: PromptTemplate }
31
+
32
+ export interface ResolvedPromptTemplate {
33
+ id: string;
34
+ /** The resolved prompt text with variables and function requests being replaced. */
35
+ text: string;
36
+ /** All functions referenced in the prompt template. */
37
+ functionDescriptions?: Map<string, ToolRequest>;
38
+ }
39
+
40
+ export const PromptService = Symbol('PromptService');
41
+ export interface PromptService {
42
+ /**
43
+ * Retrieve the raw {@link PromptTemplate} object.
44
+ * @param id the id of the {@link PromptTemplate}
45
+ */
46
+ getRawPrompt(id: string): PromptTemplate | undefined;
47
+ /**
48
+ * Retrieve the default raw {@link PromptTemplate} object.
49
+ * @param id the id of the {@link PromptTemplate}
50
+ */
51
+ getDefaultRawPrompt(id: string): PromptTemplate | undefined;
52
+ /**
53
+ * Allows to directly replace placeholders in the prompt. The supported format is 'Hi {{name}}!'.
54
+ * The placeholder is then searched inside the args object and replaced.
55
+ * Function references are also supported via format '~{functionId}'.
56
+ * @param id the id of the prompt
57
+ * @param args the object with placeholders, mapping the placeholder key to the value
58
+ */
59
+ getPrompt(id: string, args?: { [key: string]: unknown }): Promise<ResolvedPromptTemplate | undefined>;
60
+ /**
61
+ * Manually add a prompt to the list of prompts.
62
+ * @param id the id of the prompt
63
+ * @param prompt the prompt template to store
64
+ */
65
+ storePrompt(id: string, prompt: string): void;
66
+ /**
67
+ * Return all known prompts as a {@link PromptMap map}.
68
+ */
69
+ getAllPrompts(): PromptMap;
70
+ }
71
+
72
+ export const PromptCustomizationService = Symbol('PromptCustomizationService');
73
+ export interface PromptCustomizationService {
74
+ /**
75
+ * Whether there is a customization for a {@link PromptTemplate} object
76
+ * @param id the id of the {@link PromptTemplate} to check
77
+ */
78
+ isPromptTemplateCustomized(id: string): boolean;
79
+
80
+ /**
81
+ * Returns the customization of {@link PromptTemplate} object or undefined if there is none
82
+ * @param id the id of the {@link PromptTemplate} to check
83
+ */
84
+ getCustomizedPromptTemplate(id: string): string | undefined
85
+
86
+ /**
87
+ * Edit the template. If the content is specified, is will be
88
+ * used to customize the template. Otherwise, the behavior depends
89
+ * on the implementation. Implementation may for example decide to
90
+ * open an editor, or request more information from the user, ...
91
+ * @param id the template id.
92
+ * @param content optional content to customize the template.
93
+ */
94
+ editTemplate(id: string, content?: string): void;
95
+
96
+ /**
97
+ * Reset the template to its default value.
98
+ * @param id the template id.
99
+ */
100
+ resetTemplate(id: string): void;
101
+
102
+ /**
103
+ * Return the template id for a given template file.
104
+ * @param uri the uri of the template file
105
+ */
106
+ getTemplateIDFromURI(uri: URI): string | undefined;
107
+
108
+ /**
109
+ * Event which is fired when the prompt template is changed.
110
+ */
111
+ readonly onDidChangePrompt: Event<string>;
112
+ }
113
+
114
+ @injectable()
115
+ export class PromptServiceImpl implements PromptService {
116
+ @inject(PromptCustomizationService) @optional()
117
+ protected readonly customizationService: PromptCustomizationService | undefined;
118
+
119
+ @inject(AIVariableService) @optional()
120
+ protected readonly variableService: AIVariableService | undefined;
121
+
122
+ @inject(ToolInvocationRegistry) @optional()
123
+ protected readonly toolInvocationRegistry: ToolInvocationRegistry | undefined;
124
+
125
+ protected _prompts: PromptMap = {};
126
+
127
+ getRawPrompt(id: string): PromptTemplate | undefined {
128
+ if (this.customizationService !== undefined && this.customizationService.isPromptTemplateCustomized(id)) {
129
+ const template = this.customizationService.getCustomizedPromptTemplate(id);
130
+ if (template !== undefined) {
131
+ return { id, template };
132
+ }
133
+ }
134
+ return this.getDefaultRawPrompt(id);
135
+ }
136
+ getDefaultRawPrompt(id: string): PromptTemplate | undefined {
137
+ return this._prompts[id];
138
+ }
139
+ async getPrompt(id: string, args?: { [key: string]: unknown }): Promise<ResolvedPromptTemplate | undefined> {
140
+ const prompt = this.getRawPrompt(id);
141
+ if (prompt === undefined) {
142
+ return undefined;
143
+ }
144
+
145
+ const matches = [...prompt.template.matchAll(PROMPT_VARIABLE_REGEX)];
146
+ const variableAndArgReplacements = await Promise.all(matches.map(async match => {
147
+ const completeText = match[0];
148
+ const variableAndArg = match[1];
149
+ let variableName = variableAndArg;
150
+ let argument: string | undefined;
151
+ const parts = variableAndArg.split(':', 2);
152
+ if (parts.length > 1) {
153
+ variableName = parts[0];
154
+ argument = parts[1];
155
+ }
156
+ return {
157
+ placeholder: completeText,
158
+ value: String(args?.[variableAndArg] ?? (await this.variableService?.resolveVariable({
159
+ variable: variableName,
160
+ arg: argument
161
+ }, {}))?.value ?? completeText)
162
+ };
163
+ }));
164
+
165
+ const functionMatches = [...prompt.template.matchAll(PROMPT_FUNCTION_REGEX)];
166
+ const functions = new Map<string, ToolRequest>();
167
+ const functionReplacements = functionMatches.map(match => {
168
+ const completeText = match[0];
169
+ const functionId = match[1];
170
+ const toolRequest = this.toolInvocationRegistry?.getFunction(functionId);
171
+ if (toolRequest) {
172
+ functions.set(toolRequest.id, toolRequest);
173
+ }
174
+ return {
175
+ placeholder: completeText,
176
+ value: toolRequest ? toolRequestToPromptText(toolRequest) : completeText
177
+ };
178
+ });
179
+
180
+ let resolvedTemplate = prompt.template;
181
+ const replacements = [...variableAndArgReplacements, ...functionReplacements];
182
+ replacements.forEach(replacement => resolvedTemplate = resolvedTemplate.replace(replacement.placeholder, replacement.value));
183
+ return {
184
+ id,
185
+ text: resolvedTemplate,
186
+ functionDescriptions: functions.size > 0 ? functions : undefined
187
+ };
188
+ }
189
+ getAllPrompts(): PromptMap {
190
+ if (this.customizationService !== undefined) {
191
+ const myCustomization = this.customizationService;
192
+ const result: PromptMap = {};
193
+ Object.keys(this._prompts).forEach(id => {
194
+ if (myCustomization.isPromptTemplateCustomized(id)) {
195
+ const template = myCustomization.getCustomizedPromptTemplate(id);
196
+ if (template !== undefined) {
197
+ result[id] = { id, template };
198
+ } else {
199
+ result[id] = { ...this._prompts[id] };
200
+ }
201
+ } else {
202
+ result[id] = { ...this._prompts[id] };
203
+ }
204
+ });
205
+ return result;
206
+ } else {
207
+ return { ...this._prompts };
208
+ }
209
+ }
210
+ storePrompt(id: string, prompt: string): void {
211
+ this._prompts[id] = { id, template: prompt };
212
+ }
213
+ }
@@ -0,0 +1,23 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { LanguageModelMetaData } from './language-model';
18
+
19
+ export const LanguageModelRegistryClient = Symbol('LanguageModelRegistryClient');
20
+ export interface LanguageModelRegistryClient {
21
+ languageModelAdded(metadata: LanguageModelMetaData): void;
22
+ languageModelRemoved(id: string): void;
23
+ }
@@ -0,0 +1,33 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ import { Event } from '@theia/core';
17
+ import { LanguageModelRequirement } from './language-model';
18
+
19
+ export const AISettingsService = Symbol('AISettingsService');
20
+ /**
21
+ * Service to store and retrieve settings on a per-agent basis.
22
+ */
23
+ export interface AISettingsService {
24
+ updateAgentSettings(agent: string, agentSettings: Partial<AgentSettings>): Promise<void>;
25
+ getAgentSettings(agent: string): Promise<AgentSettings | undefined>;
26
+ getSettings(): Promise<AISettings>;
27
+ onDidChange: Event<void>;
28
+ }
29
+ export type AISettings = Record<string, AgentSettings>;
30
+ export interface AgentSettings {
31
+ languageModelRequirements?: LanguageModelRequirement[];
32
+ enable?: boolean;
33
+ }