From cc078b953adc4dca3f31737c15149b9a70555afa Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 6 May 2026 18:36:47 +0800 Subject: [PATCH] feat(claude): implement user ID hashing and enhance environment setup - Added a `hashUserID` function to generate a collision-safe hash for user IDs, improving metadata handling. - Updated the environment setup in `buildEnv` to override `metadata.user_id` with a sanitized value, ensuring compatibility with third-party APIs. - Enhanced logging for the environment variables to aid in debugging and monitoring. --- agent/sandbox/v2/claude/command.go | 22 ++++++++++++++++++++++ tools/image/generate.go | 4 ++++ tools/image/generate_schema.json | 6 +++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/agent/sandbox/v2/claude/command.go b/agent/sandbox/v2/claude/command.go index f2647f8a..7aed39e5 100644 --- a/agent/sandbox/v2/claude/command.go +++ b/agent/sandbox/v2/claude/command.go @@ -2,6 +2,8 @@ package claude import ( "context" + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" "regexp" @@ -26,6 +28,11 @@ var yaoSessionNS = uuid.MustParse("f47ac10b-58cc-4372-a567-0e02b2c3d479") var safeNameRe = regexp.MustCompile(`[^a-zA-Z0-9_\-.]`) +func hashUserID(raw string) string { + h := sha256.Sum256([]byte(raw)) + return hex.EncodeToString(h[:8]) // 16 hex chars — short yet collision-safe +} + func chatIDToSessionUUID(assistantID, chatID string) string { return uuid.NewSHA1(yaoSessionNS, []byte(assistantID+":"+chatID)).String() } @@ -234,6 +241,21 @@ func buildEnv(req *types.StreamRequest, p platform) map[string]string { env["ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES"]) logger.Debug("claude-env: MAX_THINKING_TOKENS=%s", env["MAX_THINKING_TOKENS"]) + // Override metadata.user_id with a sanitized value. + // Claude CLI sets metadata.user_id to a JSON object that third-party + // Anthropic-compatible APIs (e.g. DeepSeek) reject because the value + // doesn't match ^[a-zA-Z0-9_-]+$. + if _, ok := env["CLAUDE_CODE_EXTRA_BODY"]; !ok { + uid := "yao-sandbox" + if req.Config != nil && req.Config.Owner != "" { + uid = hashUserID(req.Config.Owner) + } else if assistantID != "" { + uid = hashUserID(assistantID) + } + env["CLAUDE_CODE_EXTRA_BODY"] = fmt.Sprintf(`{"metadata":{"user_id":"%s"}}`, uid) + } + logger.Debug("claude-env: EXTRA_BODY=%s", env["CLAUDE_CODE_EXTRA_BODY"]) + return env } diff --git a/tools/image/generate.go b/tools/image/generate.go index 3ab2329f..470e81fb 100644 --- a/tools/image/generate.go +++ b/tools/image/generate.go @@ -21,6 +21,7 @@ func GenerateHandler(proc *process.Process) interface{} { provider := proc.ArgsString(1) size := proc.ArgsString(2, "1024x1024") + model := proc.ArgsString(3) authInfo := authorized.ProcessAuthInfo(proc) if authInfo == nil { @@ -41,6 +42,9 @@ func GenerateHandler(proc *process.Process) interface{} { } options := map[string]interface{}{"size": size} + if model != "" { + options["model"] = model + } resp, err := agentLLM.GenerateImage(conn, prompt, options) if err != nil { return map[string]interface{}{"error": fmt.Sprintf("image generation failed: %v", err)} diff --git a/tools/image/generate_schema.json b/tools/image/generate_schema.json index 581cec40..7c453642 100644 --- a/tools/image/generate_schema.json +++ b/tools/image/generate_schema.json @@ -17,9 +17,13 @@ "type": "string", "description": "Image dimensions (default: 1024x1024). Common values: 1024x1024, 1024x1792, 1792x1024.", "default": "1024x1024" + }, + "model": { + "type": "string", + "description": "Model name to use (e.g. 'doubao-seedream-4-5-251128', 'dall-e-3'). Overrides the provider's default model. If omitted, the provider's default model is used." } }, "required": ["prompt"] }, - "x-process-args": ["$args.prompt", "$args.provider", "$args.size"] + "x-process-args": ["$args.prompt", "$args.provider", "$args.size", "$args.model"] }