Centralize message role invariants
This commit is contained in:
78
internal/domain/message.go
Normal file
78
internal/domain/message.go
Normal file
@@ -0,0 +1,78 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user