Bugfixes and improved alignment with architecture blueprint

This commit is contained in:
2026-05-04 20:36:12 -05:00
parent c07320f9d0
commit fa070a296d
10 changed files with 158 additions and 34 deletions

View File

@@ -13,6 +13,7 @@ var (
ErrMissingRequiredInput = errors.New("missing required input artifact")
ErrUnknownInput = errors.New("referenced unknown input artifact")
ErrInvalidTemplate = errors.New("invalid prompt template")
ErrRenderFailure = errors.New("prompt render failure")
ErrInvalidMessageRole = errors.New("invalid or empty message role")
)
@@ -23,9 +24,14 @@ func NewGoRenderer() Renderer {
}
func (r *goRenderer) Render(ctx context.Context, profile *domain.PromptProfile, inputs map[string]*domain.Artifact, vars map[string]string) (*domain.RenderedPrompt, error) {
if profile == nil {
return nil, fmt.Errorf("%w: nil profile", ErrRenderFailure)
}
// 1. Verify required inputs
for _, req := range profile.ExpectedInputs {
if _, ok := inputs[req]; !ok {
art, ok := inputs[req]
if !ok || art == nil {
return nil, fmt.Errorf("%w: %s", ErrMissingRequiredInput, req)
}
}
@@ -44,19 +50,25 @@ func (r *goRenderer) Render(ctx context.Context, profile *domain.PromptProfile,
var renderedMessages []domain.RenderedMessage
for i, tmplMsg := range profile.Templates {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if tmplMsg.Role == "" {
return nil, fmt.Errorf("%w: message %d", ErrInvalidMessageRole, i)
}
// Parse and execute template
tmpl, err := template.New(fmt.Sprintf("msg_%d", i)).Funcs(funcs).Parse(tmplMsg.Content)
tmpl, err := template.New(fmt.Sprintf("msg_%d", i)).Funcs(funcs).Option("missingkey=error").Parse(tmplMsg.Content)
if err != nil {
return nil, fmt.Errorf("%w: message %d: %v", ErrInvalidTemplate, i, err)
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, vars); err != nil {
return nil, fmt.Errorf("execution failed for message %d: %v", i, err)
return nil, fmt.Errorf("%w: message %d: %w", ErrRenderFailure, i, err)
}
renderedMessages = append(renderedMessages, domain.RenderedMessage{

View File

@@ -2,6 +2,7 @@ package prompt
import (
"context"
"errors"
"testing"
"gitea.maximumdirect.net/eric/scriptorium/internal/domain"
@@ -47,7 +48,7 @@ func TestGoRenderer_Render(t *testing.T) {
t.Run("missing required input", func(t *testing.T) {
emptyInputs := map[string]*domain.Artifact{}
_, err := renderer.Render(ctx, profile, emptyInputs, vars)
if err == nil || (err != ErrMissingRequiredInput && err.Error() != "missing required input artifact: transcript") {
if !errors.Is(err, ErrMissingRequiredInput) {
t.Errorf("expected ErrMissingRequiredInput, got %v", err)
}
})
@@ -59,8 +60,11 @@ func TestGoRenderer_Render(t *testing.T) {
},
}
_, err := renderer.Render(ctx, profileUnknown, inputs, vars)
if err == nil {
t.Error("expected error for unknown input")
if !errors.Is(err, ErrRenderFailure) {
t.Errorf("expected ErrRenderFailure, got %v", err)
}
if !errors.Is(err, ErrUnknownInput) {
t.Errorf("expected ErrUnknownInput, got %v", err)
}
})
@@ -71,8 +75,8 @@ func TestGoRenderer_Render(t *testing.T) {
},
}
_, err := renderer.Render(ctx, profileInvalid, inputs, vars)
if err == nil {
t.Error("expected error for invalid template syntax")
if !errors.Is(err, ErrInvalidTemplate) {
t.Errorf("expected ErrInvalidTemplate, got %v", err)
}
})
@@ -83,8 +87,20 @@ func TestGoRenderer_Render(t *testing.T) {
},
}
_, err := renderer.Render(ctx, profileNoRole, inputs, vars)
if err == nil {
t.Error("expected error for empty message role")
if !errors.Is(err, ErrInvalidMessageRole) {
t.Errorf("expected ErrInvalidMessageRole, got %v", err)
}
})
t.Run("missing variable in template", func(t *testing.T) {
profileMissingVar := &domain.PromptProfile{
Templates: []domain.PromptMessageTemplate{
{Role: "system", Content: "You are {{.missing}}"},
},
}
_, err := renderer.Render(ctx, profileMissingVar, inputs, vars)
if !errors.Is(err, ErrRenderFailure) {
t.Errorf("expected ErrRenderFailure for missing variable, got %v", err)
}
})
}