-
Notifications
You must be signed in to change notification settings - Fork 50
Open
Description
Bug Description
When using the Compile() method multiple times on the same Dotprompt instance, all compiled PromptFunction closures share the same dp.Template field. This causes subsequent prompt executions to use the wrong template.
Root Cause
In dotprompt/dotprompt.go, the Compile method:
- Creates a new
renderTplfrom parsing the source (line 231) - Stores it to the shared
dp.Templatefield viainitializeTemplate()(line 235) - Returns a closure
renderFuncthat referencesdp.Template(line 262)
func (dp *Dotprompt) Compile(source string, additionalMetadata *PromptMetadata) (PromptFunction, error) {
// ...
renderTpl, err := raymond.Parse(parsedPrompt.Template) // Line 231: Creates local template
// ...
dp.initializeTemplate(renderTpl) // Line 235: Stores to shared dp.Template
renderFunc := func(data *DataArgument, options *PromptMetadata) (RenderedPrompt, error) {
// ...
renderedString, err := dp.Template.ExecWith(inputContext, privDF, ...) // Line 262: Uses shared dp.Template
// ...
}
return renderFunc, nil
}When another prompt is compiled, dp.Template gets overwritten, causing all previous renderFunc closures to use the wrong template.
Steps to Reproduce
dp := dotprompt.NewDotprompt(nil)
// Compile first prompt about weather
prompt1, _ := dp.Compile("Talk about weather: {{topic}}", nil)
// Compile second prompt about programming
prompt2, _ := dp.Compile("Talk about programming: {{language}}", nil)
// Execute first prompt - BUG: uses second template instead!
result1, _ := prompt1(&dotprompt.DataArgument{Input: map[string]any{"topic": "sunny day"}}, nil)
// Expected: "Talk about weather: sunny day"
// Actual: "Talk about programming: sunny day" (wrong template!)Expected Behavior
Each compiled PromptFunction should use its own template, not a shared one.
Proposed Fix
Change the closure to capture the local renderTpl variable instead of referencing dp.Template:
func (dp *Dotprompt) Compile(source string, additionalMetadata *PromptMetadata) (PromptFunction, error) {
// ...
renderTpl, err := raymond.Parse(parsedPrompt.Template)
// ...
dp.initializeTemplate(renderTpl)
// Register helpers and partials
if err = dp.RegisterHelpers(dp.Template); err != nil {
return nil, err
}
if err = dp.RegisterPartials(dp.Template, parsedPrompt.Template); err != nil {
return nil, err
}
// Capture local template for this closure
localTemplate := dp.Template // <-- FIX: capture the current template
renderFunc := func(data *DataArgument, options *PromptMetadata) (RenderedPrompt, error) {
// ...
renderedString, err := localTemplate.ExecWith(inputContext, privDF, ...) // <-- FIX: use localTemplate
// ...
}
return renderFunc, nil
}Environment
- Go version: 1.23
- dotprompt version: v0.0.0-20251105222245-c1c33196b7a4
- Used via: firebase/genkit Go SDK v1.2.0
Impact
This bug affects anyone using genkit.LookupPrompt() with multiple .prompt files. The first prompt executed after loading will use the template of the last loaded prompt.
Metadata
Metadata
Assignees
Labels
No labels