0bf4b73ebc
Include the current brand description in template, custom, imitation, and KOL article-generation prompts while bounding prompt growth. - Add a shared brand-context builder that resolves the current brand, compacts long descriptions, and caps rendered output at 2000 runes. - Snapshot a structured brand prompt context on queued tasks so old jobs stay reproducible and retries stay stable. - Route brand-profile facts through the existing typed fact guard so founding dates and year counts get the same pre-persistence validation as RAG facts, with brand facts outranking conflicting RAG facts. - Fix the KOL creator to attach the brand scope its worker consumes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
163 lines
5.2 KiB
Go
163 lines
5.2 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/tenant/repository"
|
|
)
|
|
|
|
const (
|
|
generationPayloadTemplateKey = "__snapshot_template_key"
|
|
generationPayloadTemplateName = "__snapshot_template_name"
|
|
generationPayloadTemplatePromptTemplate = "__snapshot_template_prompt_template"
|
|
generationPayloadPromptRuleID = "__snapshot_prompt_rule_id"
|
|
generationPayloadPromptRuleName = "__snapshot_prompt_rule_name"
|
|
generationPayloadPromptRuleContent = "__snapshot_prompt_rule_content"
|
|
generationPayloadPromptRuleScene = "__snapshot_prompt_rule_scene"
|
|
generationPayloadPromptRuleTone = "__snapshot_prompt_rule_tone"
|
|
generationPayloadPromptRuleWordCount = "__snapshot_prompt_rule_word_count"
|
|
generationPayloadKnowledgePrompt = "__snapshot_knowledge_prompt"
|
|
generationPayloadKnowledgeFacts = "__snapshot_knowledge_fact_constraints"
|
|
generationPayloadBrandPromptContext = "__snapshot_brand_prompt_context"
|
|
)
|
|
|
|
func attachTemplateSnapshot(payload map[string]interface{}, record *repository.TemplateRecord) map[string]interface{} {
|
|
if payload == nil {
|
|
payload = map[string]interface{}{}
|
|
}
|
|
if record == nil {
|
|
return payload
|
|
}
|
|
|
|
payload[generationPayloadTemplateKey] = record.TemplateKey
|
|
payload[generationPayloadTemplateName] = record.TemplateName
|
|
if record.PromptTemplate != nil {
|
|
payload[generationPayloadTemplatePromptTemplate] = *record.PromptTemplate
|
|
}
|
|
|
|
return payload
|
|
}
|
|
|
|
func attachBrandPromptContext(payload map[string]interface{}, brandCtx *BrandPromptContext) map[string]interface{} {
|
|
if payload == nil {
|
|
payload = map[string]interface{}{}
|
|
}
|
|
if brandCtx == nil {
|
|
delete(payload, generationPayloadBrandPromptContext)
|
|
return payload
|
|
}
|
|
normalized := normalizeBrandPromptContext(*brandCtx)
|
|
payload[generationPayloadBrandPromptContext] = &normalized
|
|
return payload
|
|
}
|
|
|
|
func extractBrandPromptContext(payload map[string]interface{}) *BrandPromptContext {
|
|
if payload == nil || payload[generationPayloadBrandPromptContext] == nil {
|
|
return nil
|
|
}
|
|
encoded, err := json.Marshal(payload[generationPayloadBrandPromptContext])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var brandCtx BrandPromptContext
|
|
if err := json.Unmarshal(encoded, &brandCtx); err != nil {
|
|
return nil
|
|
}
|
|
if brandCtx.BrandID <= 0 {
|
|
return nil
|
|
}
|
|
brandCtx = normalizeBrandPromptContext(brandCtx)
|
|
return &brandCtx
|
|
}
|
|
|
|
func attachKnowledgeFactConstraints(payload map[string]interface{}, constraints []KnowledgeFactConstraint) map[string]interface{} {
|
|
if payload == nil {
|
|
payload = map[string]interface{}{}
|
|
}
|
|
if len(constraints) == 0 {
|
|
delete(payload, generationPayloadKnowledgeFacts)
|
|
return payload
|
|
}
|
|
payload[generationPayloadKnowledgeFacts] = constraints
|
|
return payload
|
|
}
|
|
|
|
func extractKnowledgeFactConstraints(payload map[string]interface{}) []KnowledgeFactConstraint {
|
|
if payload == nil || payload[generationPayloadKnowledgeFacts] == nil {
|
|
return nil
|
|
}
|
|
encoded, err := json.Marshal(payload[generationPayloadKnowledgeFacts])
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var constraints []KnowledgeFactConstraint
|
|
if err := json.Unmarshal(encoded, &constraints); err != nil {
|
|
return nil
|
|
}
|
|
return constraints
|
|
}
|
|
|
|
func extractTemplateSnapshot(payload map[string]interface{}) (string, string, *string) {
|
|
templateKey := extractString(payload, generationPayloadTemplateKey)
|
|
templateName := extractString(payload, generationPayloadTemplateName)
|
|
promptTemplateText := extractString(payload, generationPayloadTemplatePromptTemplate)
|
|
if templateKey == "" || templateName == "" {
|
|
return "", "", nil
|
|
}
|
|
if promptTemplateText == "" {
|
|
return templateKey, templateName, nil
|
|
}
|
|
return templateKey, templateName, &promptTemplateText
|
|
}
|
|
|
|
func attachPromptRuleSnapshot(payload map[string]interface{}, rule *promptRuleGenerationRecord) map[string]interface{} {
|
|
if payload == nil {
|
|
payload = map[string]interface{}{}
|
|
}
|
|
if rule == nil {
|
|
return payload
|
|
}
|
|
|
|
payload[generationPayloadPromptRuleID] = rule.ID
|
|
payload[generationPayloadPromptRuleName] = rule.Name
|
|
payload[generationPayloadPromptRuleContent] = rule.PromptContent
|
|
if rule.Scene != nil {
|
|
payload[generationPayloadPromptRuleScene] = *rule.Scene
|
|
}
|
|
if rule.DefaultTone != nil {
|
|
payload[generationPayloadPromptRuleTone] = *rule.DefaultTone
|
|
}
|
|
if rule.DefaultWordCount != nil {
|
|
payload[generationPayloadPromptRuleWordCount] = *rule.DefaultWordCount
|
|
}
|
|
|
|
return payload
|
|
}
|
|
|
|
func extractPromptRuleSnapshot(payload map[string]interface{}) *promptRuleGenerationRecord {
|
|
ruleID := int64(extractInt(payload, generationPayloadPromptRuleID))
|
|
name := extractString(payload, generationPayloadPromptRuleName)
|
|
content := extractString(payload, generationPayloadPromptRuleContent)
|
|
if ruleID <= 0 || content == "" {
|
|
return nil
|
|
}
|
|
|
|
record := &promptRuleGenerationRecord{
|
|
ID: ruleID,
|
|
Name: name,
|
|
PromptContent: content,
|
|
Status: "enabled",
|
|
}
|
|
if scene := extractString(payload, generationPayloadPromptRuleScene); scene != "" {
|
|
record.Scene = &scene
|
|
}
|
|
if tone := extractString(payload, generationPayloadPromptRuleTone); tone != "" {
|
|
record.DefaultTone = &tone
|
|
}
|
|
if wordCount := extractInt(payload, generationPayloadPromptRuleWordCount); wordCount > 0 {
|
|
value := wordCount
|
|
record.DefaultWordCount = &value
|
|
}
|
|
return record
|
|
}
|