refactor: remove endpoint tests and enhance URL handling

- Deleted outdated test files for OpenAI endpoint functionality.
- Improved the OpenAIEndpointURL function to better handle custom paths and version segments.
- Introduced a new helper function, stripEndpointVersionPrefix, to streamline endpoint processing.
This commit is contained in:
上玄
2026-07-04 19:03:14 +08:00
parent c50e7d0606
commit 86a655fb5d
3 changed files with 30 additions and 156 deletions
+29 -6
View File
@@ -304,19 +304,24 @@ func OpenAIEndpointURL(baseURL string, endpoint string) string {
if !strings.HasPrefix(normalizedEndpoint, "/") {
normalizedEndpoint = "/" + normalizedEndpoint
}
// 规则0:自定义路径模式 → 直接用 baseURL 作为完整请求地址
// 规则0:自定义路径模式
// - baseURL 已含 endpoint 后缀(/chat/completions 或 /responses)→ 直接用 base
// - 否则追加 /chat/completions(默认协议形态,覆盖 Z.AI /v4 等场景)
if normalizedEndpoint == modelchannel.OpenAIEndpointCustom {
if OpenAIEndpointFromBaseURL(base) != "" {
return base
}
return base + "/chat/completions"
}
// 规则1baseURL 已含 endpoint 后缀 → 直接用 base
if OpenAIEndpointFromBaseURL(base) != "" {
return base
}
// 规则2通用版本段去重/v1 /v2 /v3 /v4 ... 任意版本号
if version, ok := trailingVersionSegment(base); ok {
prefix := "/" + version + "/"
if strings.HasPrefix(normalizedEndpoint, prefix) {
return base + strings.TrimPrefix(normalizedEndpoint, "/"+version)
// 规则2baseURL 以 /vN 结尾时,剥离 endpoint 的版本前缀/v1/、/v2/ 等
// 这样 base=.../v4 + endpoint=/v1/chat/completions → .../v4/chat/completions
if _, ok := trailingVersionSegment(base); ok {
if rest, stripped := stripEndpointVersionPrefix(normalizedEndpoint); stripped {
return base + rest
}
}
// 规则3:兜底原样拼接
@@ -342,6 +347,24 @@ func trailingVersionSegment(base string) (string, bool) {
return seg, true
}
// stripEndpointVersionPrefix 剥离 endpoint 路径开头的版本段前缀(/vN/),
// 返回剩余路径和是否成功剥离。
// /v1/chat/completions → ("/chat/completions", true)
// /chat/completions → ("", false)
func stripEndpointVersionPrefix(endpoint string) (string, bool) {
if len(endpoint) < 4 || endpoint[0] != '/' || endpoint[1] != 'v' {
return "", false
}
i := 2
for i < len(endpoint) && endpoint[i] >= '0' && endpoint[i] <= '9' {
i++
}
if i == 2 || i >= len(endpoint) || endpoint[i] != '/' {
return "", false
}
return endpoint[i:], true
}
func ResolveOpenAIEndpoint(baseURL string, endpoint string) string {
if endpointFromURL := OpenAIEndpointFromBaseURL(baseURL); endpointFromURL != "" {
return endpointFromURL
@@ -1,79 +0,0 @@
package modeladapter
import "testing"
func TestTrailingVersionSegment(t *testing.T) {
cases := []struct {
base string
wantSeg string
wantOK bool
}{
{"https://api.openai.com/v1", "v1", true},
{"https://api.z.ai/api/coding/paas/v4", "v4", true},
{"https://example.com/v2", "v2", true},
{"https://example.com/v12", "v12", true},
// 非版本段
{"https://api.openai.com", "", false},
{"https://example.com/chat", "", false},
{"https://example.com/api", "", false},
{"https://example.com/v", "", false},
{"https://example.com/vx", "", false},
{"", "", false},
}
for _, tc := range cases {
t.Run(tc.base, func(t *testing.T) {
gotSeg, gotOK := trailingVersionSegment(tc.base)
if gotSeg != tc.wantSeg || gotOK != tc.wantOK {
t.Fatalf("trailingVersionSegment(%q) = (%q, %v), want (%q, %v)", tc.base, gotSeg, gotOK, tc.wantSeg, tc.wantOK)
}
})
}
}
func TestOpenAIEndpointURL(t *testing.T) {
cases := []struct {
name string
baseURL string
endpoint string
want string
}{
// === issue #166 核心场景:选"自定义路径"baseURL 填完整地址 ===
{"custom_zai_full_chat", "https://api.z.ai/api/coding/paas/v4/chat/completions", "/custom", "https://api.z.ai/api/coding/paas/v4/chat/completions"},
{"custom_zai_full_responses", "https://api.z.ai/api/coding/paas/v4/responses", "/custom", "https://api.z.ai/api/coding/paas/v4/responses"},
// === 存量场景回归:OpenAI 官方 /v1 ===
{"openai_v1_responses_dedup", "https://api.openai.com/v1", "/v1/responses", "https://api.openai.com/v1/responses"},
{"openai_v1_chat_dedup", "https://api.openai.com/v1", "/v1/chat/completions", "https://api.openai.com/v1/chat/completions"},
// baseURL 不带版本号
{"openai_noversion_chat", "https://api.openai.com", "/v1/chat/completions", "https://api.openai.com/v1/chat/completions"},
// === baseURL 已含完整 endpoint 后缀 → 直接用 base ===
{"baseurl_has_chat_suffix", "https://api.openai.com/v1/chat/completions", "/v1/chat/completions", "https://api.openai.com/v1/chat/completions"},
{"baseurl_has_responses_suffix", "https://api.openai.com/v1/responses", "/v1/responses", "https://api.openai.com/v1/responses"},
// === 空 endpoint 默认 responses ===
{"empty_endpoint_defaults", "https://api.openai.com/v1", "", "https://api.openai.com/v1/responses"},
// === 通用版本段去重:/v2 /v3 ===
{"v2_dedup", "https://example.com/v2", "/v2/chat/completions", "https://example.com/v2/chat/completions"},
{"v3_dedup", "https://example.com/v3", "/v3/responses", "https://example.com/v3/responses"},
// === 不同版本号不去重:base /v4 + endpoint /v1 ===
{"different_version_no_dedup", "https://example.com/v4", "/v1/chat/completions", "https://example.com/v4/v1/chat/completions"},
// === 尾部斜杠清理 ===
{"trailing_slash_base", "https://api.openai.com/v1/", "/v1/responses", "https://api.openai.com/v1/responses"},
// === /custom + baseURL 不含已知后缀 → 直接返回 base ===
{"custom_no_suffix", "https://api.example.com/some/path", "/custom", "https://api.example.com/some/path"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := OpenAIEndpointURL(tc.baseURL, tc.endpoint)
if got != tc.want {
t.Fatalf("OpenAIEndpointURL(%q, %q)\n got %s\n want %s", tc.baseURL, tc.endpoint, got, tc.want)
}
})
}
}
-70
View File
@@ -1,70 +0,0 @@
package modelchannel
import "testing"
func TestNormalizeOpenAIEndpoint(t *testing.T) {
cases := []struct {
name string
providerType string
endpoint string
want string
}{
// 非 openai 类型始终返回空
{"anthropic_ignored", "anthropic", "/v1/responses", ""},
{"empty_type_ignored", "", "/v1/responses", ""},
// 预设值
{"empty_defaults_to_responses", "openai", "", OpenAIEndpointResponses},
{"preset_responses", "openai", "/v1/responses", OpenAIEndpointResponses},
{"preset_chat_completions", "openai", "/v1/chat/completions", OpenAIEndpointChatCompletions},
{"preset_custom", "openai", "/custom", OpenAIEndpointCustom},
// 非法值:不再允许任意自定义路径,只接受三个预设值
{"arbitrary_path_rejected", "openai", "/v4/chat/completions", ""},
{"arbitrary_path_rejected_2", "openai", "/chat/completions", ""},
{"missing_slash", "openai", "v1/responses", ""},
{"slash_only", "openai", "/", ""},
{"whitespace", "openai", " ", OpenAIEndpointResponses},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := NormalizeOpenAIEndpoint(tc.providerType, tc.endpoint)
if got != tc.want {
t.Fatalf("NormalizeOpenAIEndpoint(%q, %q) = %q, want %q", tc.providerType, tc.endpoint, got, tc.want)
}
})
}
}
func TestOpenAIEndpointShape(t *testing.T) {
cases := []struct {
endpoint string
want string
}{
// Responses 形态
{"/v1/responses", "responses"},
{"/v4/responses", "responses"},
{"/responses", "responses"},
// Chat Completions 形态
{"/v1/chat/completions", "chat/completions"},
{"/v4/chat/completions", "chat/completions"},
{"/chat/completions", "chat/completions"},
// /custom 不含已知后缀 → 兜底走 chat/completions
{"/custom", "chat/completions"},
// 兜底默认
{"", "chat/completions"},
}
for _, tc := range cases {
t.Run(tc.endpoint, func(t *testing.T) {
got := OpenAIEndpointShape(tc.endpoint)
if got != tc.want {
t.Fatalf("OpenAIEndpointShape(%q) = %q, want %q", tc.endpoint, got, tc.want)
}
})
}
}