fixes
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AppendFormulaConstraints appends language-agnostic title/description formula
|
||||
// guidance to the enhance user template (before {{var}} render). Empty templates
|
||||
// are no-ops. Shared skeleton stays in company/built-in prompts; formulas only
|
||||
// constrain structure for the active language via {{language}} elsewhere.
|
||||
func AppendFormulaConstraints(userTpl string, titleTemplate, descriptionTemplate any) string {
|
||||
userTpl = strings.TrimSpace(userTpl)
|
||||
titleBlock := FormatTitleFormulaConstraint(titleTemplate)
|
||||
descBlock := FormatDescriptionFormulaConstraint(descriptionTemplate)
|
||||
if titleBlock == "" && descBlock == "" {
|
||||
return userTpl
|
||||
}
|
||||
var b strings.Builder
|
||||
if userTpl != "" {
|
||||
b.WriteString(userTpl)
|
||||
b.WriteString("\n\n")
|
||||
}
|
||||
if titleBlock != "" {
|
||||
b.WriteString(titleBlock)
|
||||
if descBlock != "" {
|
||||
b.WriteString("\n\n")
|
||||
}
|
||||
}
|
||||
if descBlock != "" {
|
||||
b.WriteString(descBlock)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// FormatTitleFormulaConstraint turns categories.title_template into enhance-prompt
|
||||
// constraints (element order: literal text + attribute variable keys).
|
||||
func FormatTitleFormulaConstraint(template any) string {
|
||||
elements, separator, ok := parseTitleFormula(template)
|
||||
if !ok || len(elements) == 0 {
|
||||
return ""
|
||||
}
|
||||
sep := separator
|
||||
if sep == "" {
|
||||
sep = " "
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString("Title formula (order matters; join with ")
|
||||
b.WriteString(strconv.Quote(sep))
|
||||
b.WriteString("). Build name from Attrs using this structure:\n")
|
||||
for i, el := range elements {
|
||||
switch el.Type {
|
||||
case "text":
|
||||
fmt.Fprintf(&b, "%d. text %s\n", i+1, strconv.Quote(el.Value))
|
||||
case "variable":
|
||||
key := strings.TrimSpace(el.Value)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, "%d. attr [%s]\n", i+1, key)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
b.WriteString("Prefer Attrs values for [attr] slots; keep literal text as written; write name in {{language}}.")
|
||||
return strings.TrimSpace(b.String())
|
||||
}
|
||||
|
||||
// FormatDescriptionFormulaConstraint turns categories.description_template sections
|
||||
// into bullet instructions for the enhance user prompt.
|
||||
func FormatDescriptionFormulaConstraint(template any) string {
|
||||
sections, ok := parseDescriptionFormulaSections(template)
|
||||
if !ok || len(sections) == 0 {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
b.WriteString("Description formula (cover each section in order; write in {{language}}):\n")
|
||||
for _, s := range sections {
|
||||
typ := strings.TrimSpace(s.Type)
|
||||
instr := strings.TrimSpace(s.Instructions)
|
||||
if typ == "" && instr == "" {
|
||||
continue
|
||||
}
|
||||
if typ == "" {
|
||||
typ = "section"
|
||||
}
|
||||
if instr == "" {
|
||||
fmt.Fprintf(&b, "- %s\n", typ)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&b, "- %s: %s\n", typ, instr)
|
||||
}
|
||||
return strings.TrimSpace(b.String())
|
||||
}
|
||||
|
||||
type titleFormulaElement struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type descriptionFormulaSection struct {
|
||||
Type string `json:"type"`
|
||||
Instructions string `json:"instructions"`
|
||||
}
|
||||
|
||||
func parseTitleFormula(template any) (elements []titleFormulaElement, separator string, ok bool) {
|
||||
if template == nil {
|
||||
return nil, "", false
|
||||
}
|
||||
obj, err := asObjectMap(template)
|
||||
if err != nil || obj == nil {
|
||||
return nil, "", false
|
||||
}
|
||||
sep, _ := obj["separator"].(string)
|
||||
rawEls, exists := obj["elements"]
|
||||
if !exists || rawEls == nil {
|
||||
return nil, "", false
|
||||
}
|
||||
b, err := json.Marshal(rawEls)
|
||||
if err != nil {
|
||||
return nil, "", false
|
||||
}
|
||||
var parsed []titleFormulaElement
|
||||
if err := json.Unmarshal(b, &parsed); err != nil {
|
||||
return nil, "", false
|
||||
}
|
||||
out := make([]titleFormulaElement, 0, len(parsed))
|
||||
for _, el := range parsed {
|
||||
t := strings.ToLower(strings.TrimSpace(el.Type))
|
||||
v := strings.TrimSpace(el.Value)
|
||||
if t != "text" && t != "variable" {
|
||||
continue
|
||||
}
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, titleFormulaElement{Type: t, Value: v})
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil, "", false
|
||||
}
|
||||
return out, sep, true
|
||||
}
|
||||
|
||||
func parseDescriptionFormulaSections(template any) ([]descriptionFormulaSection, bool) {
|
||||
if template == nil {
|
||||
return nil, false
|
||||
}
|
||||
obj, err := asObjectMap(template)
|
||||
if err != nil || obj == nil {
|
||||
return nil, false
|
||||
}
|
||||
raw, exists := obj["sections"]
|
||||
if !exists || raw == nil {
|
||||
return nil, false
|
||||
}
|
||||
b, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
var parsed []descriptionFormulaSection
|
||||
if err := json.Unmarshal(b, &parsed); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
out := make([]descriptionFormulaSection, 0, len(parsed))
|
||||
for _, s := range parsed {
|
||||
t := strings.ToLower(strings.TrimSpace(s.Type))
|
||||
instr := strings.TrimSpace(s.Instructions)
|
||||
if t == "" && instr == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, descriptionFormulaSection{Type: t, Instructions: instr})
|
||||
}
|
||||
return out, len(out) > 0
|
||||
}
|
||||
|
||||
// categoryFormulasFor resolves title/description templates for a category key
|
||||
// (unique_id or name). Explicit ProductInput fields win over the job cache map.
|
||||
func categoryFormulasFor(in ProductInput, category string) (title, description any) {
|
||||
if in.TitleTemplate != nil || in.DescriptionTemplate != nil {
|
||||
return in.TitleTemplate, in.DescriptionTemplate
|
||||
}
|
||||
key := strings.ToLower(strings.TrimSpace(category))
|
||||
if key == "" || len(in.CategoryFormulasByKey) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
f, ok := in.CategoryFormulasByKey[key]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return f.TitleTemplate, f.DescriptionTemplate
|
||||
}
|
||||
|
||||
func asObjectMap(v any) (map[string]any, error) {
|
||||
switch t := v.(type) {
|
||||
case map[string]any:
|
||||
return t, nil
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
b, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(b) == 0 || string(b) == "null" {
|
||||
return nil, nil
|
||||
}
|
||||
var obj map[string]any
|
||||
if err := json.Unmarshal(b, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user