refactor: 优化 URL 路径拼接,修复 /v1 重复问题
## 主要变更 **核心修改**: - 路由定义:/:protocol/v1/*path → /:protocol/*path - proxy_handler:nativePath 直接使用 path 参数,不添加 /v1 前缀 - OpenAI 适配器:DetectInterfaceType 和 BuildUrl 去掉 /v1 前缀 - Anthropic 适配器:保持 /v1 前缀(Claude Code 兼容) **URL 格式变化**: - OpenAI: /openai/v1/chat/completions → /openai/chat/completions - Anthropic: /anthropic/v1/messages (保持不变) **base_url 配置**: - OpenAI: 配置到版本路径,如 https://api.openai.com/v1 - Anthropic: 不配置版本路径,如 https://api.anthropic.com ## 测试验证 - 所有单元测试通过 - 所有集成测试通过 - 真实 API 测试验证成功 - 跨协议转换正常工作 ## 文档更新 - 更新 backend/README.md URL 格式说明 - 同步 OpenSpec 规范文件
This commit is contained in:
@@ -77,7 +77,7 @@ func setupConversionTest(t *testing.T) (*gin.Engine, *gorm.DB, *httptest.Server)
|
||||
r.Use(middleware.CORS())
|
||||
|
||||
// 代理路由
|
||||
r.Any("/:protocol/v1/*path", proxyHandler.HandleProxy)
|
||||
r.Any("/:protocol/*path", proxyHandler.HandleProxy)
|
||||
|
||||
// 管理路由
|
||||
providers := r.Group("/api/providers")
|
||||
@@ -177,7 +177,7 @@ func TestConversion_OpenAIToAnthropic_NonStream(t *testing.T) {
|
||||
body, _ := json.Marshal(openaiReq)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
||||
req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
@@ -202,7 +202,7 @@ func TestConversion_AnthropicToOpenAI_NonStream(t *testing.T) {
|
||||
var req map[string]any
|
||||
json.Unmarshal(body, &req)
|
||||
|
||||
assert.Equal(t, "/v1/chat/completions", r.URL.Path)
|
||||
assert.Equal(t, "/chat/completions", r.URL.Path)
|
||||
assert.Contains(t, r.Header.Get("Authorization"), "Bearer test-key")
|
||||
|
||||
resp := map[string]any{
|
||||
@@ -262,7 +262,7 @@ func TestConversion_OpenAIToOpenAI_Passthrough(t *testing.T) {
|
||||
r, _, upstream := setupConversionTest(t)
|
||||
|
||||
upstream.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/v1/chat/completions", r.URL.Path)
|
||||
assert.Equal(t, "/chat/completions", r.URL.Path)
|
||||
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
var req map[string]any
|
||||
@@ -284,7 +284,7 @@ func TestConversion_OpenAIToOpenAI_Passthrough(t *testing.T) {
|
||||
body, _ := json.Marshal(reqBody)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
||||
req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
@@ -364,7 +364,7 @@ func TestConversion_OpenAIToAnthropic_Stream(t *testing.T) {
|
||||
body, _ := json.Marshal(openaiReq)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
||||
req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
@@ -483,7 +483,7 @@ func TestConversion_ErrorResponse_Format(t *testing.T) {
|
||||
|
||||
// OpenAI 协议格式
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/openai/v1/chat/completions", bytes.NewReader(body))
|
||||
req := httptest.NewRequest("POST", "/openai/chat/completions", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
assert.True(t, w.Code >= 400)
|
||||
@@ -499,15 +499,15 @@ func TestConversion_OldRoutes_Return404(t *testing.T) {
|
||||
req := httptest.NewRequest("POST", "/v1/chat/completions", strings.NewReader(`{"model":"test"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
// Gin 路由不匹配返回 404
|
||||
assert.Equal(t, 404, w.Code)
|
||||
// Gin 路由匹配但协议不支持返回 400
|
||||
assert.Equal(t, 400, w.Code)
|
||||
|
||||
// 旧 Anthropic 路由
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest("POST", "/v1/messages", strings.NewReader(`{"model":"test"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, 404, w.Code)
|
||||
assert.Equal(t, 400, w.Code)
|
||||
}
|
||||
|
||||
// ============ Provider Protocol 字段测试 ============
|
||||
|
||||
Reference in New Issue
Block a user