@wundr.io/prompt-templates 1.0.3
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/dist/engine.d.ts +206 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +506 -0
- package/dist/engine.js.map +1 -0
- package/dist/helpers.d.ts +201 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +566 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +102 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +129 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +415 -0
- package/dist/loader.js.map +1 -0
- package/dist/macros.d.ts +64 -0
- package/dist/macros.d.ts.map +1 -0
- package/dist/macros.js +620 -0
- package/dist/macros.js.map +1 -0
- package/dist/types.d.ts +443 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +62 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
- package/src/engine.ts +616 -0
- package/src/helpers.ts +650 -0
- package/src/index.ts +138 -0
- package/src/loader.ts +468 -0
- package/src/macros.ts +635 -0
- package/src/types.ts +380 -0
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wundr/prompt-templates - Built-in Handlebars helpers for prompt templating
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Handlebars from 'handlebars';
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
ToolDefinition,
|
|
9
|
+
ConversationMessage,
|
|
10
|
+
HelperDefinition,
|
|
11
|
+
} from './types.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format tools array into a structured prompt format
|
|
15
|
+
*
|
|
16
|
+
* @param tools - Array of tool definitions
|
|
17
|
+
* @param options - Handlebars helper options
|
|
18
|
+
* @returns Formatted tools string
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* {{formatTools tools}}
|
|
22
|
+
*/
|
|
23
|
+
export function formatTools(
|
|
24
|
+
tools: ToolDefinition[] | undefined,
|
|
25
|
+
options?: Handlebars.HelperOptions,
|
|
26
|
+
): string {
|
|
27
|
+
if (!tools || tools.length === 0) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const format = options?.hash?.['format'] as string | undefined;
|
|
32
|
+
|
|
33
|
+
if (format === 'json') {
|
|
34
|
+
return JSON.stringify(tools, null, 2);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (format === 'compact') {
|
|
38
|
+
return tools.map(t => `- ${t.name}: ${t.description}`).join('\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Default detailed format
|
|
42
|
+
const formatted = tools
|
|
43
|
+
.map(tool => {
|
|
44
|
+
let output = `### ${tool.name}\n${tool.description}`;
|
|
45
|
+
|
|
46
|
+
if (tool.parameters?.properties) {
|
|
47
|
+
output += '\n\n**Parameters:**';
|
|
48
|
+
for (const [name, prop] of Object.entries(tool.parameters.properties)) {
|
|
49
|
+
const required = tool.parameters.required?.includes(name)
|
|
50
|
+
? ' (required)'
|
|
51
|
+
: '';
|
|
52
|
+
const paramProp = prop as { type?: string; description?: string };
|
|
53
|
+
output += `\n- \`${name}\`${required}: ${paramProp.type || 'any'} - ${paramProp.description || 'No description'}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (tool.examples && tool.examples.length > 0) {
|
|
58
|
+
output += '\n\n**Examples:**';
|
|
59
|
+
for (const example of tool.examples) {
|
|
60
|
+
output += `\n\`\`\`\n${example}\n\`\`\``;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return output;
|
|
65
|
+
})
|
|
66
|
+
.join('\n\n---\n\n');
|
|
67
|
+
|
|
68
|
+
return formatted;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Conditionally render block if value is defined and not null/undefined
|
|
73
|
+
*
|
|
74
|
+
* @param value - Value to check
|
|
75
|
+
* @param options - Handlebars helper options
|
|
76
|
+
* @returns Rendered block or empty string
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* {{#ifDefined user.name}}Hello, {{user.name}}{{/ifDefined}}
|
|
80
|
+
*/
|
|
81
|
+
export function ifDefined(
|
|
82
|
+
this: unknown,
|
|
83
|
+
value: unknown,
|
|
84
|
+
options: Handlebars.HelperOptions,
|
|
85
|
+
): string {
|
|
86
|
+
if (value !== undefined && value !== null) {
|
|
87
|
+
return options.fn(this);
|
|
88
|
+
}
|
|
89
|
+
return options.inverse ? options.inverse(this) : '';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Wrap content in a code block with optional language
|
|
94
|
+
*
|
|
95
|
+
* @param language - Programming language for syntax highlighting
|
|
96
|
+
* @param options - Handlebars helper options
|
|
97
|
+
* @returns Code block formatted string
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* {{#codeBlock "javascript"}}
|
|
101
|
+
* const x = 1;
|
|
102
|
+
* {{/codeBlock}}
|
|
103
|
+
*/
|
|
104
|
+
export function codeBlock(
|
|
105
|
+
this: unknown,
|
|
106
|
+
language: string | Handlebars.HelperOptions,
|
|
107
|
+
options?: Handlebars.HelperOptions,
|
|
108
|
+
): string {
|
|
109
|
+
// Handle case where language is not provided
|
|
110
|
+
if (typeof language === 'object' && !options) {
|
|
111
|
+
options = language as Handlebars.HelperOptions;
|
|
112
|
+
language = '';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const content = options?.fn(this) || '';
|
|
116
|
+
const lang = typeof language === 'string' ? language : '';
|
|
117
|
+
return `\`\`\`${lang}\n${content.trim()}\n\`\`\``;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Format memory/conversation history into a readable format
|
|
122
|
+
*
|
|
123
|
+
* @param messages - Array of conversation messages
|
|
124
|
+
* @param options - Handlebars helper options
|
|
125
|
+
* @returns Formatted memory string
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* {{formatMemory memory.messages}}
|
|
129
|
+
*/
|
|
130
|
+
export function formatMemory(
|
|
131
|
+
messages: ConversationMessage[] | undefined,
|
|
132
|
+
options?: Handlebars.HelperOptions,
|
|
133
|
+
): string {
|
|
134
|
+
if (!messages || messages.length === 0) {
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const maxMessages = (options?.hash?.['max'] as number) ?? messages.length;
|
|
139
|
+
const format = options?.hash?.['format'] as string | undefined;
|
|
140
|
+
const slicedMessages = messages.slice(-maxMessages);
|
|
141
|
+
|
|
142
|
+
if (format === 'compact') {
|
|
143
|
+
return slicedMessages
|
|
144
|
+
.map(m => `[${m.role.toUpperCase()}]: ${m.content}`)
|
|
145
|
+
.join('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (format === 'xml') {
|
|
149
|
+
return slicedMessages
|
|
150
|
+
.map(
|
|
151
|
+
m =>
|
|
152
|
+
`<message role="${m.role}"${m.name ? ` name="${m.name}"` : ''}>\n${m.content}\n</message>`,
|
|
153
|
+
)
|
|
154
|
+
.join('\n\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Default format
|
|
158
|
+
return slicedMessages
|
|
159
|
+
.map(m => {
|
|
160
|
+
const roleLabel =
|
|
161
|
+
m.role.charAt(0).toUpperCase() + m.role.slice(1).toLowerCase();
|
|
162
|
+
const nameLabel = m.name ? ` (${m.name})` : '';
|
|
163
|
+
return `**${roleLabel}${nameLabel}:**\n${m.content}`;
|
|
164
|
+
})
|
|
165
|
+
.join('\n\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Repeat content n times
|
|
170
|
+
*
|
|
171
|
+
* @param count - Number of times to repeat
|
|
172
|
+
* @param options - Handlebars helper options
|
|
173
|
+
* @returns Repeated content
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* {{#repeat 3}}Item {{@index}}{{/repeat}}
|
|
177
|
+
*/
|
|
178
|
+
export function repeat(
|
|
179
|
+
this: unknown,
|
|
180
|
+
count: number,
|
|
181
|
+
options: Handlebars.HelperOptions,
|
|
182
|
+
): string {
|
|
183
|
+
const results: string[] = [];
|
|
184
|
+
for (let i = 0; i < count; i++) {
|
|
185
|
+
const data = Handlebars.createFrame(options.data || {});
|
|
186
|
+
data['index'] = i;
|
|
187
|
+
data['first'] = i === 0;
|
|
188
|
+
data['last'] = i === count - 1;
|
|
189
|
+
results.push(options.fn(this, { data }));
|
|
190
|
+
}
|
|
191
|
+
return results.join('');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Format a date value
|
|
196
|
+
*
|
|
197
|
+
* @param date - Date to format
|
|
198
|
+
* @param formatStr - Format string (iso, locale, relative, or custom)
|
|
199
|
+
* @returns Formatted date string
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* {{formatDate timestamp "iso"}}
|
|
203
|
+
*/
|
|
204
|
+
export function formatDate(
|
|
205
|
+
date: Date | string | number | undefined,
|
|
206
|
+
formatStr?: string,
|
|
207
|
+
): string {
|
|
208
|
+
if (!date) {
|
|
209
|
+
return '';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const dateObj = date instanceof Date ? date : new Date(date);
|
|
213
|
+
|
|
214
|
+
if (isNaN(dateObj.getTime())) {
|
|
215
|
+
return '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const format = typeof formatStr === 'string' ? formatStr : 'iso';
|
|
219
|
+
|
|
220
|
+
switch (format) {
|
|
221
|
+
case 'iso':
|
|
222
|
+
return dateObj.toISOString();
|
|
223
|
+
case 'locale':
|
|
224
|
+
return dateObj.toLocaleString();
|
|
225
|
+
case 'date':
|
|
226
|
+
return dateObj.toLocaleDateString();
|
|
227
|
+
case 'time':
|
|
228
|
+
return dateObj.toLocaleTimeString();
|
|
229
|
+
case 'relative': {
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const diff = now - dateObj.getTime();
|
|
232
|
+
const seconds = Math.floor(diff / 1000);
|
|
233
|
+
const minutes = Math.floor(seconds / 60);
|
|
234
|
+
const hours = Math.floor(minutes / 60);
|
|
235
|
+
const days = Math.floor(hours / 24);
|
|
236
|
+
|
|
237
|
+
if (days > 0) {
|
|
238
|
+
return `${days} day${days > 1 ? 's' : ''} ago`;
|
|
239
|
+
}
|
|
240
|
+
if (hours > 0) {
|
|
241
|
+
return `${hours} hour${hours > 1 ? 's' : ''} ago`;
|
|
242
|
+
}
|
|
243
|
+
if (minutes > 0) {
|
|
244
|
+
return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
|
|
245
|
+
}
|
|
246
|
+
return 'just now';
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
return dateObj.toISOString();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* JSON stringify with formatting options
|
|
255
|
+
*
|
|
256
|
+
* @param value - Value to stringify
|
|
257
|
+
* @param options - Handlebars helper options
|
|
258
|
+
* @returns JSON string
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* {{json data indent=2}}
|
|
262
|
+
*/
|
|
263
|
+
export function json(
|
|
264
|
+
value: unknown,
|
|
265
|
+
options?: Handlebars.HelperOptions,
|
|
266
|
+
): string {
|
|
267
|
+
const indent = (options?.hash?.['indent'] as number) ?? 2;
|
|
268
|
+
try {
|
|
269
|
+
return JSON.stringify(value, null, indent);
|
|
270
|
+
} catch {
|
|
271
|
+
return String(value);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Truncate text to a maximum length
|
|
277
|
+
*
|
|
278
|
+
* @param text - Text to truncate
|
|
279
|
+
* @param length - Maximum length
|
|
280
|
+
* @param suffix - Suffix to add when truncated (default: '...')
|
|
281
|
+
* @returns Truncated text
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* {{truncate description 100 "..."}}
|
|
285
|
+
*/
|
|
286
|
+
export function truncate(
|
|
287
|
+
text: string | undefined,
|
|
288
|
+
length?: number | Handlebars.HelperOptions,
|
|
289
|
+
suffix?: string | Handlebars.HelperOptions,
|
|
290
|
+
): string {
|
|
291
|
+
if (!text) {
|
|
292
|
+
return '';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const maxLength = typeof length === 'number' ? length : 100;
|
|
296
|
+
const ellipsis = typeof suffix === 'string' ? suffix : '...';
|
|
297
|
+
|
|
298
|
+
if (text.length <= maxLength) {
|
|
299
|
+
return text;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return text.substring(0, maxLength - ellipsis.length) + ellipsis;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Join array items with a separator
|
|
307
|
+
*
|
|
308
|
+
* @param items - Array to join
|
|
309
|
+
* @param separator - Separator string (default: ', ')
|
|
310
|
+
* @returns Joined string
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* {{join tags ", "}}
|
|
314
|
+
*/
|
|
315
|
+
export function join(
|
|
316
|
+
items: unknown[] | undefined,
|
|
317
|
+
separator?: string | Handlebars.HelperOptions,
|
|
318
|
+
): string {
|
|
319
|
+
if (!items || !Array.isArray(items)) {
|
|
320
|
+
return '';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const sep = typeof separator === 'string' ? separator : ', ';
|
|
324
|
+
return items.map(String).join(sep);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* String comparison helper
|
|
329
|
+
*
|
|
330
|
+
* @param a - First value
|
|
331
|
+
* @param operator - Comparison operator
|
|
332
|
+
* @param b - Second value
|
|
333
|
+
* @param options - Handlebars helper options
|
|
334
|
+
* @returns Rendered block based on comparison result
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* {{#compare role "eq" "admin"}}Admin content{{/compare}}
|
|
338
|
+
*/
|
|
339
|
+
export function compare(
|
|
340
|
+
this: unknown,
|
|
341
|
+
a: unknown,
|
|
342
|
+
operator: string,
|
|
343
|
+
b: unknown,
|
|
344
|
+
options: Handlebars.HelperOptions,
|
|
345
|
+
): string {
|
|
346
|
+
let result = false;
|
|
347
|
+
|
|
348
|
+
switch (operator) {
|
|
349
|
+
case 'eq':
|
|
350
|
+
case '==':
|
|
351
|
+
case '===':
|
|
352
|
+
result = a === b;
|
|
353
|
+
break;
|
|
354
|
+
case 'ne':
|
|
355
|
+
case '!=':
|
|
356
|
+
case '!==':
|
|
357
|
+
result = a !== b;
|
|
358
|
+
break;
|
|
359
|
+
case 'lt':
|
|
360
|
+
case '<':
|
|
361
|
+
result = Number(a) < Number(b);
|
|
362
|
+
break;
|
|
363
|
+
case 'lte':
|
|
364
|
+
case '<=':
|
|
365
|
+
result = Number(a) <= Number(b);
|
|
366
|
+
break;
|
|
367
|
+
case 'gt':
|
|
368
|
+
case '>':
|
|
369
|
+
result = Number(a) > Number(b);
|
|
370
|
+
break;
|
|
371
|
+
case 'gte':
|
|
372
|
+
case '>=':
|
|
373
|
+
result = Number(a) >= Number(b);
|
|
374
|
+
break;
|
|
375
|
+
case 'contains':
|
|
376
|
+
result = typeof a === 'string' && typeof b === 'string' && a.includes(b);
|
|
377
|
+
break;
|
|
378
|
+
case 'startsWith':
|
|
379
|
+
result =
|
|
380
|
+
typeof a === 'string' && typeof b === 'string' && a.startsWith(b);
|
|
381
|
+
break;
|
|
382
|
+
case 'endsWith':
|
|
383
|
+
result = typeof a === 'string' && typeof b === 'string' && a.endsWith(b);
|
|
384
|
+
break;
|
|
385
|
+
default:
|
|
386
|
+
result = false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (result) {
|
|
390
|
+
return options.fn(this);
|
|
391
|
+
}
|
|
392
|
+
return options.inverse ? options.inverse(this) : '';
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Capitalize first letter of string
|
|
397
|
+
*
|
|
398
|
+
* @param text - Text to capitalize
|
|
399
|
+
* @returns Capitalized text
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* {{capitalize name}}
|
|
403
|
+
*/
|
|
404
|
+
export function capitalize(text: string | undefined): string {
|
|
405
|
+
if (!text) {
|
|
406
|
+
return '';
|
|
407
|
+
}
|
|
408
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Convert string to uppercase
|
|
413
|
+
*
|
|
414
|
+
* @param text - Text to convert
|
|
415
|
+
* @returns Uppercase text
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* {{uppercase status}}
|
|
419
|
+
*/
|
|
420
|
+
export function uppercase(text: string | undefined): string {
|
|
421
|
+
if (!text) {
|
|
422
|
+
return '';
|
|
423
|
+
}
|
|
424
|
+
return text.toUpperCase();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Convert string to lowercase
|
|
429
|
+
*
|
|
430
|
+
* @param text - Text to convert
|
|
431
|
+
* @returns Lowercase text
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* {{lowercase status}}
|
|
435
|
+
*/
|
|
436
|
+
export function lowercase(text: string | undefined): string {
|
|
437
|
+
if (!text) {
|
|
438
|
+
return '';
|
|
439
|
+
}
|
|
440
|
+
return text.toLowerCase();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Indent text by a number of spaces
|
|
445
|
+
*
|
|
446
|
+
* @param text - Text to indent
|
|
447
|
+
* @param spaces - Number of spaces (default: 2)
|
|
448
|
+
* @returns Indented text
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* {{indent content 4}}
|
|
452
|
+
*/
|
|
453
|
+
export function indent(
|
|
454
|
+
text: string | undefined,
|
|
455
|
+
spaces?: number | Handlebars.HelperOptions,
|
|
456
|
+
): string {
|
|
457
|
+
if (!text) {
|
|
458
|
+
return '';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const numSpaces = typeof spaces === 'number' ? spaces : 2;
|
|
462
|
+
const indentStr = ' '.repeat(numSpaces);
|
|
463
|
+
return text
|
|
464
|
+
.split('\n')
|
|
465
|
+
.map(line => indentStr + line)
|
|
466
|
+
.join('\n');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Wrap text to a maximum line width
|
|
471
|
+
*
|
|
472
|
+
* @param text - Text to wrap
|
|
473
|
+
* @param width - Maximum line width (default: 80)
|
|
474
|
+
* @returns Wrapped text
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* {{wrap longText 72}}
|
|
478
|
+
*/
|
|
479
|
+
export function wrap(
|
|
480
|
+
text: string | undefined,
|
|
481
|
+
width?: number | Handlebars.HelperOptions,
|
|
482
|
+
): string {
|
|
483
|
+
if (!text) {
|
|
484
|
+
return '';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const maxWidth = typeof width === 'number' ? width : 80;
|
|
488
|
+
const words = text.split(' ');
|
|
489
|
+
const lines: string[] = [];
|
|
490
|
+
let currentLine = '';
|
|
491
|
+
|
|
492
|
+
for (const word of words) {
|
|
493
|
+
if (currentLine.length + word.length + 1 > maxWidth) {
|
|
494
|
+
if (currentLine) {
|
|
495
|
+
lines.push(currentLine);
|
|
496
|
+
}
|
|
497
|
+
currentLine = word;
|
|
498
|
+
} else {
|
|
499
|
+
currentLine = currentLine ? `${currentLine} ${word}` : word;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (currentLine) {
|
|
504
|
+
lines.push(currentLine);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return lines.join('\n');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Create a bulleted list from array
|
|
512
|
+
*
|
|
513
|
+
* @param items - Array of items
|
|
514
|
+
* @param options - Handlebars helper options
|
|
515
|
+
* @returns Bulleted list string
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* {{bulletList items bullet="*"}}
|
|
519
|
+
*/
|
|
520
|
+
export function bulletList(
|
|
521
|
+
items: unknown[] | undefined,
|
|
522
|
+
options?: Handlebars.HelperOptions,
|
|
523
|
+
): string {
|
|
524
|
+
if (!items || !Array.isArray(items)) {
|
|
525
|
+
return '';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const bullet = (options?.hash?.['bullet'] as string) || '-';
|
|
529
|
+
return items.map(item => `${bullet} ${String(item)}`).join('\n');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Create a numbered list from array
|
|
534
|
+
*
|
|
535
|
+
* @param items - Array of items
|
|
536
|
+
* @param options - Handlebars helper options
|
|
537
|
+
* @returns Numbered list string
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* {{numberedList steps start=1}}
|
|
541
|
+
*/
|
|
542
|
+
export function numberedList(
|
|
543
|
+
items: unknown[] | undefined,
|
|
544
|
+
options?: Handlebars.HelperOptions,
|
|
545
|
+
): string {
|
|
546
|
+
if (!items || !Array.isArray(items)) {
|
|
547
|
+
return '';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const start = (options?.hash?.['start'] as number) || 1;
|
|
551
|
+
return items
|
|
552
|
+
.map((item, index) => `${start + index}. ${String(item)}`)
|
|
553
|
+
.join('\n');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Get all built-in helper definitions
|
|
558
|
+
*
|
|
559
|
+
* @returns Array of helper definitions
|
|
560
|
+
*/
|
|
561
|
+
export function getBuiltinHelpers(): HelperDefinition[] {
|
|
562
|
+
return [
|
|
563
|
+
{
|
|
564
|
+
name: 'formatTools',
|
|
565
|
+
description: 'Format tools array into a structured prompt format',
|
|
566
|
+
fn: formatTools as HelperDefinition['fn'],
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
name: 'ifDefined',
|
|
570
|
+
description:
|
|
571
|
+
'Conditionally render block if value is defined and not null/undefined',
|
|
572
|
+
fn: ifDefined as HelperDefinition['fn'],
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: 'codeBlock',
|
|
576
|
+
description: 'Wrap content in a code block with optional language',
|
|
577
|
+
fn: codeBlock as HelperDefinition['fn'],
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
name: 'formatMemory',
|
|
581
|
+
description: 'Format memory/conversation history into a readable format',
|
|
582
|
+
fn: formatMemory as HelperDefinition['fn'],
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
name: 'repeat',
|
|
586
|
+
description: 'Repeat content n times',
|
|
587
|
+
fn: repeat as HelperDefinition['fn'],
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: 'formatDate',
|
|
591
|
+
description: 'Format a date value',
|
|
592
|
+
fn: formatDate as HelperDefinition['fn'],
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: 'json',
|
|
596
|
+
description: 'JSON stringify with formatting options',
|
|
597
|
+
fn: json as HelperDefinition['fn'],
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
name: 'truncate',
|
|
601
|
+
description: 'Truncate text to a maximum length',
|
|
602
|
+
fn: truncate as HelperDefinition['fn'],
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: 'join',
|
|
606
|
+
description: 'Join array items with a separator',
|
|
607
|
+
fn: join as HelperDefinition['fn'],
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
name: 'compare',
|
|
611
|
+
description: 'String comparison helper',
|
|
612
|
+
fn: compare as HelperDefinition['fn'],
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: 'capitalize',
|
|
616
|
+
description: 'Capitalize first letter of string',
|
|
617
|
+
fn: capitalize as HelperDefinition['fn'],
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
name: 'uppercase',
|
|
621
|
+
description: 'Convert string to uppercase',
|
|
622
|
+
fn: uppercase as HelperDefinition['fn'],
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: 'lowercase',
|
|
626
|
+
description: 'Convert string to lowercase',
|
|
627
|
+
fn: lowercase as HelperDefinition['fn'],
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
name: 'indent',
|
|
631
|
+
description: 'Indent text by a number of spaces',
|
|
632
|
+
fn: indent as HelperDefinition['fn'],
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: 'wrap',
|
|
636
|
+
description: 'Wrap text to a maximum line width',
|
|
637
|
+
fn: wrap as HelperDefinition['fn'],
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: 'bulletList',
|
|
641
|
+
description: 'Create a bulleted list from array',
|
|
642
|
+
fn: bulletList as HelperDefinition['fn'],
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
name: 'numberedList',
|
|
646
|
+
description: 'Create a numbered list from array',
|
|
647
|
+
fn: numberedList as HelperDefinition['fn'],
|
|
648
|
+
},
|
|
649
|
+
];
|
|
650
|
+
}
|