diff --git a/llm/transformer/anthropic/outbound_convert.go b/llm/transformer/anthropic/outbound_convert.go index f10b2aef9..98697d736 100644 --- a/llm/transformer/anthropic/outbound_convert.go +++ b/llm/transformer/anthropic/outbound_convert.go @@ -120,8 +120,15 @@ func buildBaseRequest(chatReq *llm.Request, config *Config) *MessageRequest { MaxTokens: resolveMaxTokens(chatReq), } + var userID string if chatReq.Metadata != nil && chatReq.Metadata["user_id"] != "" { - req.Metadata = &AnthropicMetadata{UserID: chatReq.Metadata["user_id"]} + userID = chatReq.Metadata["user_id"] + } else if chatReq.User != nil && *chatReq.User != "" { + userID = *chatReq.User + } + + if userID != "" { + req.Metadata = &AnthropicMetadata{UserID: shared.SanitizeUserID(userID)} } // DeepSeek Anthropic format supports output_config.effort. When reasoning_effort diff --git a/llm/transformer/doubao/outbound.go b/llm/transformer/doubao/outbound.go index fbd4e7803..eb4535309 100644 --- a/llm/transformer/doubao/outbound.go +++ b/llm/transformer/doubao/outbound.go @@ -17,6 +17,7 @@ import ( "github.com/looplj/axonhub/llm/internal/pkg/xurl" "github.com/looplj/axonhub/llm/transformer" "github.com/looplj/axonhub/llm/transformer/openai" + "github.com/looplj/axonhub/llm/transformer/shared" ) // Config holds all configuration for the Doubao outbound transformer. @@ -142,8 +143,18 @@ func (t *OutboundTransformer) TransformRequest( RequestID: "", } - if llmReq.Metadata != nil { - doubaoReq.UserID = llmReq.Metadata["user_id"] + var userID string + if llmReq.Metadata != nil && llmReq.Metadata["user_id"] != "" { + userID = llmReq.Metadata["user_id"] + } else if llmReq.User != nil && *llmReq.User != "" { + userID = *llmReq.User + } + + if userID != "" { + doubaoReq.UserID = shared.SanitizeUserID(userID) + } + + if llmReq.Metadata != nil && llmReq.Metadata["request_id"] != "" { doubaoReq.RequestID = llmReq.Metadata["request_id"] } diff --git a/llm/transformer/shared/userid.go b/llm/transformer/shared/userid.go new file mode 100644 index 000000000..02eaf4809 --- /dev/null +++ b/llm/transformer/shared/userid.go @@ -0,0 +1,35 @@ +package shared + +import "strings" + +// SanitizeUserID ensures the user ID matches common platform constraints: +// ^[a-zA-Z0-9_-]{6,128}$ +func SanitizeUserID(userID string) string { + if userID == "" { + return "" + } + + var sb strings.Builder + for _, c := range userID { + if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' { + sb.WriteRune(c) + } else { + sb.WriteRune('_') + } + } + + sanitized := sb.String() + + if len(sanitized) < 6 { + sanitized = "user_" + sanitized + if len(sanitized) < 6 { + sanitized = sanitized + strings.Repeat("_", 6-len(sanitized)) + } + } + + if len(sanitized) > 128 { + sanitized = sanitized[:128] + } + + return sanitized +} diff --git a/llm/transformer/zai/outbound.go b/llm/transformer/zai/outbound.go index dfc69df8f..af5765f38 100644 --- a/llm/transformer/zai/outbound.go +++ b/llm/transformer/zai/outbound.go @@ -129,8 +129,18 @@ func (t *OutboundTransformer) TransformRequest( RequestID: "", } - if llmReq.Metadata != nil { - zaiReq.UserID = llmReq.Metadata["user_id"] + var userID string + if llmReq.Metadata != nil && llmReq.Metadata["user_id"] != "" { + userID = llmReq.Metadata["user_id"] + } else if llmReq.User != nil && *llmReq.User != "" { + userID = *llmReq.User + } + + if userID != "" { + zaiReq.UserID = shared.SanitizeUserID(userID) + } + + if llmReq.Metadata != nil && llmReq.Metadata["request_id"] != "" { zaiReq.RequestID = llmReq.Metadata["request_id"] }