Refactor: split prompt definition from execution settings and migrate run contracts to prompt_* + execution_target

This commit is contained in:
2026-05-05 10:09:31 -05:00
parent fdfd8641f5
commit a633c67538
28 changed files with 712 additions and 1021 deletions

View File

@@ -23,16 +23,19 @@ func NewGoRenderer() Renderer {
return &goRenderer{}
}
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)
func (r *goRenderer) Render(ctx context.Context, definition *domain.PromptDefinition, inputs map[string]*domain.Artifact, vars map[string]string) (*domain.RenderedPrompt, error) {
if definition == nil {
return nil, fmt.Errorf("%w: nil prompt definition", ErrRenderFailure)
}
// 1. Verify required inputs
for _, req := range profile.ExpectedInputs {
art, ok := inputs[req]
for _, in := range definition.Inputs {
if !in.Required {
continue
}
art, ok := inputs[in.Name]
if !ok || art == nil {
return nil, fmt.Errorf("%w: %s", ErrMissingRequiredInput, req)
return nil, fmt.Errorf("%w: %s", ErrMissingRequiredInput, in.Name)
}
}
@@ -49,7 +52,7 @@ func (r *goRenderer) Render(ctx context.Context, profile *domain.PromptProfile,
var renderedMessages []domain.RenderedMessage
for i, tmplMsg := range profile.Templates {
for i, tmplMsg := range definition.Templates {
select {
case <-ctx.Done():
return nil, ctx.Err()
@@ -61,6 +64,9 @@ func (r *goRenderer) Render(ctx context.Context, profile *domain.PromptProfile,
}
// Parse and execute template
if tmplMsg.ContentFile != "" {
return nil, fmt.Errorf("%w: message %d: content_file is not implemented yet", ErrRenderFailure, i)
}
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)

View File

@@ -7,5 +7,5 @@ import (
// Renderer renders prompt templates using named artifacts and variables.
type Renderer interface {
Render(ctx context.Context, profile *domain.PromptProfile, inputs map[string]*domain.Artifact, vars map[string]string) (*domain.RenderedPrompt, error)
Render(ctx context.Context, definition *domain.PromptDefinition, inputs map[string]*domain.Artifact, vars map[string]string) (*domain.RenderedPrompt, error)
}

View File

@@ -12,9 +12,9 @@ func TestGoRenderer_Render(t *testing.T) {
renderer := NewGoRenderer()
ctx := context.Background()
profile := &domain.PromptProfile{
profile := &domain.PromptDefinition{
ID: "test-profile",
ExpectedInputs: []string{"transcript"},
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{
{Role: "system", Content: "You are a {{.role}}."},
{Role: "user", Content: "Analyze this: {{input \"transcript\"}}"},
@@ -54,7 +54,8 @@ func TestGoRenderer_Render(t *testing.T) {
})
t.Run("unknown input in template", func(t *testing.T) {
profileUnknown := &domain.PromptProfile{
profileUnknown := &domain.PromptDefinition{
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{
{Role: "user", Content: "Hello {{input \"ghost\"}}"},
},
@@ -69,7 +70,8 @@ func TestGoRenderer_Render(t *testing.T) {
})
t.Run("invalid template syntax", func(t *testing.T) {
profileInvalid := &domain.PromptProfile{
profileInvalid := &domain.PromptDefinition{
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{
{Role: "user", Content: "Hello {{.unclosed"},
},
@@ -81,7 +83,8 @@ func TestGoRenderer_Render(t *testing.T) {
})
t.Run("empty message role", func(t *testing.T) {
profileNoRole := &domain.PromptProfile{
profileNoRole := &domain.PromptDefinition{
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{
{Role: "", Content: "Hello"},
},
@@ -93,7 +96,8 @@ func TestGoRenderer_Render(t *testing.T) {
})
t.Run("missing variable in template", func(t *testing.T) {
profileMissingVar := &domain.PromptProfile{
profileMissingVar := &domain.PromptDefinition{
Inputs: []domain.PromptInput{{Name: "transcript", Required: true}},
Templates: []domain.PromptMessageTemplate{
{Role: "system", Content: "You are {{.missing}}"},
},