|
|
|
|
@@ -14,7 +14,6 @@ import (
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
renderer := NewGoRenderer()
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
inputs := map[string]*domain.Artifact{
|
|
|
|
|
@@ -33,7 +32,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -53,7 +52,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -71,7 +70,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -99,7 +98,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -129,7 +128,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -151,7 +150,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -169,7 +168,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
"tone": "concise",
|
|
|
|
|
"session_id": "agent-session-123",
|
|
|
|
|
})
|
|
|
|
|
@@ -190,7 +189,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -208,7 +207,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if !errors.Is(err, ErrRenderFailure) {
|
|
|
|
|
t.Fatalf("expected ErrRenderFailure, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -223,7 +222,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
"tone": "concise",
|
|
|
|
|
"session_id": strings.Repeat("x", domain.SessionIDMaxLength+1),
|
|
|
|
|
})
|
|
|
|
|
@@ -241,7 +240,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, map[string]string{
|
|
|
|
|
"tone": "concise",
|
|
|
|
|
"session_id": "session" + string([]byte{0xff}),
|
|
|
|
|
})
|
|
|
|
|
@@ -258,7 +257,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -278,7 +277,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -298,7 +297,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if !errors.Is(err, ErrRenderFailure) {
|
|
|
|
|
t.Fatalf("expected ErrRenderFailure, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -315,7 +314,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, map[string]*domain.Artifact{}, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, map[string]*domain.Artifact{}, vars)
|
|
|
|
|
if !errors.Is(err, ErrMissingRequiredInput) {
|
|
|
|
|
t.Fatalf("expected ErrMissingRequiredInput, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -329,9 +328,9 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
if !errors.Is(err, ErrInvalidTemplate) {
|
|
|
|
|
t.Fatalf("expected ErrInvalidTemplate, got %v", err)
|
|
|
|
|
_, err := CompileTemplate("message", def.Templates[0].Content)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected template compilation to fail")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@@ -343,7 +342,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if !errors.Is(err, ErrRenderFailure) {
|
|
|
|
|
t.Fatalf("expected ErrRenderFailure, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -359,7 +358,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
{Role: "", Content: "Hello"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_, err := renderer.Render(ctx, def, inputs, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, inputs, vars)
|
|
|
|
|
if !errors.Is(err, ErrInvalidMessageRole) {
|
|
|
|
|
t.Fatalf("expected ErrInvalidMessageRole, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -371,7 +370,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
{Role: " \u2003DeVeLoPeR\u2003 ", Content: "Hello"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
res, err := renderer.Render(ctx, def, nil, vars)
|
|
|
|
|
res, err := capturedTestRenderer(t, def).Render(ctx, def, nil, vars)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -385,7 +384,7 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
def := &domain.PromptDefinition{
|
|
|
|
|
Templates: []domain.PromptMessageTemplate{{Role: unsupportedRole, Content: "Hello"}},
|
|
|
|
|
}
|
|
|
|
|
_, err := renderer.Render(ctx, def, nil, vars)
|
|
|
|
|
_, err := capturedTestRenderer(t, def).Render(ctx, def, nil, vars)
|
|
|
|
|
if !errors.Is(err, ErrInvalidMessageRole) {
|
|
|
|
|
t.Fatalf("expected ErrInvalidMessageRole, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -396,9 +395,9 @@ func TestGoRenderer_Render(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGoRendererCancellation(t *testing.T) {
|
|
|
|
|
t.Run("before session parsing", func(t *testing.T) {
|
|
|
|
|
t.Run("before rendering", func(t *testing.T) {
|
|
|
|
|
definition := &domain.PromptDefinition{
|
|
|
|
|
SessionID: "{{ malformed",
|
|
|
|
|
SessionID: "{{.session}}",
|
|
|
|
|
Templates: []domain.PromptMessageTemplate{
|
|
|
|
|
{Role: "user", Content: "not rendered"},
|
|
|
|
|
},
|
|
|
|
|
@@ -406,18 +405,11 @@ func TestGoRendererCancellation(t *testing.T) {
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
cancel()
|
|
|
|
|
|
|
|
|
|
result, err := NewGoRenderer().Render(ctx, definition, nil, nil)
|
|
|
|
|
renderer := capturedTestRenderer(t, definition)
|
|
|
|
|
result, err := renderer.Render(ctx, definition, nil, nil)
|
|
|
|
|
if result != nil || !errors.Is(err, context.Canceled) {
|
|
|
|
|
t.Fatalf("result=%#v err=%v, want nil/context.Canceled", result, err)
|
|
|
|
|
}
|
|
|
|
|
if errors.Is(err, ErrInvalidTemplate) {
|
|
|
|
|
t.Fatalf("pre-canceled render parsed the malformed session: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result, err = NewGoRenderer().Render(context.Background(), definition, nil, nil)
|
|
|
|
|
if result != nil || !errors.Is(err, ErrInvalidTemplate) {
|
|
|
|
|
t.Fatalf("active render result=%#v err=%v, want nil/ErrInvalidTemplate", result, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("during artifact text conversion", func(t *testing.T) {
|
|
|
|
|
@@ -447,14 +439,15 @@ func TestGoRendererCancellation(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
counter := &checkCountingContext{Context: context.Background()}
|
|
|
|
|
if _, err := NewGoRenderer().Render(counter, definition, nil, nil); err != nil {
|
|
|
|
|
renderer := capturedTestRenderer(t, definition)
|
|
|
|
|
if _, err := renderer.Render(counter, definition, nil, nil); err != nil {
|
|
|
|
|
t.Fatalf("count render checkpoints: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The final three checks occur after template execution, after the
|
|
|
|
|
// message is assembled, and immediately before publication.
|
|
|
|
|
ctx := newCancelOnCheckContext(counter.checks - 2)
|
|
|
|
|
result, err := NewGoRenderer().Render(ctx, definition, nil, nil)
|
|
|
|
|
result, err := renderer.Render(ctx, definition, nil, nil)
|
|
|
|
|
if result != nil || !errors.Is(err, context.Canceled) {
|
|
|
|
|
t.Fatalf("result=%#v err=%v, want nil/context.Canceled", result, err)
|
|
|
|
|
}
|
|
|
|
|
@@ -472,7 +465,8 @@ func TestGoRendererArtifactTextLifecycle(t *testing.T) {
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
first, err := NewGoRenderer().Render(context.Background(), definition, inputs, nil)
|
|
|
|
|
renderer := capturedTestRenderer(t, definition)
|
|
|
|
|
first, err := renderer.Render(context.Background(), definition, inputs, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("first render: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -488,7 +482,7 @@ func TestGoRendererArtifactTextLifecycle(t *testing.T) {
|
|
|
|
|
if bytes.Equal([]byte(first.Messages[0].Content), append(append(append([]byte(nil), body...), '|'), body...)) {
|
|
|
|
|
t.Fatal("completed render aliases the artifact body")
|
|
|
|
|
}
|
|
|
|
|
second, err := NewGoRenderer().Render(context.Background(), definition, inputs, nil)
|
|
|
|
|
second, err := renderer.Render(context.Background(), definition, inputs, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("second render: %v", err)
|
|
|
|
|
}
|
|
|
|
|
@@ -498,7 +492,7 @@ func TestGoRendererArtifactTextLifecycle(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nilInputs := map[string]*domain.Artifact{"document": nil}
|
|
|
|
|
result, err := NewGoRenderer().Render(context.Background(), definition, nilInputs, nil)
|
|
|
|
|
result, err := renderer.Render(context.Background(), definition, nilInputs, nil)
|
|
|
|
|
if result != nil || !errors.Is(err, ErrUnknownInput) || !errors.Is(err, ErrRenderFailure) {
|
|
|
|
|
t.Fatalf("nil input result=%#v err=%v, want ErrUnknownInput and ErrRenderFailure", result, err)
|
|
|
|
|
}
|
|
|
|
|
@@ -559,11 +553,56 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCompiledRendererRequiresCapturedTemplates(t *testing.T) {
|
|
|
|
|
message, err := CompileTemplate("message", "hello")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
name string
|
|
|
|
|
lookup TemplateLookup
|
|
|
|
|
sessionID string
|
|
|
|
|
}{
|
|
|
|
|
{name: "missing lookup"},
|
|
|
|
|
{name: "missing prompt", lookup: compiledRendererTestLookup{id: "other"}},
|
|
|
|
|
{name: "missing message", lookup: compiledRendererTestLookup{id: "prompt"}},
|
|
|
|
|
{name: "nil message", lookup: compiledRendererTestLookup{id: "prompt", templates: CompiledTemplates{Messages: []*template.Template{nil}}}},
|
|
|
|
|
{name: "missing session", lookup: compiledRendererTestLookup{id: "prompt", templates: CompiledTemplates{Messages: []*template.Template{message}}}, sessionID: "session"},
|
|
|
|
|
} {
|
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
|
definition := &domain.PromptDefinition{ID: "prompt", SessionID: tc.sessionID, Templates: []domain.PromptMessageTemplate{{Role: "user", Content: "hello"}}}
|
|
|
|
|
if _, err := NewCompiledRenderer(tc.lookup).Render(context.Background(), definition, nil, nil); !errors.Is(err, ErrRenderFailure) {
|
|
|
|
|
t.Fatalf("render error = %v, want render failure", err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type compiledRendererTestLookup struct {
|
|
|
|
|
id string
|
|
|
|
|
templates CompiledTemplates
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func capturedTestRenderer(t testing.TB, definition *domain.PromptDefinition) Renderer {
|
|
|
|
|
t.Helper()
|
|
|
|
|
var compiled CompiledTemplates
|
|
|
|
|
var err error
|
|
|
|
|
if definition.SessionID != "" {
|
|
|
|
|
compiled.Session, err = CompileTemplate("session", definition.SessionID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, message := range definition.Templates {
|
|
|
|
|
parsed, err := CompileTemplate("message", message.Content)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
compiled.Messages = append(compiled.Messages, parsed)
|
|
|
|
|
}
|
|
|
|
|
return NewCompiledRenderer(compiledRendererTestLookup{id: definition.ID, templates: compiled})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (lookup compiledRendererTestLookup) GetCompiledTemplates(id string) (CompiledTemplates, bool) {
|
|
|
|
|
if id != lookup.id {
|
|
|
|
|
return CompiledTemplates{}, false
|
|
|
|
|
@@ -571,8 +610,7 @@ func (lookup compiledRendererTestLookup) GetCompiledTemplates(id string) (Compil
|
|
|
|
|
return lookup.templates, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BenchmarkGoRendererArtifactReferences measures NewGoRenderer's per-call
|
|
|
|
|
// template-parsing fallback rather than the captured compiled path.
|
|
|
|
|
// BenchmarkGoRendererArtifactReferences measures rendering captured templates.
|
|
|
|
|
func BenchmarkGoRendererArtifactReferences(b *testing.B) {
|
|
|
|
|
body := bytes.Repeat([]byte("document content "), (artifactTextChunkSize*4)/len("document content "))
|
|
|
|
|
inputs := map[string]*domain.Artifact{
|
|
|
|
|
@@ -604,7 +642,7 @@ func BenchmarkGoRendererArtifactReferences(b *testing.B) {
|
|
|
|
|
|
|
|
|
|
for _, tc := range tests {
|
|
|
|
|
b.Run(tc.name, func(b *testing.B) {
|
|
|
|
|
renderer := NewGoRenderer()
|
|
|
|
|
renderer := capturedTestRenderer(b, tc.definition)
|
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
b.SetBytes(int64(len(body)))
|
|
|
|
|
for range b.N {
|
|
|
|
|
|