fixes
This commit is contained in:
@@ -22,7 +22,7 @@ func TestResolveSteps(t *testing.T) {
|
||||
"full": {StepNormalize, StepParseSpecs, StepFillFields, StepEPREL, StepAIEnhance},
|
||||
"enhance_only": {StepNormalize, StepAIEnhance},
|
||||
"attributes_only": {StepNormalize, StepParseSpecs, StepFillFields},
|
||||
"eprel_only": {StepNormalize, StepEPREL},
|
||||
"eprel_only": {StepNormalize, StepParseSpecs, StepEPREL},
|
||||
"normalize_only": {StepNormalize},
|
||||
}
|
||||
for in, want := range cases {
|
||||
@@ -187,6 +187,18 @@ func TestPreferredProductTitle_skipsPromptLabels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreferredProductTitle_skipsFormulaLeakage(t *testing.T) {
|
||||
leak := "short retail title; follow any Title formula constraints that follow; use Attrs"
|
||||
got := preferredProductTitle("1", leak, "follow any Title formula constraints that follow; use Attrs", "Vox SWA-8000W")
|
||||
if got != "Vox SWA-8000W" {
|
||||
t.Fatalf("got %q want feed title", got)
|
||||
}
|
||||
got = preferredProductTitle("", leak, "Schema: {\"name\":\"string\"}", "Reply with ONLY JSON")
|
||||
if got != "Product" {
|
||||
t.Fatalf("all-leakage fallback got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPromptLabelTitle(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"Category:": true,
|
||||
@@ -204,6 +216,100 @@ func TestIsPromptLabelTitle(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPromptLeakageTitle(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"follow any Title formula constraints that follow; use Attrs": true,
|
||||
"short retail title; follow any Title formula constraints…": true,
|
||||
"write name in {{language}}": true,
|
||||
"Schema: {\"name\":\"string\",\"description\":\"string\"}": true,
|
||||
"Reply with ONLY JSON (no markdown)": true,
|
||||
"Vox SWA-8000W dishwasher": false,
|
||||
"Acme Widget Pro": false,
|
||||
"": false,
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := isPromptLeakageTitle(in); got != want {
|
||||
t.Fatalf("%q: leakage got %v want %v", in, got, want)
|
||||
}
|
||||
if want && !isPromptLabelTitle(in) {
|
||||
t.Fatalf("%q: isPromptLabelTitle should include leakage", in)
|
||||
}
|
||||
}
|
||||
// Long dump with formula keyword (>120 runes).
|
||||
long := strings.Repeat("x", 100) + " Title formula scaffolding " + strings.Repeat("y", 30)
|
||||
if !isPromptLeakageTitle(long) {
|
||||
t.Fatalf("long formula dump should leak: len=%d", len([]rune(long)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnhance_rejectsFormulaLeakageTitle(t *testing.T) {
|
||||
leak := "short retail title; follow any Title formula constraints that follow; use Attrs"
|
||||
e := &Engine{
|
||||
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {
|
||||
return Completion{Text: `{"name":"` + leak + `","description":"A solid washer."}`, TotalTokens: 2}, nil
|
||||
}},
|
||||
Vector: NoopVectorCategorizer{},
|
||||
}
|
||||
out, err := e.RunSteps(context.Background(), "co", ProductInput{
|
||||
GTIN: "8712285326882",
|
||||
Name: "Vox SWA-8000W",
|
||||
Mapped: map[string]any{"name": "Vox SWA-8000W", "description": "Washer", "category": "demo-electronics"},
|
||||
}, "enhance_only", nil, StepPolicy{AllowAI: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if out.ProcessedName != "Vox SWA-8000W" {
|
||||
t.Fatalf("ProcessedName=%q want Vox SWA-8000W (reject formula leakage)", out.ProcessedName)
|
||||
}
|
||||
if isPromptLeakageTitle(out.Name) || isPromptLabelTitle(out.Name) {
|
||||
t.Fatalf("Name polluted: %q", out.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeuristicCompleter_keepsNameWhenFormulaConstraintsPresent(t *testing.T) {
|
||||
h := HeuristicCompleter{}
|
||||
// Shape matches aiprompts.CategoryEnhanceUserTemplate: instruction "- name:"
|
||||
// appears BEFORE the real "Name:" field — labeledPromptValue must skip leakage.
|
||||
user := `Your reply is parsed as JSON {"name":"string","description":"string"} only (system schema). Write name and description in English (do not hardcode a language).
|
||||
- name: short retail title; follow any Title formula constraints that follow; use Attrs
|
||||
- description: prefer 1-3 factual paragraphs as ONE string
|
||||
|
||||
Category: demo-electronics
|
||||
Name: Vox SWA-8000W
|
||||
Desc: Washer
|
||||
Attrs: {"brand":"Vox"}
|
||||
|
||||
Title formula (order matters; join with " "). Build name from Attrs using this structure:
|
||||
1. attr [brand]
|
||||
Prefer Attrs values for [attr] slots; write name in English.`
|
||||
comp, err := h.Complete(context.Background(), `Return JSON with "name" and "description".`, user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
obj, err := ParseJSONObject(comp.Text)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
name := SanitizeOutput(fmt.Sprint(obj["name"]))
|
||||
if isPromptLeakageTitle(name) || isPromptLabelTitle(name) {
|
||||
t.Fatalf("heuristic leaked formula into name: %q", name)
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(name), "vox") {
|
||||
t.Fatalf("name=%q want Vox from Name: line", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLabeledPromptValue_skipsInstructionNameBullet(t *testing.T) {
|
||||
user := `- name: short retail title; follow any Title formula constraints that follow; use Attrs
|
||||
Name: Vox SWA-8000W
|
||||
Desc: Washer
|
||||
Attrs: {}`
|
||||
got := labeledPromptValue(user, "name:", "current name:")
|
||||
if got != "Vox SWA-8000W" {
|
||||
t.Fatalf("got %q want Vox SWA-8000W", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSteps_full_mappedCategoryWinsOverPrior(t *testing.T) {
|
||||
e := &Engine{
|
||||
Completer: stubCompleter{fn: func(system, user string) (Completion, error) {
|
||||
|
||||
Reference in New Issue
Block a user