package anthropic import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "nex/backend/internal/protocol/openai" ) func TestConvertRequest_Basic(t *testing.T) { temp := 0.7 req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 1024, Temperature: &temp, Messages: []AnthropicMessage{ { Role: "user", Content: []ContentBlock{ {Type: "text", Text: "Hello"}, }, }, }, } result, err := ConvertRequest(req) require.NoError(t, err) assert.Equal(t, "claude-3-opus", result.Model) assert.Equal(t, 1024, *result.MaxTokens) assert.Equal(t, &temp, result.Temperature) require.Len(t, result.Messages, 1) assert.Equal(t, "user", result.Messages[0].Role) assert.Equal(t, "Hello", result.Messages[0].Content) } func TestConvertRequest_WithSystem(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 100, System: "You are a helpful assistant.", Messages: []AnthropicMessage{ { Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}, }, }, } result, err := ConvertRequest(req) require.NoError(t, err) require.Len(t, result.Messages, 2) assert.Equal(t, "system", result.Messages[0].Role) assert.Equal(t, "You are a helpful assistant.", result.Messages[0].Content) assert.Equal(t, "user", result.Messages[1].Role) } func TestConvertRequest_DefaultMaxTokens(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 0, // 未设置 Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, } result, err := ConvertRequest(req) require.NoError(t, err) assert.Equal(t, 4096, *result.MaxTokens) } func TestConvertRequest_WithTools(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 100, Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, Tools: []AnthropicTool{ { Name: "get_weather", Description: "Get weather info", InputSchema: map[string]interface{}{"type": "object"}, }, }, } result, err := ConvertRequest(req) require.NoError(t, err) require.Len(t, result.Tools, 1) assert.Equal(t, "function", result.Tools[0].Type) assert.Equal(t, "get_weather", result.Tools[0].Function.Name) } func TestConvertRequest_WithStopSequences(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 100, StopSequences: []string{"STOP", "END"}, Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, } result, err := ConvertRequest(req) require.NoError(t, err) assert.Equal(t, []string{"STOP", "END"}, result.Stop) } func TestConvertRequest_ToolResult(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 100, Messages: []AnthropicMessage{ { Role: "user", Content: []ContentBlock{ { Type: "tool_result", ToolUseID: "tool_123", Content: "result data", }, }, }, }, } result, err := ConvertRequest(req) require.NoError(t, err) require.Len(t, result.Messages, 1) assert.Equal(t, "tool", result.Messages[0].Role) assert.Equal(t, "tool_123", result.Messages[0].ToolCallID) assert.Equal(t, "result data", result.Messages[0].Content) } func TestConvertResponse(t *testing.T) { resp := &openai.ChatCompletionResponse{ ID: "chatcmpl-123", Model: "gpt-4", Choices: []openai.Choice{ { Index: 0, Message: &openai.Message{Role: "assistant", Content: "Hello!"}, FinishReason: "stop", }, }, Usage: openai.Usage{PromptTokens: 10, CompletionTokens: 5}, } result, err := ConvertResponse(resp) require.NoError(t, err) assert.Equal(t, "chatcmpl-123", result.ID) assert.Equal(t, "message", result.Type) assert.Equal(t, "assistant", result.Role) assert.Equal(t, "end_turn", result.StopReason) require.Len(t, result.Content, 1) assert.Equal(t, "text", result.Content[0].Type) assert.Equal(t, "Hello!", result.Content[0].Text) assert.Equal(t, 10, result.Usage.InputTokens) assert.Equal(t, 5, result.Usage.OutputTokens) } func TestConvertResponse_ToolCalls(t *testing.T) { args, _ := json.Marshal(map[string]interface{}{"city": "Beijing"}) resp := &openai.ChatCompletionResponse{ ID: "chatcmpl-456", Model: "gpt-4", Choices: []openai.Choice{ { Index: 0, Message: &openai.Message{ Role: "assistant", ToolCalls: []openai.ToolCall{ { ID: "call_123", Type: "function", Function: openai.FunctionCall{ Name: "get_weather", Arguments: string(args), }, }, }, }, FinishReason: "tool_calls", }, }, Usage: openai.Usage{}, } result, err := ConvertResponse(resp) require.NoError(t, err) assert.Equal(t, "tool_use", result.StopReason) require.Len(t, result.Content, 1) assert.Equal(t, "tool_use", result.Content[0].Type) assert.Equal(t, "call_123", result.Content[0].ID) assert.Equal(t, "get_weather", result.Content[0].Name) } func TestConvertToolChoice_String(t *testing.T) { tests := []struct { name string input interface{} wantErr bool check func(interface{}) }{ {"auto字符串", "auto", false, func(r interface{}) { assert.Equal(t, "auto", r) }}, {"any字符串", "any", false, func(r interface{}) { assert.Equal(t, "auto", r) }}, {"无效字符串", "invalid", true, nil}, {"tool对象", map[string]interface{}{"type": "tool", "name": "my_func"}, false, func(r interface{}) { m := r.(map[string]interface{}) assert.Equal(t, "function", m["type"]) }}, {"缺少name的tool对象", map[string]interface{}{"type": "tool"}, true, nil}, {"缺少type的对象", map[string]interface{}{"name": "func"}, true, nil}, {"无效类型", 42, true, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := convertToolChoice(tt.input) if tt.wantErr { assert.Error(t, err) } else { require.NoError(t, err) tt.check(result) } }) } } func TestValidateRequest(t *testing.T) { t.Run("有效请求", func(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 100, Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, } errs := ValidateRequest(req) assert.Nil(t, errs) }) t.Run("缺少模型", func(t *testing.T) { req := &MessagesRequest{ MaxTokens: 100, Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, } errs := ValidateRequest(req) assert.NotNil(t, errs) assert.Contains(t, errs["model"], "不能为空") }) t.Run("MaxTokens为0", func(t *testing.T) { req := &MessagesRequest{ Model: "claude-3-opus", MaxTokens: 0, Messages: []AnthropicMessage{ {Role: "user", Content: []ContentBlock{{Type: "text", Text: "Hi"}}}, }, } errs := ValidateRequest(req) assert.NotNil(t, errs) }) }