@theia/ai-core 1.55.0 → 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.
Files changed (54) hide show
  1. package/data/prompttemplate.tmLanguage.json +60 -5
  2. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
  3. package/lib/browser/ai-configuration/agent-configuration-widget.js +10 -5
  4. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
  5. package/lib/browser/ai-configuration/language-model-renderer.js +1 -1
  6. package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
  7. package/lib/browser/ai-configuration/template-settings-renderer.d.ts +6 -4
  8. package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
  9. package/lib/browser/ai-configuration/template-settings-renderer.js +49 -11
  10. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
  11. package/lib/browser/ai-core-preferences.d.ts +15 -0
  12. package/lib/browser/ai-core-preferences.d.ts.map +1 -1
  13. package/lib/browser/ai-core-preferences.js +30 -1
  14. package/lib/browser/ai-core-preferences.js.map +1 -1
  15. package/lib/browser/frontend-prompt-customization-service.d.ts +1 -0
  16. package/lib/browser/frontend-prompt-customization-service.d.ts.map +1 -1
  17. package/lib/browser/frontend-prompt-customization-service.js +6 -0
  18. package/lib/browser/frontend-prompt-customization-service.js.map +1 -1
  19. package/lib/common/agent-service.js +1 -1
  20. package/lib/common/agent-service.js.map +1 -1
  21. package/lib/common/agent.d.ts +1 -1
  22. package/lib/common/communication-recording-service.d.ts +5 -2
  23. package/lib/common/communication-recording-service.d.ts.map +1 -1
  24. package/lib/common/communication-recording-service.js.map +1 -1
  25. package/lib/common/language-model.d.ts +7 -0
  26. package/lib/common/language-model.d.ts.map +1 -1
  27. package/lib/common/language-model.js.map +1 -1
  28. package/lib/common/prompt-service-util.d.ts +5 -2
  29. package/lib/common/prompt-service-util.d.ts.map +1 -1
  30. package/lib/common/prompt-service-util.js +14 -3
  31. package/lib/common/prompt-service-util.js.map +1 -1
  32. package/lib/common/prompt-service.d.ts +36 -6
  33. package/lib/common/prompt-service.d.ts.map +1 -1
  34. package/lib/common/prompt-service.js +49 -6
  35. package/lib/common/prompt-service.js.map +1 -1
  36. package/lib/common/prompt-service.spec.js +176 -8
  37. package/lib/common/prompt-service.spec.js.map +1 -1
  38. package/lib/common/settings-service.d.ts +5 -0
  39. package/lib/common/settings-service.d.ts.map +1 -1
  40. package/package.json +10 -10
  41. package/src/browser/ai-configuration/agent-configuration-widget.tsx +28 -11
  42. package/src/browser/ai-configuration/language-model-renderer.tsx +1 -1
  43. package/src/browser/ai-configuration/template-settings-renderer.tsx +105 -16
  44. package/src/browser/ai-core-preferences.ts +40 -1
  45. package/src/browser/frontend-prompt-customization-service.ts +8 -0
  46. package/src/browser/style/index.css +37 -5
  47. package/src/common/agent-service.ts +1 -1
  48. package/src/common/agent.ts +1 -1
  49. package/src/common/communication-recording-service.ts +6 -2
  50. package/src/common/language-model.ts +5 -0
  51. package/src/common/prompt-service-util.ts +12 -2
  52. package/src/common/prompt-service.spec.ts +211 -8
  53. package/src/common/prompt-service.ts +85 -12
  54. package/src/common/settings-service.ts +5 -0
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@theia/ai-core",
3
- "version": "1.55.0",
3
+ "version": "1.56.0",
4
4
  "description": "Theia - AI Core",
