Render captured prompt templates without reloading sources

This commit is contained in:
2026-09-30 03:24:12 +00:00
parent ba9c972faa
commit d818e0d162
6 changed files with 264 additions and 4 deletions

View File

@@ -333,3 +333,15 @@ func (state *loaded) GetPromptDefinition(ctx context.Context, id, version string
definition, _, err := state.GetPromptSnapshot(ctx, id, version)
return definition, err
}
// GetCompiledTemplates returns construction-time parses for one exact prompt.
func (state *loaded) GetCompiledTemplates(id, version string) (prompt.CompiledTemplates, bool) {
if state == nil {
return prompt.CompiledTemplates{}, false
}
entry := state.prompts[id][version]
if entry == nil {
return prompt.CompiledTemplates{}, false
}
return prompt.CompiledTemplates{Session: entry.session, Messages: entry.messages}, true
}

View File

@@ -131,7 +131,7 @@ func New(cfg Config, options Options) (*Engine, error) {
reader = artifact.NewCompositeReader()
}
return &Engine{runner: usecase.NewRunnerWithRepairer(snapshot, snapshot, snapshot, reader, prompt.NewGoRenderer(), client, validator, usecase.NewDefaultOutputRepairer(client), manager, defaultRetries)}, nil
return &Engine{runner: usecase.NewRunnerWithRepairer(snapshot, snapshot, snapshot, reader, prompt.NewCompiledRenderer(snapshot), client, validator, usecase.NewDefaultOutputRepairer(client), manager, defaultRetries)}, nil
}
func validateSourceFS(fsys fs.FS, root string) error {

View File

@@ -21,12 +21,20 @@ var (
const artifactTextChunkSize = 64 * 1024
type goRenderer struct{}
type goRenderer struct {
templates TemplateLookup
}
func NewGoRenderer() Renderer {
return &goRenderer{}
}
// NewCompiledRenderer renders definitions using templates captured with the
// configuration snapshot. Per-call clones bind request-specific input helpers.
func NewCompiledRenderer(templates TemplateLookup) Renderer {
return &goRenderer{templates: templates}
}
func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefinition, inputs map[string]*domain.Artifact, vars map[string]string) (*domain.RenderedPrompt, error) {
if err := ctx.Err(); err != nil {
return nil, err
@@ -49,11 +57,30 @@ func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefini
funcs := template.FuncMap{
"input": resolver.resolve,
}
var compiled CompiledTemplates
if r.templates != nil {
var found bool
compiled, found = r.templates.GetCompiledTemplates(definition.ID, definition.Version)
if !found || len(compiled.Messages) != len(definition.Templates) {
return nil, fmt.Errorf("%w: captured templates are unavailable", ErrRenderFailure)
}
}
if err := ctx.Err(); err != nil {
return nil, err
}
sessionID, err := renderSessionID(ctx, definition.SessionID, funcs, vars)
var sessionID string
var err error
if r.templates != nil {
if definition.SessionID != "" {
if compiled.Session == nil {
return nil, fmt.Errorf("%w: captured session template is unavailable", ErrRenderFailure)
}
sessionID, err = renderCompiledSessionID(ctx, compiled.Session, funcs, vars)
}
} else {
sessionID, err = renderSessionID(ctx, definition.SessionID, funcs, vars)
}
if err != nil {
return nil, err
}
@@ -76,7 +103,19 @@ func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefini
if err := ctx.Err(); err != nil {
return nil, err
}
tmpl, parseErr := parseTemplate(fmt.Sprintf("msg_%d", i), tmplMsg.Content, funcs)
var tmpl *template.Template
var parseErr error
if r.templates != nil {
if compiled.Messages[i] == nil {
return nil, fmt.Errorf("%w: captured message template is unavailable", ErrRenderFailure)
}
tmpl, parseErr = compiled.Messages[i].Clone()
if parseErr == nil {
tmpl.Funcs(funcs)
}
} else {
tmpl, parseErr = parseTemplate(fmt.Sprintf("msg_%d", i), tmplMsg.Content, funcs)
}
if err := ctx.Err(); err != nil {
return nil, err
}
@@ -174,7 +213,25 @@ func renderSessionID(ctx context.Context, raw string, funcs template.FuncMap, va
if parseErr != nil {
return "", fmt.Errorf("%w: session_id: %v", ErrInvalidTemplate, parseErr)
}
return executeSessionTemplate(ctx, tmpl, vars)
}
func renderCompiledSessionID(ctx context.Context, compiled *template.Template, funcs template.FuncMap, vars map[string]string) (string, error) {
if err := ctx.Err(); err != nil {
return "", err
}
tmpl, err := compiled.Clone()
if err := ctx.Err(); err != nil {
return "", err
}
if err != nil {
return "", fmt.Errorf("%w: session_id: %v", ErrRenderFailure, err)
}
tmpl.Funcs(funcs)
return executeSessionTemplate(ctx, tmpl, vars)
}
func executeSessionTemplate(ctx context.Context, tmpl *template.Template, vars map[string]string) (string, error) {
if err := ctx.Err(); err != nil {
return "", err
}

View File

@@ -2,6 +2,18 @@ package prompt
import "text/template"
// CompiledTemplates is the immutable parsed template set for one captured
// prompt. Callers must not mutate its templates after handing it to a renderer.
type CompiledTemplates struct {
Session *template.Template
Messages []*template.Template
}
// TemplateLookup provides parsed templates for a captured prompt identity.
type TemplateLookup interface {
GetCompiledTemplates(id, version string) (CompiledTemplates, bool)
}
// CompileTemplate checks static syntax with the same grammar and function
// names as rendering, without evaluating request inputs or variables.
func CompileTemplate(name, raw string) (*template.Template, error) {