128 lines
4.4 KiB
Go
128 lines
4.4 KiB
Go
package processing
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// enhanceLogHeadTail caps each end of prompt/response bodies in worker logs.
|
|
const enhanceLogHeadTail = 500
|
|
|
|
func emptyLogDash(s string) string {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return "-"
|
|
}
|
|
return s
|
|
}
|
|
|
|
// truncateLogEnds returns a PII-safe preview: total rune length plus first/last
|
|
// slices when the body is large (avoids megabyte log lines).
|
|
func truncateLogEnds(s string, head, tail int) string {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return "len=0"
|
|
}
|
|
runes := []rune(s)
|
|
n := len(runes)
|
|
if head < 0 {
|
|
head = 0
|
|
}
|
|
if tail < 0 {
|
|
tail = 0
|
|
}
|
|
if n <= head+tail+32 {
|
|
return fmt.Sprintf("len=%d body=%q", n, s)
|
|
}
|
|
return fmt.Sprintf("len=%d head=%q tail=%q", n, string(runes[:head]), string(runes[n-tail:]))
|
|
}
|
|
|
|
// formulaTemplateSummary summarizes a category title/description formula for logs
|
|
// (section/element types + JSON byte length + short content hash — not a full dump).
|
|
func formulaTemplateSummary(template any, kind string) string {
|
|
if template == nil {
|
|
return "none"
|
|
}
|
|
raw, err := json.Marshal(template)
|
|
if err != nil || len(raw) == 0 || string(raw) == "null" || string(raw) == "{}" {
|
|
return "none"
|
|
}
|
|
sum := sha256.Sum256(raw)
|
|
hash8 := hex.EncodeToString(sum[:4])
|
|
switch kind {
|
|
case "title":
|
|
els, sep, ok := parseTitleFormula(template)
|
|
if !ok {
|
|
return fmt.Sprintf("unparsed len=%d hash=%s", len(raw), hash8)
|
|
}
|
|
types := make([]string, 0, len(els))
|
|
for _, el := range els {
|
|
types = append(types, el.Type)
|
|
}
|
|
return fmt.Sprintf("elements=%s sep=%q len=%d hash=%s", strings.Join(types, "+"), sep, len(raw), hash8)
|
|
case "description":
|
|
sections, ok := parseDescriptionFormulaSections(template)
|
|
if !ok {
|
|
return fmt.Sprintf("unparsed len=%d hash=%s", len(raw), hash8)
|
|
}
|
|
types := make([]string, 0, len(sections))
|
|
for _, sec := range sections {
|
|
if t := strings.TrimSpace(sec.Type); t != "" {
|
|
types = append(types, t)
|
|
}
|
|
}
|
|
return fmt.Sprintf("sections=%s len=%d hash=%s", strings.Join(types, "+"), len(raw), hash8)
|
|
default:
|
|
return fmt.Sprintf("len=%d hash=%s", len(raw), hash8)
|
|
}
|
|
}
|
|
|
|
func finishReasonFromCompletion(comp Completion) string {
|
|
m, ok := comp.Raw.(map[string]any)
|
|
if !ok || m == nil {
|
|
return ""
|
|
}
|
|
fr, _ := m["finish_reason"].(string)
|
|
return strings.TrimSpace(fr)
|
|
}
|
|
|
|
func enhanceFormulaOverride(in ProductInput) bool {
|
|
return FormatDescriptionFormulaConstraint(in.DescriptionTemplate) != "" ||
|
|
FormatTitleFormulaConstraint(in.TitleTemplate) != ""
|
|
}
|
|
|
|
// logEnhancePrompt emits one structured line before the LLM call (truncated prompts).
|
|
func logEnhancePrompt(in ProductInput, categoryUID, categoryName, system, user string) {
|
|
log.Printf("processing: ai_enhance prompt job=%s company=%s raw=%s category_uid=%q category_name=%q title_formula=%s desc_formula=%s formula_override=%t system=%s user=%s",
|
|
emptyLogDash(in.JobID), emptyLogDash(in.CompanyID), emptyLogDash(in.RawProductID),
|
|
strings.TrimSpace(categoryUID), strings.TrimSpace(categoryName),
|
|
formulaTemplateSummary(in.TitleTemplate, "title"),
|
|
formulaTemplateSummary(in.DescriptionTemplate, "description"),
|
|
enhanceFormulaOverride(in),
|
|
truncateLogEnds(system, enhanceLogHeadTail, enhanceLogHeadTail),
|
|
truncateLogEnds(user, enhanceLogHeadTail, enhanceLogHeadTail),
|
|
)
|
|
}
|
|
|
|
// logEnhanceOutcome emits one structured line for the enhance result (truncated response).
|
|
func logEnhanceOutcome(in ProductInput, categoryUID, categoryName, outcome, reason string, comp Completion, elapsed time.Duration, forceReason string) {
|
|
log.Printf("processing: ai_enhance outcome=%s reason=%s job=%s company=%s raw=%s category_uid=%q category_name=%q title_formula=%s desc_formula=%s formula_override=%t finish_reason=%s prompt_tokens=%d completion_tokens=%d total_tokens=%d elapsed=%s response=%s force_reason=%s",
|
|
emptyLogDash(outcome), emptyLogDash(reason),
|
|
emptyLogDash(in.JobID), emptyLogDash(in.CompanyID), emptyLogDash(in.RawProductID),
|
|
strings.TrimSpace(categoryUID), strings.TrimSpace(categoryName),
|
|
formulaTemplateSummary(in.TitleTemplate, "title"),
|
|
formulaTemplateSummary(in.DescriptionTemplate, "description"),
|
|
enhanceFormulaOverride(in),
|
|
emptyLogDash(finishReasonFromCompletion(comp)),
|
|
comp.PromptTokens, comp.OutputTokens, comp.TotalTokens,
|
|
elapsed.Round(time.Millisecond),
|
|
truncateLogEnds(comp.Text, enhanceLogHeadTail, enhanceLogHeadTail),
|
|
emptyLogDash(forceReason),
|
|
)
|
|
}
|