5
5
  "dependencies": {
6
- "@theia/core": "1.55.0",
7
- "@theia/editor": "1.55.0",
8
- "@theia/filesystem": "1.55.0",
9
- "@theia/monaco": "1.55.0",
6
+ "@theia/core": "1.56.0",
7
+ "@theia/editor": "1.56.0",
8
+ "@theia/filesystem": "1.56.0",
9
+ "@theia/monaco": "1.56.0",
10
10
  "@theia/monaco-editor-core": "1.83.101",
11
- "@theia/output": "1.55.0",
12
- "@theia/variable-resolver": "1.55.0",
13
- "@theia/workspace": "1.55.0",
11
+ "@theia/output": "1.56.0",
12
+ "@theia/variable-resolver": "1.56.0",
13
+ "@theia/workspace": "1.56.0",
14
14
  "@types/js-yaml": "^4.0.9",
15
15
  "js-yaml": "^4.1.0",
16
16
  "minimatch": "^5.1.0",
@@ -52,10 +52,10 @@
52
52
  "watch": "theiaext watch"
53
53
  },
54
54
  "devDependencies": {
55
- "@theia/ext-scripts": "1.55.0"
55
+ "@theia/ext-scripts": "1.56.0"
56
56
  },
57
57
  "nyc": {
58
58
  "extends": "../../configs/nyc.json"
59
59
  },
60
- "gitHead": "0e7a523b8e798679d2e098709c63bd7060e54c8a"
60
+ "gitHead": "3b4498f8f1c66be688847418fcc35595282671dc"
61
61
  }
@@ -23,8 +23,8 @@ import {
23
23
  AIVariableService,
24
24
  LanguageModel,
25
25
  LanguageModelRegistry,
26
+ matchVariablesRegEx,
26
27
  PROMPT_FUNCTION_REGEX,
27
- PROMPT_VARIABLE_REGEX,
28
28
  PromptCustomizationService,
29
29
  PromptService,
30
30
  } from '../../common';
@@ -112,7 +112,7 @@ export class AIAgentConfigurationWidget extends ReactWidget {
112
112
  }
113
113
 
114
114
  private renderAgentName(agent: Agent): React.ReactNode {
115
- const tagsSuffix = agent.tags?.length ? <span>{agent.tags.map(tag => <span className='agent-tag'>{tag}</span>)}</span> : '';
115
+ const tagsSuffix = agent.tags?.length ? <span>{agent.tags.map(tag => <span key={tag} className='agent-tag'>{tag}</span>)}</span> : '';
116
116
  return <span>{agent.name} {tagsSuffix}</span>;
117
117
  }
118
118
 
