@@ -88,22 +88,8 @@ export class Dotprompt {
8888 this . schemaResolver = options ?. schemaResolver ;
8989 this . partialResolver = options ?. partialResolver ;
9090
91- for ( const key in helpers ) {
92- this . defineHelper ( key , helpers [ key as keyof typeof helpers ] ) ;
93- this . handlebars . registerHelper ( key , helpers [ key as keyof typeof helpers ] ) ;
94- }
95-
96- if ( options ?. helpers ) {
97- for ( const key in options . helpers ) {
98- this . defineHelper ( key , options . helpers [ key ] ) ;
99- }
100- }
101-
102- if ( options ?. partials ) {
103- for ( const key in options . partials ) {
104- this . definePartial ( key , options . partials [ key ] ) ;
105- }
106- }
91+ this . registerInitialHelpers ( options ?. helpers ) ;
92+ this . registerInitialPartials ( options ?. partials ) ;
10793 }
10894
10995 /**
@@ -174,6 +160,106 @@ export class Dotprompt {
174160 return renderer ( data , options ) ;
175161 }
176162
163+ /**
164+ * Compiles a template into a reusable function for rendering prompts.
165+ *
166+ * @param source The template source or parsed prompt to compile
167+ * @param additionalMetadata Additional metadata to include in the compiled template
168+ * @return A promise resolving to a function for rendering the template
169+ */
170+ async compile <
171+ Variables = Record < string , unknown > ,
172+ ModelConfig = Record < string , unknown > ,
173+ > (
174+ source : string | ParsedPrompt < ModelConfig > ,
175+ additionalMetadata ?: PromptMetadata < ModelConfig >
176+ ) : Promise < PromptFunction < ModelConfig > > {
177+ let parsedSource : ParsedPrompt < ModelConfig > ;
178+ if ( typeof source === 'string' ) {
179+ parsedSource = this . parse < ModelConfig > ( source ) ;
180+ } else {
181+ parsedSource = source ;
182+ }
183+
184+ if ( additionalMetadata ) {
185+ parsedSource = { ...parsedSource , ...additionalMetadata } ;
186+ }
187+
188+ // Resolve all partials before compilation.
189+ await this . resolvePartials ( parsedSource . template ) ;
190+
191+ const renderString = this . handlebars . compile < Variables > (
192+ parsedSource . template ,
193+ {
194+ knownHelpers : this . knownHelpers ,
195+ knownHelpersOnly : true ,
196+ noEscape : true ,
197+ }
198+ ) ;
199+
200+ const renderFunc = async (
201+ data : DataArgument ,
202+ options ?: PromptMetadata < ModelConfig >
203+ ) => {
204+ // Discard the input schema as once rendered it doesn't make sense.
205+ const { input, ...mergedMetadata } =
206+ await this . renderMetadata ( parsedSource ) ;
207+
208+ const renderedString = renderString (
209+ { ...( options ?. input ?. default || { } ) , ...data . input } ,
210+ {
211+ data : {
212+ metadata : {
213+ prompt : mergedMetadata ,
214+ docs : data . docs ,
215+ messages : data . messages ,
216+ } ,
217+ ...( data . context || { } ) ,
218+ } ,
219+ }
220+ ) ;
221+
222+ return {
223+ ...mergedMetadata ,
224+ messages : toMessages < ModelConfig > ( renderedString , data ) ,
225+ } ;
226+ } ;
227+ ( renderFunc as PromptFunction < ModelConfig > ) . prompt = parsedSource ;
228+ return renderFunc as PromptFunction < ModelConfig > ;
229+ }
230+
231+ /**
232+ * Processes and resolves all metadata for a prompt template.
233+ *
234+ * @param source The template source or parsed prompt
235+ * @param additionalMetadata Additional metadata to include
236+ * @return A promise resolving to the fully processed metadata
237+ */
238+ async renderMetadata < ModelConfig > (
239+ source : string | ParsedPrompt < ModelConfig > ,
240+ additionalMetadata ?: PromptMetadata < ModelConfig >
241+ ) : Promise < PromptMetadata < ModelConfig > > {
242+ let parsedSource : ParsedPrompt < ModelConfig > ;
243+ if ( typeof source === 'string' ) {
244+ parsedSource = this . parse < ModelConfig > ( source ) ;
245+ } else {
246+ parsedSource = source ;
247+ }
248+
249+ const model =
250+ additionalMetadata ?. model || parsedSource . model || this . defaultModel ;
251+
252+ let modelConfig : ModelConfig | undefined ;
253+ if ( model && this . modelConfigs [ model ] ) {
254+ modelConfig = this . modelConfigs [ model ] as ModelConfig ;
255+ }
256+
257+ return this . resolveMetadata < ModelConfig > (
258+ modelConfig ? { config : modelConfig } : { } ,
259+ parsedSource ,
260+ additionalMetadata
261+ ) ;
262+ }
177263 /**
178264 * Processes schema definitions in picoschema format into standard JSON Schema.
179265 *
@@ -401,103 +487,37 @@ export class Dotprompt {
401487 }
402488
403489 /**
404- * Compiles a template into a reusable function for rendering prompts.
405- *
406- * @param source The template source or parsed prompt to compile
407- * @param additionalMetadata Additional metadata to include in the compiled template
408- * @return A promise resolving to a function for rendering the template
490+ * Registers initial helpers from built-in helpers and options.
491+ * @private
409492 */
410- async compile <
411- Variables = Record < string , unknown > ,
412- ModelConfig = Record < string , unknown > ,
413- > (
414- source : string | ParsedPrompt < ModelConfig > ,
415- additionalMetadata ?: PromptMetadata < ModelConfig >
416- ) : Promise < PromptFunction < ModelConfig > > {
417- let parsedSource : ParsedPrompt < ModelConfig > ;
418- if ( typeof source === 'string' ) {
419- parsedSource = this . parse < ModelConfig > ( source ) ;
420- } else {
421- parsedSource = source ;
422- }
423-
424- if ( additionalMetadata ) {
425- parsedSource = { ...parsedSource , ...additionalMetadata } ;
493+ private registerInitialHelpers (
494+ customHelpers ?: Record < string , Handlebars . HelperDelegate >
495+ ) : void {
496+ // Register built-in helpers
497+ for ( const key in helpers ) {
498+ this . defineHelper ( key , helpers [ key as keyof typeof helpers ] ) ;
499+ this . handlebars . registerHelper ( key , helpers [ key as keyof typeof helpers ] ) ;
426500 }
427501
428- // Resolve all partials before compilation.
429- await this . resolvePartials ( parsedSource . template ) ;
430-
431- const renderString = this . handlebars . compile < Variables > (
432- parsedSource . template ,
433- {
434- knownHelpers : this . knownHelpers ,
435- knownHelpersOnly : true ,
436- noEscape : true ,
502+ // Register custom helpers from options
503+ if ( customHelpers ) {
504+ for ( const key in customHelpers ) {
505+ this . defineHelper ( key , customHelpers [ key ] ) ;
437506 }
438- ) ;
439-
440- const renderFunc = async (
441- data : DataArgument ,
442- options ?: PromptMetadata < ModelConfig >
443- ) => {
444- // Discard the input schema as once rendered it doesn't make sense.
445- const { input, ...mergedMetadata } =
446- await this . renderMetadata ( parsedSource ) ;
447-
448- const renderedString = renderString (
449- { ...( options ?. input ?. default || { } ) , ...data . input } ,
450- {
451- data : {
452- metadata : {
453- prompt : mergedMetadata ,
454- docs : data . docs ,
455- messages : data . messages ,
456- } ,
457- ...( data . context || { } ) ,
458- } ,
459- }
460- ) ;
461-
462- return {
463- ...mergedMetadata ,
464- messages : toMessages < ModelConfig > ( renderedString , data ) ,
465- } ;
466- } ;
467- ( renderFunc as PromptFunction < ModelConfig > ) . prompt = parsedSource ;
468- return renderFunc as PromptFunction < ModelConfig > ;
507+ }
469508 }
470509
471510 /**
472- * Processes and resolves all metadata for a prompt template .
511+ * Registers initial partials from the options .
473512 *
474- * @param source The template source or parsed prompt
475- * @param additionalMetadata Additional metadata to include
476- * @return A promise resolving to the fully processed metadata
513+ * @param partials The partials to register
514+ * @private
477515 */
478- async renderMetadata < ModelConfig > (
479- source : string | ParsedPrompt < ModelConfig > ,
480- additionalMetadata ?: PromptMetadata < ModelConfig >
481- ) : Promise < PromptMetadata < ModelConfig > > {
482- let parsedSource : ParsedPrompt < ModelConfig > ;
483- if ( typeof source === 'string' ) {
484- parsedSource = this . parse < ModelConfig > ( source ) ;
485- } else {
486- parsedSource = source ;
487- }
488-
489- const model =
490- additionalMetadata ?. model || parsedSource . model || this . defaultModel ;
491-
492- let modelConfig : ModelConfig | undefined ;
493- if ( model && this . modelConfigs [ model ] ) {
494- modelConfig = this . modelConfigs [ model ] as ModelConfig ;
516+ private registerInitialPartials ( partials ?: Record < string , string > ) : void {
517+ if ( partials ) {
518+ for ( const key in partials ) {
519+ this . definePartial ( key , partials [ key ] ) ;
520+ }
495521 }
496-
497- return this . resolveMetadata < ModelConfig > (
498- modelConfig ? { config : modelConfig } : { } ,
499- parsedSource ,
500- additionalMetadata
501- ) ;
502522 }
503523}
0 commit comments