fix
This commit is contained in:
@@ -326,19 +326,24 @@ func (e *Engine) RunSteps(ctx context.Context, companyID string, in ProductInput
|
||||
// Prefer title-aware selection + synthesize before deciding hash persistence.
|
||||
name = preferredProductTitle(in.GTIN, name, out.Name, priorName, in.Name)
|
||||
desc = preferredProductDescription(name, desc, out.Description, priorDesc)
|
||||
outerSynth := false
|
||||
if isWeakPriorEnhanceDescription(desc, name) {
|
||||
if synth := synthesizeDescriptionFromTitle(name, displayCat, lang, enhanceAttrs); synth != "" {
|
||||
if synth := synthesizeProductDescription(name, displayCat, lang, enhanceAttrs, descTpl); synth != "" {
|
||||
desc = synth
|
||||
outerSynth = true
|
||||
}
|
||||
}
|
||||
weakDesc := isWeakPriorEnhanceDescription(desc, name)
|
||||
// Only persist enhance_input_hash for quality ok / hash-skip unchanged.
|
||||
// Never copy input_hash from error/passthrough/thin meta into localized.
|
||||
// Never copy input_hash from error/passthrough/thin/synthesized meta.
|
||||
persistHash := ""
|
||||
rawHash := enhanceHashFromMeta(raw)
|
||||
switch {
|
||||
case err != nil:
|
||||
persistHash = ""
|
||||
case status == "synthesized" || outerSynth:
|
||||
persistHash = ""
|
||||
allUnchanged = false
|
||||
case status == "unchanged" && !weakDesc:
|
||||
persistHash = rawHash
|
||||
case status == "ok" && !weakDesc:
|
||||
@@ -402,7 +407,8 @@ func (e *Engine) RunSteps(ctx context.Context, companyID string, in ProductInput
|
||||
// Timeout/error/empty: always synthesize a factual fallback when a title exists.
|
||||
if out.ProcessedName != "" && (out.ProcessedDescription == "" ||
|
||||
isWeakPriorEnhanceDescription(out.ProcessedDescription, out.ProcessedName, out.Name)) {
|
||||
if synth := synthesizeDescriptionFromTitle(out.ProcessedName, displayCat, primary, enhanceAttrs); synth != "" {
|
||||
_, failDescTpl := categoryFormulasFor(in, out.Category)
|
||||
if synth := synthesizeProductDescription(out.ProcessedName, displayCat, primary, enhanceAttrs, failDescTpl); synth != "" {
|
||||
out.ProcessedDescription = synth
|
||||
if out.Description == "" || isWeakPriorEnhanceDescription(out.Description, out.Name, out.ProcessedName) {
|
||||
out.Description = synth
|
||||
@@ -477,15 +483,16 @@ func (e *Engine) RunSteps(ctx context.Context, companyID string, in ProductInput
|
||||
if out.ProcessedName == "" {
|
||||
out.ProcessedName = out.Name
|
||||
}
|
||||
_, finalDescTpl := categoryFormulasFor(in, out.Category)
|
||||
if out.ProcessedDescription == "" || isWeakPriorEnhanceDescription(out.ProcessedDescription, out.ProcessedName, out.Name) {
|
||||
if synth := synthesizeDescriptionFromTitle(out.ProcessedName, displayCat, in.Language, out.Attributes); synth != "" {
|
||||
if synth := synthesizeProductDescription(out.ProcessedName, displayCat, in.Language, out.Attributes, finalDescTpl); synth != "" {
|
||||
out.ProcessedDescription = synth
|
||||
}
|
||||
}
|
||||
if out.Description == "" || isWeakPriorEnhanceDescription(out.Description, out.Name, out.ProcessedName) {
|
||||
if out.ProcessedDescription != "" && !isWeakPriorEnhanceDescription(out.ProcessedDescription, out.Name, out.ProcessedName) {
|
||||
out.Description = out.ProcessedDescription
|
||||
} else if synth := synthesizeDescriptionFromTitle(out.Name, displayCat, in.Language, out.Attributes); synth != "" {
|
||||
} else if synth := synthesizeProductDescription(out.Name, displayCat, in.Language, out.Attributes, finalDescTpl); synth != "" {
|
||||
out.Description = synth
|
||||
if out.ProcessedDescription == "" || isWeakPriorEnhanceDescription(out.ProcessedDescription, out.ProcessedName, out.Name) {
|
||||
out.ProcessedDescription = synth
|
||||
@@ -657,7 +664,7 @@ func (e *Engine) enhance(ctx context.Context, in ProductInput, category string,
|
||||
name := preferredProductTitle(in.GTIN, in.Name, in.PriorProcessedName)
|
||||
desc := preferredProductDescription(name, in.Description, in.PriorProcessedDescription)
|
||||
if isWeakPriorEnhanceDescription(desc, name) {
|
||||
if synth := synthesizeDescriptionFromTitle(name, category, in.Language, attrs); synth != "" {
|
||||
if synth := synthesizeProductDescription(name, category, in.Language, attrs, in.DescriptionTemplate); synth != "" {
|
||||
desc = synth
|
||||
}
|
||||
}
|
||||
@@ -676,18 +683,22 @@ func (e *Engine) enhance(ctx context.Context, in ProductInput, category string,
|
||||
}
|
||||
name := preferredProductTitle(in.GTIN, SanitizeOutput(fmt.Sprint(obj["name"])), in.Name, in.PriorProcessedName)
|
||||
desc := preferredProductDescription(name, SanitizeOutput(fmt.Sprint(obj["description"])), in.Description, in.PriorProcessedDescription)
|
||||
synthesized := false
|
||||
if isWeakPriorEnhanceDescription(desc, name) {
|
||||
if synth := synthesizeDescriptionFromTitle(name, category, in.Language, attrs); synth != "" {
|
||||
if synth := synthesizeProductDescription(name, category, in.Language, attrs, in.DescriptionTemplate); synth != "" {
|
||||
desc = synth
|
||||
synthesized = true
|
||||
}
|
||||
}
|
||||
meta := map[string]any{
|
||||
"status": "ok",
|
||||
"raw": comp.Raw,
|
||||
}
|
||||
// Only attach input_hash when output description is quality-worthy; thin
|
||||
// title-echo / heuristic filler must not poison field_sources / localized skip hashes.
|
||||
if !isWeakPriorEnhanceDescription(desc, name) {
|
||||
// Heuristic synthesize must not poison enhance_input_hash (would hash-skip
|
||||
// formula-aware LLM copy on reprocess). Only persist hash for real LLM quality.
|
||||
if synthesized {
|
||||
meta["status"] = "synthesized"
|
||||
} else if !isWeakPriorEnhanceDescription(desc, name) {
|
||||
meta["input_hash"] = hash
|
||||
}
|
||||
return name, desc, comp.TotalTokens, meta, nil
|
||||
@@ -948,6 +959,73 @@ func formatAttrDimParts(attrs map[string]any, maxParts int) []string {
|
||||
return parts
|
||||
}
|
||||
|
||||
// synthesizeProductDescription prefers a category description_template skeleton
|
||||
// (HTML section types) when present; otherwise falls back to plain title synthesize.
|
||||
func synthesizeProductDescription(title, category, language string, attrs map[string]any, descriptionTemplate any) string {
|
||||
if sections, ok := parseDescriptionFormulaSections(descriptionTemplate); ok && len(sections) > 0 {
|
||||
if out := synthesizeDescriptionFromFormula(title, category, language, attrs, sections); out != "" {
|
||||
return out
|
||||
}
|
||||
}
|
||||
return synthesizeDescriptionFromTitle(title, category, language, attrs)
|
||||
}
|
||||
|
||||
// synthesizeDescriptionFromFormula builds a minimal HTML description matching
|
||||
// category description_template section types so timeout/fallback still respects
|
||||
// A1 category structure (unlike plain title synthesize).
|
||||
func synthesizeDescriptionFromFormula(title, category, language string, attrs map[string]any, sections []descriptionFormulaSection) string {
|
||||
title = strings.TrimSpace(title)
|
||||
if title == "" || title == "<nil>" || isPromptLabelTitle(title) {
|
||||
return ""
|
||||
}
|
||||
base := synthesizeDescriptionFromTitle(title, category, language, attrs)
|
||||
if base == "" {
|
||||
return ""
|
||||
}
|
||||
dims := formatAttrDimParts(attrs, 6)
|
||||
var b strings.Builder
|
||||
paraUsed := false
|
||||
listUsed := false
|
||||
for _, s := range sections {
|
||||
typ := strings.ToLower(strings.TrimSpace(s.Type))
|
||||
switch typ {
|
||||
case "h1", "h2", "h3", "h4":
|
||||
heading := title
|
||||
if typ != "h1" {
|
||||
if isSlovenianContentLanguage(language) {
|
||||
heading = "Ključne lastnosti"
|
||||
} else {
|
||||
heading = "Key features"
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&b, "<%s>%s</%s>", typ, SanitizeOutput(heading), typ)
|
||||
case "ul":
|
||||
b.WriteString("<ul>")
|
||||
items := dims
|
||||
if len(items) == 0 {
|
||||
items = []string{base}
|
||||
}
|
||||
for _, it := range items {
|
||||
fmt.Fprintf(&b, "<li>%s</li>", SanitizeOutput(it))
|
||||
}
|
||||
b.WriteString("</ul>")
|
||||
listUsed = true
|
||||
default: // p and unknown → paragraph
|
||||
body := base
|
||||
if paraUsed && len(dims) > 0 && !listUsed {
|
||||
if isSlovenianContentLanguage(language) {
|
||||
body = "Ključne specifikacije: " + strings.Join(dims, ", ") + "."
|
||||
} else {
|
||||
body = "Key specs: " + strings.Join(dims, ", ") + "."
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&b, "<p>%s</p>", SanitizeOutput(body))
|
||||
paraUsed = true
|
||||
}
|
||||
}
|
||||
return SanitizeOutput(b.String())
|
||||
}
|
||||
|
||||
// synthesizeDescriptionFromTitle builds a short factual fallback when enhance
|
||||
// returns empty/title-echo/filler copy. Uses title, category, brand, model, and
|
||||
// key dims. language is a content-language code (en/sl/…) or English label.
|
||||
|
||||
Reference in New Issue
Block a user