Files
descrybe/apps/api/internal/processing/enhance_log.go
T

128 lines
4.4 KiB
Go
Raw Normal View History

2026-08-16 21:35:48 +02:00
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),
)
}