@@ -137,14 +137,31 @@ export class AIAgentConfigurationWidget extends ReactWidget {
137
137
  Enable Agent
138
138
  </label>
139
139
  </div>
140
- <div className='ai-templates'>
141
- {agent.promptTemplates?.map(template =>
142
- <TemplateRenderer
143
- key={agent?.id + '.' + template.id}
144
- agentId={agent.id}
145
- template={template}
146
- promptCustomizationService={this.promptCustomizationService} />)}
140
+ <div className="settings-section-subcategory-title ai-settings-section-subcategory-title">
141
+ Prompt Templates
147
142
  </div>
143
+ <div className="ai-templates">
144
+ {(() => {
145
+ const defaultTemplates = agent.promptTemplates?.filter(template => !template.variantOf) || [];
146
+ return defaultTemplates.length > 0 ? (
147
+ defaultTemplates.map(template => (
148
+ <div key={agent.id + '.' + template.id}>
149
+ <TemplateRenderer
150
+ key={agent.id + '.' + template.id}
151
+ agentId={agent.id}
152
+ template={template}
153
+ promptService={this.promptService}
154
+ aiSettingsService={this.aiSettingsService}
155
+ promptCustomizationService={this.promptCustomizationService}
156
+ />
157
+ </div>
158
+ ))
159
+ ) : (
160
+ <div>No default template available</div>
161
+ );
162
+ })()}
163
+ </div>
164
+
148
165
  <div className='ai-lm-requirements'>
149
166
  <LanguageModelRenderer
150
167
  agent={agent}
@@ -180,9 +197,9 @@ export class AIAgentConfigurationWidget extends ReactWidget {
180
197
  const promptTemplates = agent.promptTemplates;
181
198
  const result: ParsedPrompt = { functions: [], globalVariables: [], agentSpecificVariables: [] };
182
199
  promptTemplates.forEach(template => {
183
- const storedPrompt = this.promptService.getRawPrompt(template.id);
200
+ const storedPrompt = this.promptService.getUnresolvedPrompt(template.id);
184
201
  const prompt = storedPrompt?.template ?? template.template;
185
- const variableMatches = [...prompt.matchAll(PROMPT_VARIABLE_REGEX)];
202
+ const variableMatches = matchVariablesRegEx(prompt);
186
203
 
187
204
  variableMatches.forEach(match => {
188
205
  const variableId = match[1];
@@ -99,7 +99,7 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
99
99
  onChange={event => onSelectedModelChange(requirements.purpose, event)}
100
100
  >
101
101
  <option value=""></option>
102
- {languageModels?.map(model => (
102
+ {languageModels?.sort((a, b) => (a.name ?? a.id).localeCompare(b.name ?? b.id)).map(model => (
103
103
  <option key={model.id} value={model.id}>{model.name ?? model.id}</option>
104
104
  ))}
105
105
  </select>
@@ -14,26 +14,115 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
  import * as React from '@theia/core/shared/react';
17
- import { PromptCustomizationService } from '../../common/prompt-service';
18
- import { PromptTemplate } from '../../common';
17
+ import { PromptCustomizationService, PromptService } from '../../common/prompt-service';
18
+ import { AISettingsService, PromptTemplate } from '../../common';
19
19
 
20
- export interface TemplateSettingProps {
20
+ const DEFAULT_VARIANT = 'default';
21
+
22
+ export interface TemplateRendererProps {
21
23
  agentId: string;
22
24
  template: PromptTemplate;
23
25
  promptCustomizationService: PromptCustomizationService;
26
+ promptService: PromptService;
27
+ aiSettingsService: AISettingsService;
24
28
  }
25
29
 
26
- export const TemplateRenderer: React.FC<TemplateSettingProps> = ({ agentId, template, promptCustomizationService }) => {
27
- const openTemplate = React.useCallback(async () => {
28
- promptCustomizationService.editTemplate(template.id, template.template);
29
- }, [template, promptCustomizationService]);
30
- const resetTemplate = React.useCallback(async () => {
31
- promptCustomizationService.resetTemplate(template.id);
32
- }, [promptCustomizationService, template]);
33
-
34
- return <>
35
- {template.id}
36
- <button className='theia-button main' onClick={openTemplate}>Edit</button>
37
- <button className='theia-button secondary' onClick={resetTemplate}>Reset</button>
38
- </>;
30
+ export const TemplateRenderer: React.FC<TemplateRendererProps> = ({
31
+ agentId,
32
+ template,
33
+ promptCustomizationService,
34
+ promptService,
35
+ aiSettingsService,
36
+ }) => {
37
+ const variantIds = [DEFAULT_VARIANT, ...promptService.getVariantIds(template.id)];
38
+ const [selectedVariant, setSelectedVariant] = React.useState<string>(DEFAULT_VARIANT);
39
+
40
+ React.useEffect(() => {
41
+ (async () => {
42
+ const agentSettings = await aiSettingsService.getAgentSettings(agentId);
43
+ const currentVariant =
44
+ agentSettings?.selectedVariants?.[template.id] || DEFAULT_VARIANT;
45
+ setSelectedVariant(currentVariant);
46
+ })();
47
+ }, [template.id, aiSettingsService, agentId]);
48
+
49
+ const isInvalidVariant = !variantIds.includes(selectedVariant);
50
+
51
+ const handleVariantChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
52
+ const newVariant = event.target.value;
53
+ setSelectedVariant(newVariant);
54
+
55
+ const agentSettings = await aiSettingsService.getAgentSettings(agentId);
56
+ const selectedVariants = agentSettings?.selectedVariants || {};
57
+
58
+ const updatedVariants = { ...selectedVariants };
59
+ if (newVariant === DEFAULT_VARIANT) {
60
+ delete updatedVariants[template.id];
61
+ } else {
62
+ updatedVariants[template.id] = newVariant;
63
+ }
64
+
65
+ await aiSettingsService.updateAgentSettings(agentId, {
66
+ selectedVariants: updatedVariants,
67
+ });
68
+ };
69
+
70
+ const openTemplate = () => {
71
+ const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant;
72
+ const selectedTemplate = promptService.getRawPrompt(templateId);
73
+ promptCustomizationService.editTemplate(templateId, selectedTemplate?.template || '');
74
+ };
75
+
76
+ const resetTemplate = () => {
77
+ const templateId = selectedVariant === DEFAULT_VARIANT ? template.id : selectedVariant;
78
+ promptCustomizationService.resetTemplate(templateId);
79
+ };
80
+
81
+ return (
82
+ <div className="template-renderer">
83
+ <div className="settings-section-title template-header">
84
+ <strong>{template.id}</strong>
85
+ </div>
86
+ <div className="template-controls">
87
+ {(variantIds.length > 1 || isInvalidVariant) && (
88
+ <>
89
+ <label htmlFor={`variant-selector-${template.id}`} className="template-select-label">
90
+ Select Variant:
91
+ </label>
92
+ <select
93
+ id={`variant-selector-${template.id}`}
94
+ className={`theia-select template-variant-selector ${isInvalidVariant ? 'error' : ''}`}
95
+ value={isInvalidVariant ? 'invalid' : selectedVariant}
96
+ onChange={handleVariantChange}
97
+ >
98
+ {isInvalidVariant && (
99
+ <option value="invalid" disabled>
100
+ The selected variant is no longer available
101
+ </option>
102
+ )}
103
+ {variantIds.map(variantId => (
104
+ <option key={variantId} value={variantId}>
105
+ {variantId}
106
+ </option>
107
+ ))}
108
+ </select>
109
+ </>
110
+ )}
111
+ <button
112
+ className="theia-button main"
113
+ onClick={openTemplate}
114
+ disabled={isInvalidVariant}
115
+ >
116
+ Edit
117
+ </button>
118
+ <button
119
+ className="theia-button secondary"
120
+ onClick={resetTemplate}
121
+ disabled={isInvalidVariant}
122
+ >
123
+ Reset
124
+ </button>
125
+ </div>
126
+ </div>
127
+ );
39
128
  };
@@ -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');
@@ -116,6 +116,10 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati
116
116
  }));
117
117
 
118
118
  this.onDidChangeCustomAgentsEmitter.fire();
119
+
120
+ if (!(await this.fileService.exists(templateURI))) {
121
+ return;
122
+ }
119
123
  const stat = await this.fileService.resolve(templateURI);
120
124
  if (stat.children === undefined) {
121
125
  return;
@@ -165,6 +169,10 @@ export class FrontendPromptCustomizationServiceImpl implements PromptCustomizati
165
169
  return this.templates.get(id);
166
170
  }
167
171
 
172
+ getCustomPromptTemplateIDs(): string[] {
173
+ return Array.from(this.templates.keys());
174
+ }
175
+
168
176
  async editTemplate(id: string, defaultContent?: string): Promise<void> {
169
177
  const editorUri = await this.getTemplateURI(id);
170
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: grid;
19
- /** Display content in 3 columns */
20
- grid-template-columns: 1fr auto auto;
21
- /** add a 3px gap between rows */
22
- row-gap: 3px;
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.storePrompt(template.id, template.template)
102
+ template => this.promptService.storePromptTemplate(template)
103
103
  );
104
104
  this.onDidChangeAgentsEmitter.fire();
105
105
  }
@@ -60,7 +60,7 @@ export interface Agent {
60
60
  readonly languageModelRequirements: LanguageModelRequirement[];
61
61
 
62
62
  /** A list of tags to filter agents and to display capabilities in the UI */
63
- readonly tags?: String[];
63
+ readonly tags?: string[];
64
64
 
65
65
  /** The list of local variable identifiers this agent needs to clarify its context requirements. */
66
66
  readonly agentSpecificVariables: AgentSpecificVariables[];
@@ -26,18 +26,22 @@ export interface CommunicationHistoryEntry {
26
26
  request?: string;
27
27
  response?: string;
28
28
  responseTime?: number;
29
+ systemMessage?: string;
29
30
  messages?: unknown[];
30
31
  }
31
32
 
32
33
  export type CommunicationRequestEntry = Omit<CommunicationHistoryEntry, 'response' | 'responseTime'>;
33
34
  export type CommunicationResponseEntry = Omit<CommunicationHistoryEntry, 'request'>;
34
35
 
36
+ export type CommunicationRequestEntryParam = Omit<CommunicationRequestEntry, 'timestamp'> & Partial<Pick<CommunicationHistoryEntry, 'timestamp'>>;
37
+ export type CommunicationResponseEntryParam = Omit<CommunicationResponseEntry, 'timestamp'> & Partial<Pick<CommunicationHistoryEntry, 'timestamp'>>;
38
+
35
39
  export const CommunicationRecordingService = Symbol('CommunicationRecordingService');
36
40
  export interface CommunicationRecordingService {
37
- recordRequest(requestEntry: CommunicationRequestEntry): void;
41
+ recordRequest(requestEntry: CommunicationRequestEntryParam): void;
38
42
  readonly onDidRecordRequest: Event<CommunicationRequestEntry>;
39
43
 
40
- recordResponse(responseEntry: CommunicationResponseEntry): void;
44
+ recordResponse(responseEntry: CommunicationResponseEntryParam): void;
41
45
  readonly onDidRecordResponse: Event<CommunicationResponseEntry>;
42
46
 
43
47
  getHistory(agentId: string): CommunicationHistory;
@@ -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 {
@@ -14,8 +14,18 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- /** Should match the one from VariableResolverService. The format is `{{variableName:arg}}`. */
18
- export const PROMPT_VARIABLE_REGEX = /\{\{\s*(.*?)\s*\}\}/g;
17
+ /** Should match the one from VariableResolverService. The format is `{{variableName:arg}}`. We allow {{}} and {{{}}} but no mixtures */
18
+ export const PROMPT_VARIABLE_TWO_BRACES_REGEX = /(?<!\{)\{\{\s*([^{}]+?)\s*\}\}(?!\})/g;
19
+ export const PROMPT_VARIABLE_THREE_BRACES_REGEX = /(?<!\{)\{\{\{\s*([^{}]+?)\s*\}\}\}(?!\})/g;
20
+ export function matchVariablesRegEx(template: string): RegExpMatchArray[] {
21
+ const twoBraceMatches = [...template.matchAll(PROMPT_VARIABLE_TWO_BRACES_REGEX)];
22
+ const threeBraceMatches = [...template.matchAll(PROMPT_VARIABLE_THREE_BRACES_REGEX)];
23
+ return twoBraceMatches.concat(threeBraceMatches);
24
+ }
19
25
 
20
26
  /** Match function/tool references in the prompt. The format is `~{functionId}`. */
21
27
  export const PROMPT_FUNCTION_REGEX = /\~\{\s*(.*?)\s*\}/g;
28
+
29
+ export function matchFunctionsRegEx(template: string): RegExpMatchArray[] {
30
+ return [...template.matchAll(PROMPT_FUNCTION_REGEX)];
31
+ }