package domain import ( "errors" "strings" "unicode/utf8" ) const ( RoleDeveloper = "developer" RoleSystem = "system" RoleUser = "user" RoleAssistant = "assistant" ) // NormalizeMessageRole validates and canonicalizes a provider-bound chat role. func NormalizeMessageRole(role string) (string, error) { if !utf8.ValidString(role) { return "", errors.New("message role must be valid UTF-8") } normalized := strings.ToLower(strings.TrimSpace(role)) switch normalized { case RoleDeveloper, RoleSystem, RoleUser, RoleAssistant: return normalized, nil default: return "", errors.New("message role must be developer, system, user, or assistant") } } // NormalizeCacheControl validates, canonicalizes, and copies cache metadata. func NormalizeCacheControl(control *CacheControl) (*CacheControl, error) { if control == nil { return nil, nil } if !utf8.ValidString(string(control.Type)) { return nil, errors.New("cache control type must be valid UTF-8") } if !utf8.ValidString(control.TTL) { return nil, errors.New("cache control ttl must be valid UTF-8") } cacheType := strings.TrimSpace(string(control.Type)) if cacheType == "" { return nil, errors.New("cache control type is required") } if CacheControlType(cacheType) != CacheControlEphemeral { return nil, errors.New("unsupported type") } ttl := strings.TrimSpace(control.TTL) if ttl != "" && ttl != "1h" { return nil, errors.New("unsupported ttl") } return &CacheControl{Type: CacheControlType(cacheType), TTL: ttl}, nil } // CloneRenderedMessages returns a deep copy of rendered messages. func CloneRenderedMessages(messages []RenderedMessage) []RenderedMessage { cloned := make([]RenderedMessage, len(messages)) for index, message := range messages { cloned[index] = message if message.CacheControl != nil { cacheControl := *message.CacheControl cloned[index].CacheControl = &cacheControl } } return cloned } // ConcatRenderedMessages returns an independently owned concatenation of messages. func ConcatRenderedMessages(prefix, suffix []RenderedMessage) []RenderedMessage { messages := make([]RenderedMessage, 0, len(prefix)+len(suffix)) messages = append(messages, CloneRenderedMessages(prefix)...) messages = append(messages, CloneRenderedMessages(suffix)...) return messages }