Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHashEnhanceInput_stableAndSensitive(t *testing.T) {
|
||||
attrs := map[string]any{"brand": "Acme", "color": "Red"}
|
||||
a := HashEnhanceInput("Shoes", "Runner", "A shoe", "", "", "", "", attrs)
|
||||
b := HashEnhanceInput("Shoes", "Runner", "A shoe", "", "", "", "", attrs)
|
||||
if a == "" || a != b {
|
||||
t.Fatalf("expected stable hash, got %q vs %q", a, b)
|
||||
}
|
||||
if HashEnhanceInput("Shoes", "Runner X", "A shoe", "", "", "", "", attrs) == a {
|
||||
t.Fatal("name change must change hash")
|
||||
}
|
||||
if HashEnhanceInput("Shoes", "Runner", "A shoe", "Brand:\n- tone: bold", "", "", "", attrs) == a {
|
||||
t.Fatal("brand prompt must change hash")
|
||||
}
|
||||
if HashEnhanceInput("Shoes", "Runner", "A shoe", "", "", "sys-a", "user-a", attrs) == a {
|
||||
t.Fatal("prompt template change must change hash")
|
||||
}
|
||||
if HashEnhanceInput("Shoes", "Runner", "A shoe", "", "fr", "", "", attrs) == a {
|
||||
t.Fatal("language change must change hash")
|
||||
}
|
||||
// Attr key order must not matter (CompactAttrs + json map sort).
|
||||
attrs2 := map[string]any{"color": "Red", "brand": "Acme"}
|
||||
if HashEnhanceInput("Shoes", "Runner", "A shoe", "", "", "", "", attrs2) != a {
|
||||
t.Fatal("attr key order must not change hash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSteps_skipsEnhanceWhenInputHashUnchanged(t *testing.T) {
|
||||
calls := 0
|
||||
e := &Engine{
|
||||
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {
|
||||
calls++
|
||||
return Completion{Text: `{"name":"ShouldNotRun","description":"Nope"}`, TotalTokens: 9}, nil
|
||||
}},
|
||||
Vector: NoopVectorCategorizer{},
|
||||
}
|
||||
in := ProductInput{
|
||||
Name: "Widget",
|
||||
Description: "A widget",
|
||||
Mapped: map[string]any{"name": "Widget", "description": "A widget"},
|
||||
PriorProcessedName: "Cached Widget",
|
||||
PriorProcessedDescription: "Cached description.",
|
||||
}
|
||||
// First pass without prior hash to compute the hash shape via enhance path is awkward;
|
||||
// compute the same hash RunSteps will see after normalize (name/desc from mapped).
|
||||
// enhance_only: normalize then enhance with out.Name from normalized.
|
||||
normName := "Widget"
|
||||
normDesc := "A widget"
|
||||
sysTpl, userTpl := resolveProductPromptTemplates(ProductInput{})
|
||||
in.PriorEnhanceHash = HashEnhanceInput("", normName, normDesc, "", "", sysTpl, userTpl, map[string]any{})
|
||||
|
||||
out, err := e.RunSteps(context.Background(), "co", in, "enhance_only", nil, StepPolicy{AllowAI: true, AllowEPREL: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 0 {
|
||||
t.Fatalf("expected LLM skip, calls=%d", calls)
|
||||
}
|
||||
if out.ProcessedName != "Cached Widget" {
|
||||
t.Fatalf("name=%q", out.ProcessedName)
|
||||
}
|
||||
if out.ProcessedDescription != "Cached description." {
|
||||
t.Fatalf("desc=%q", out.ProcessedDescription)
|
||||
}
|
||||
if out.TotalTokens != 0 {
|
||||
t.Fatalf("tokens=%d want 0", out.TotalTokens)
|
||||
}
|
||||
if out.FieldSources[FieldEnhanceInputHash] != in.PriorEnhanceHash {
|
||||
t.Fatalf("hash field=%v", out.FieldSources[FieldEnhanceInputHash])
|
||||
}
|
||||
if src, _ := out.FieldSources["name"].(string); src != "ai_enhance_unchanged" {
|
||||
t.Fatalf("name source=%v", out.FieldSources["name"])
|
||||
}
|
||||
if !out.SkipCreditDebit {
|
||||
t.Fatal("expected SkipCreditDebit when enhance hash unchanged")
|
||||
}
|
||||
if shouldDebitProductProcessing(false, out) {
|
||||
t.Fatal("processOne must not debit when enhance unchanged")
|
||||
}
|
||||
joined := strings.Join(out.Notes, ";")
|
||||
if !strings.Contains(joined, "unchanged") {
|
||||
t.Fatalf("notes=%v", out.Notes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSteps_callsEnhanceWhenInputHashDiffers(t *testing.T) {
|
||||
calls := 0
|
||||
e := &Engine{
|
||||
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {
|
||||
calls++
|
||||
return Completion{Text: `{"name":"Fresh","description":"New copy."}`, TotalTokens: 3}, nil
|
||||
}},
|
||||
Vector: NoopVectorCategorizer{},
|
||||
}
|
||||
out, err := e.RunSteps(context.Background(), "co", ProductInput{
|
||||
Mapped: map[string]any{"name": "Widget", "description": "A widget"},
|
||||
PriorEnhanceHash: "deadbeef",
|
||||
PriorProcessedName: "Old",
|
||||
PriorProcessedDescription: "Old desc",
|
||||
}, "enhance_only", nil, StepPolicy{AllowAI: true, AllowEPREL: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 1 {
|
||||
t.Fatalf("calls=%d", calls)
|
||||
}
|
||||
if out.ProcessedName != "Fresh" {
|
||||
t.Fatalf("name=%q", out.ProcessedName)
|
||||
}
|
||||
if out.TotalTokens != 3 {
|
||||
t.Fatalf("tokens=%d", out.TotalTokens)
|
||||
}
|
||||
if out.SkipCreditDebit {
|
||||
t.Fatal("hash miss must still debit")
|
||||
}
|
||||
if !shouldDebitProductProcessing(false, out) {
|
||||
t.Fatal("expected debit when enhance ran")
|
||||
}
|
||||
h, _ := out.FieldSources[FieldEnhanceInputHash].(string)
|
||||
if h == "" || h == "deadbeef" {
|
||||
t.Fatalf("expected new hash in field_sources, got %q", h)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user