From 019b606b1349b6816b3993ed459b750ad24e5dc8 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 31 Jan 2026 14:36:01 +0800 Subject: [PATCH] Fix sandbox integration tests for claude-proxy architecture - Update TestClaudeCommandBuilding to expect new command format - Rename TestClaudeCCRConfigBuilding to TestClaudeProxyConfigBuilding - Update assertions for claude-proxy env vars instead of CCR - Apply gofmt formatting to proxy/types.go Co-authored-by: Cursor --- agent/assistant/sandbox_integration_test.go | 40 +++++----- sandbox/proxy/types.go | 82 ++++++++++----------- 2 files changed, 62 insertions(+), 60 deletions(-) diff --git a/agent/assistant/sandbox_integration_test.go b/agent/assistant/sandbox_integration_test.go index 4852f9dd..84d229a0 100644 --- a/agent/assistant/sandbox_integration_test.go +++ b/agent/assistant/sandbox_integration_test.go @@ -78,28 +78,30 @@ func TestClaudeCommandBuilding(t *testing.T) { require.NoError(t, err) // Verify command structure - // Command is now: ["bash", "-c", "nohup ccr start ... && ccr code ..."] + // Command is now: ["bash", "-c", "cat << 'INPUTEOF' | claude -p ... INPUTEOF"] assert.NotEmpty(t, cmd) assert.Equal(t, "bash", cmd[0], "Command should start with bash") assert.Equal(t, "-c", cmd[1], "Second arg should be -c") - assert.Contains(t, cmd[2], "ccr code", "Bash command should contain ccr code") + assert.Contains(t, cmd[2], "claude -p", "Bash command should contain claude -p") assert.Contains(t, cmd[2], "--permission-mode", "Should include permission mode") + assert.Contains(t, cmd[2], "--input-format", "Should include input-format flag") + assert.Contains(t, cmd[2], "--output-format", "Should include output-format flag") + assert.Contains(t, cmd[2], "--verbose", "Should include verbose flag") + assert.Contains(t, cmd[2], "stream-json", "Should use stream-json format") + assert.Contains(t, cmd[2], "INPUTEOF", "Should use heredoc for input") t.Logf("Built command: %v", cmd) - // Verify environment variables + // Verify environment variables (claude-proxy mode) assert.NotEmpty(t, env) - assert.Equal(t, "https://ark.cn-beijing.volces.com/api/v3", env["CCR_API_BASE"]) - assert.Equal(t, "test-api-key", env["CCR_API_KEY"]) - assert.Equal(t, "ep-xxxxx", env["CCR_MODEL"]) + assert.Equal(t, "http://127.0.0.1:3456", env["ANTHROPIC_BASE_URL"], "Should set proxy base URL") + assert.Equal(t, "dummy", env["ANTHROPIC_API_KEY"], "Should set dummy API key for proxy") assert.Equal(t, "10", env["CLAUDE_MAX_TURNS"]) - assert.Equal(t, "acceptEdits", env["CLAUDE_PERMISSION_MODE"]) - assert.Equal(t, "stream-json", env["CLAUDE_OUTPUT_FORMAT"]) assert.Contains(t, env["CLAUDE_SYSTEM_PROMPT"], "You are a helpful coding assistant") t.Logf("Built environment: %v", env) } -// TestClaudeCCRConfigBuilding tests that CCR config is correctly built -func TestClaudeCCRConfigBuilding(t *testing.T) { +// TestClaudeProxyConfigBuilding tests that claude-proxy config is correctly built +func TestClaudeProxyConfigBuilding(t *testing.T) { test.Prepare(t, config.Conf) defer test.Clean() @@ -109,20 +111,20 @@ func TestClaudeCCRConfigBuilding(t *testing.T) { Model: "ep-xxxxx", } - configJSON, err := claude.BuildCCRConfig(opts) + configJSON, err := claude.BuildProxyConfig(opts) require.NoError(t, err) require.NotEmpty(t, configJSON) - t.Logf("CCR config: %s", string(configJSON)) + t.Logf("Proxy config: %s", string(configJSON)) - // Verify the JSON contains expected fields (CCR uses snake_case) - assert.Contains(t, string(configJSON), "api_base_url") + // Verify the JSON contains expected fields for claude-proxy + assert.Contains(t, string(configJSON), "backend") assert.Contains(t, string(configJSON), "api_key") - assert.Contains(t, string(configJSON), "models") - // Verify CCR format fields - assert.Contains(t, string(configJSON), "Providers") - assert.Contains(t, string(configJSON), "Router") - assert.Contains(t, string(configJSON), "volcengine") + assert.Contains(t, string(configJSON), "model") + assert.Contains(t, string(configJSON), "test-api-key") + assert.Contains(t, string(configJSON), "ep-xxxxx") + // Backend URL should end with /chat/completions + assert.Contains(t, string(configJSON), "/chat/completions") } // TestDefaultImageSelection tests that default images are correctly selected diff --git a/sandbox/proxy/types.go b/sandbox/proxy/types.go index e5132942..296f1a0e 100644 --- a/sandbox/proxy/types.go +++ b/sandbox/proxy/types.go @@ -6,18 +6,18 @@ package proxy // AnthropicRequest represents a request to the Anthropic Messages API type AnthropicRequest struct { - Model string `json:"model"` - Messages []AnthropicMsg `json:"messages"` - System interface{} `json:"system,omitempty"` // string or []SystemBlock - MaxTokens int `json:"max_tokens"` - Stream bool `json:"stream,omitempty"` - Temperature *float64 `json:"temperature,omitempty"` - TopP *float64 `json:"top_p,omitempty"` - TopK *int `json:"top_k,omitempty"` - StopSequences []string `json:"stop_sequences,omitempty"` - Tools []AnthropicTool `json:"tools,omitempty"` + Model string `json:"model"` + Messages []AnthropicMsg `json:"messages"` + System interface{} `json:"system,omitempty"` // string or []SystemBlock + MaxTokens int `json:"max_tokens"` + Stream bool `json:"stream,omitempty"` + Temperature *float64 `json:"temperature,omitempty"` + TopP *float64 `json:"top_p,omitempty"` + TopK *int `json:"top_k,omitempty"` + StopSequences []string `json:"stop_sequences,omitempty"` + Tools []AnthropicTool `json:"tools,omitempty"` ToolChoice *AnthropicToolChoice `json:"tool_choice,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` } // AnthropicMsg represents a message in Anthropic format @@ -42,14 +42,14 @@ type ContentBlock struct { Input interface{} `json:"input,omitempty"` // For tool_result blocks - ToolUseID string `json:"tool_use_id,omitempty"` + ToolUseID string `json:"tool_use_id,omitempty"` Content interface{} `json:"content,omitempty"` // string or []ContentBlock - IsError bool `json:"is_error,omitempty"` + IsError bool `json:"is_error,omitempty"` } // ImageSource represents an image source in Anthropic format type ImageSource struct { - Type string `json:"type"` // "base64" or "url" + Type string `json:"type"` // "base64" or "url" MediaType string `json:"media_type,omitempty"` // e.g., "image/jpeg" Data string `json:"data,omitempty"` // base64 encoded data URL string `json:"url,omitempty"` // URL for url type @@ -94,12 +94,12 @@ type Usage struct { // AnthropicStreamEvent represents an SSE event in Anthropic format type AnthropicStreamEvent struct { - Type string `json:"type"` - Index int `json:"index,omitempty"` + Type string `json:"type"` + Index int `json:"index,omitempty"` Message *AnthropicResponse `json:"message,omitempty"` - ContentBlock *ContentBlock `json:"content_block,omitempty"` - Delta *DeltaContent `json:"delta,omitempty"` - Usage *Usage `json:"usage,omitempty"` + ContentBlock *ContentBlock `json:"content_block,omitempty"` + Delta *DeltaContent `json:"delta,omitempty"` + Usage *Usage `json:"usage,omitempty"` } // DeltaContent represents delta content in streaming @@ -116,16 +116,16 @@ type DeltaContent struct { // OpenAIRequest represents a request to OpenAI Chat Completions API type OpenAIRequest struct { - Model string `json:"model"` - Messages []OpenAIMsg `json:"messages"` - MaxTokens int `json:"max_tokens,omitempty"` - Stream bool `json:"stream,omitempty"` - StreamOptions *StreamOptions `json:"stream_options,omitempty"` - Temperature *float64 `json:"temperature,omitempty"` - TopP *float64 `json:"top_p,omitempty"` - Stop []string `json:"stop,omitempty"` - Tools []OpenAITool `json:"tools,omitempty"` - ToolChoice interface{} `json:"tool_choice,omitempty"` // "auto", "none", "required", or object + Model string `json:"model"` + Messages []OpenAIMsg `json:"messages"` + MaxTokens int `json:"max_tokens,omitempty"` + Stream bool `json:"stream,omitempty"` + StreamOptions *StreamOptions `json:"stream_options,omitempty"` + Temperature *float64 `json:"temperature,omitempty"` + TopP *float64 `json:"top_p,omitempty"` + Stop []string `json:"stop,omitempty"` + Tools []OpenAITool `json:"tools,omitempty"` + ToolChoice interface{} `json:"tool_choice,omitempty"` // "auto", "none", "required", or object } // StreamOptions represents stream options in OpenAI format @@ -144,8 +144,8 @@ type OpenAIMsg struct { // OpenAIContent represents content in OpenAI messages (for multimodal) type OpenAIContent struct { - Type string `json:"type"` - Text string `json:"text,omitempty"` + Type string `json:"type"` + Text string `json:"text,omitempty"` ImageURL *OpenAIImageURL `json:"image_url,omitempty"` } @@ -170,10 +170,10 @@ type OpenAIFunction struct { // OpenAIToolCall represents a tool call in OpenAI format type OpenAIToolCall struct { - ID string `json:"id"` - Type string `json:"type"` - Function OpenAIFunctionCall `json:"function"` - Index *int `json:"index,omitempty"` // For streaming + ID string `json:"id"` + Type string `json:"type"` + Function OpenAIFunctionCall `json:"function"` + Index *int `json:"index,omitempty"` // For streaming } // OpenAIFunctionCall represents a function call in OpenAI format @@ -194,9 +194,9 @@ type OpenAIResponse struct { // OpenAIChoice represents a choice in OpenAI response type OpenAIChoice struct { - Index int `json:"index"` - Message OpenAIMsg `json:"message"` - FinishReason string `json:"finish_reason"` + Index int `json:"index"` + Message OpenAIMsg `json:"message"` + FinishReason string `json:"finish_reason"` } // OpenAIUsage represents usage statistics in OpenAI format @@ -218,9 +218,9 @@ type OpenAIStreamChunk struct { // OpenAIStreamChoice represents a choice in OpenAI streaming response type OpenAIStreamChoice struct { - Index int `json:"index"` - Delta OpenAIStreamDelta `json:"delta"` - FinishReason string `json:"finish_reason,omitempty"` + Index int `json:"index"` + Delta OpenAIStreamDelta `json:"delta"` + FinishReason string `json:"finish_reason,omitempty"` } // OpenAIStreamDelta represents delta content in OpenAI streaming