feat(ai-proxy): support Qwen reranks and conversations paths (#3724)

This commit is contained in:
woody
2026-04-16 15:52:58 +08:00
committed by GitHub
parent 26e17c48eb
commit 94b40aab9a
5 changed files with 185 additions and 3 deletions

View File

@@ -366,6 +366,29 @@ func RunQwenOnHttpRequestHeadersTests(t *testing.T) {
require.Contains(t, authValue, "sk-qwen-test123456789", "Authorization should contain qwen API token")
})
// 测试qwen请求头处理reranks接口
t.Run("qwen reranks request headers", func(t *testing.T) {
host, status := test.NewTestHost(basicQwenConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
action := host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/reranks"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
require.Equal(t, types.HeaderStopIteration, action)
requestHeaders := host.GetRequestHeaders()
require.NotNil(t, requestHeaders)
pathValue, hasPath := test.GetHeaderValue(requestHeaders, ":path")
require.True(t, hasPath)
require.Contains(t, pathValue, "/api/v1/services/rerank/text-rerank/text-rerank", "Path should be converted to qwen rerank path")
})
// 测试qwen自定义域名请求头处理
t.Run("qwen custom domain request headers", func(t *testing.T) {
host, status := test.NewTestHost(qwenCustomDomainConfig)
@@ -451,6 +474,52 @@ func RunQwenOnHttpRequestHeadersTests(t *testing.T) {
require.True(t, hasPath)
require.Contains(t, pathValue, "/compatible-mode/v1/responses", "Path should use compatible mode responses path")
})
// 测试qwen兼容模式请求头处理reranks接口
t.Run("qwen compatible mode reranks request headers", func(t *testing.T) {
host, status := test.NewTestHost(qwenEnableCompatibleConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
action := host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/reranks"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
require.Equal(t, types.HeaderStopIteration, action)
requestHeaders := host.GetRequestHeaders()
require.NotNil(t, requestHeaders)
pathValue, hasPath := test.GetHeaderValue(requestHeaders, ":path")
require.True(t, hasPath)
require.Contains(t, pathValue, "/compatible-api/v1/reranks", "Path should use compatible API reranks path")
})
// 测试qwen兼容模式请求头处理conversations接口
t.Run("qwen compatible mode conversations request headers", func(t *testing.T) {
host, status := test.NewTestHost(qwenEnableCompatibleConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
action := host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/conversations"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
require.Equal(t, types.HeaderStopIteration, action)
requestHeaders := host.GetRequestHeaders()
require.NotNil(t, requestHeaders)
pathValue, hasPath := test.GetHeaderValue(requestHeaders, ":path")
require.True(t, hasPath)
require.Contains(t, pathValue, "/compatible-mode/v1/conversations", "Path should use compatible mode conversations path")
})
})
}
@@ -583,6 +652,35 @@ func RunQwenOnHttpRequestBodyTests(t *testing.T) {
require.True(t, hasFileLogs, "Should have file processing logs")
})
// 测试qwen请求体处理reranks接口
t.Run("qwen reranks request body", func(t *testing.T) {
host, status := test.NewTestHost(qwenMultiModelConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/reranks"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
requestBody := `{
"model":"qwen3-rerank",
"documents":["doc1","doc2"],
"query":"test query",
"top_n":1
}`
action := host.CallOnHttpRequestBody([]byte(requestBody))
require.Equal(t, types.ActionContinue, action)
processedBody := host.GetRequestBody()
require.NotNil(t, processedBody)
require.Contains(t, string(processedBody), "qwen3-rerank", "Reranks request model should be preserved")
require.Contains(t, string(processedBody), "documents", "Reranks request documents should be preserved")
})
// 测试qwen请求体处理qwen-vl模型多模态
t.Run("qwen qwen-vl model multimodal request body", func(t *testing.T) {
host, status := test.NewTestHost(qwenMultiModelConfig)
@@ -723,6 +821,63 @@ func RunQwenOnHttpRequestBodyTests(t *testing.T) {
require.Contains(t, string(processedBody), "qwen-turbo", "Model name should be preserved in responses request")
})
// 测试qwen请求体处理兼容模式 reranks接口
t.Run("qwen compatible mode reranks request body", func(t *testing.T) {
host, status := test.NewTestHost(qwenEnableCompatibleConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/reranks"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
requestBody := `{
"model":"qwen3-rerank",
"documents":["doc1","doc2"],
"query":"test query",
"top_n":1
}`
action := host.CallOnHttpRequestBody([]byte(requestBody))
require.Equal(t, types.ActionContinue, action)
processedBody := host.GetRequestBody()
require.NotNil(t, processedBody)
require.Contains(t, string(processedBody), "qwen-turbo", "Reranks request model should be mapped by wildcard")
require.Contains(t, string(processedBody), "documents", "Reranks request documents should be preserved")
})
// 测试qwen请求体处理兼容模式 conversations接口
t.Run("qwen compatible mode conversations request body", func(t *testing.T) {
host, status := test.NewTestHost(qwenEnableCompatibleConfig)
defer host.Reset()
require.Equal(t, types.OnPluginStartStatusOK, status)
host.CallOnHttpRequestHeaders([][2]string{
{":authority", "example.com"},
{":path", "/v1/conversations"},
{":method", "POST"},
{"Content-Type", "application/json"},
})
requestBody := `{
"metadata":{"topic":"demo"},
"items":[{"type":"message","role":"system","content":"test content"}]
}`
action := host.CallOnHttpRequestBody([]byte(requestBody))
require.Equal(t, types.ActionContinue, action)
processedBody := host.GetRequestBody()
require.NotNil(t, processedBody)
require.Contains(t, string(processedBody), "\"metadata\"", "Conversations metadata should be preserved")
require.Contains(t, string(processedBody), "\"items\"", "Conversations items should be preserved")
require.NotContains(t, string(processedBody), "\"model\":", "Conversations request should not inject model field")
})
// 测试qwen请求体处理非兼容模式 responses接口应报不支持
t.Run("qwen non-compatible mode responses request body unsupported", func(t *testing.T) {
host, status := test.NewTestHost(basicQwenConfig)
@@ -755,9 +910,11 @@ func RunQwenOnHttpRequestBodyTests(t *testing.T) {
// 覆盖 qwen.GetApiName 中以下分支:
// - qwenCompatibleTextEmbeddingPath => ApiNameEmbeddings
// - qwenCompatibleResponsesPath => ApiNameResponses
// - qwenCompatibleTextRerankPath => ApiNameQwenV1Rerank
// - qwenCompatibleConversationsPath => ApiNameQwenV1Conversations
// - qwenAsyncAIGCPath => ApiNameQwenAsyncAIGC
// - qwenAsyncTaskPath => ApiNameQwenAsyncTask
t.Run("qwen original protocol get api name coverage for compatible embeddings responses and async paths", func(t *testing.T) {
t.Run("qwen original protocol get api name coverage for compatible paths and async paths", func(t *testing.T) {
cases := []struct {
name string
path string
@@ -770,6 +927,14 @@ func RunQwenOnHttpRequestBodyTests(t *testing.T) {
name: "compatible responses path",
path: "/compatible-mode/v1/responses",
},
{
name: "compatible reranks path",
path: "/compatible-api/v1/reranks",
},
{
name: "compatible conversations path",
path: "/compatible-mode/v1/conversations",
},
{
name: "async aigc path",
path: "/api/v1/services/aigc/custom-async-endpoint",