完善文档页文本向量与重排序调用支持

This commit is contained in:
2026-05-31 21:18:41 +08:00
parent 8ee7a7969e
commit 644a6f9d17
24 changed files with 1945 additions and 71 deletions
@@ -31,6 +31,33 @@ func TestPlanTaskResponseTreatsAPIV1ChatCompletionsAsSynchronousCompatibleRespon
}
}
func TestPlanTaskResponseTreatsAPIV1EmbeddingAndRerankAsSynchronousCompatibleResponse(t *testing.T) {
for _, item := range []struct {
kind string
path string
}{
{kind: "embeddings", path: "/api/v1/embeddings"},
{kind: "reranks", path: "/api/v1/reranks"},
} {
t.Run(item.kind, func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, item.path, nil)
req.Header.Set("X-Async", "true")
plan := planTaskResponse(item.kind, false, map[string]any{"stream": true}, req)
if plan.asyncMode {
t.Fatalf("%s must not enter async task mode", item.path)
}
if !plan.compatibleMode {
t.Fatalf("%s should return compatible response payloads", item.path)
}
if plan.streamMode {
t.Fatal("embedding and rerank endpoints should stay JSON-only even when stream=true is present")
}
})
}
}
func TestPlanTaskResponseKeepsAsyncTaskModeForOtherAPIV1Tasks(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/v1/images/generations", nil)
req.Header.Set("X-Async", "true")
+26 -2
View File
@@ -876,6 +876,8 @@ func (s *Server) listModelRateLimitStatuses(w http.ResponseWriter, r *http.Reque
// @Failure 429 {object} ErrorEnvelope
// @Failure 502 {object} ErrorEnvelope
// @Router /api/v1/responses [post]
// @Router /api/v1/embeddings [post]
// @Router /api/v1/reranks [post]
// @Router /api/v1/images/generations [post]
// @Router /api/v1/images/edits [post]
// @Router /api/v1/videos/generations [post]
@@ -883,6 +885,10 @@ func (s *Server) listModelRateLimitStatuses(w http.ResponseWriter, r *http.Reque
// @Router /v1/chat/completions [post]
// @Router /responses [post]
// @Router /v1/responses [post]
// @Router /embeddings [post]
// @Router /v1/embeddings [post]
// @Router /reranks [post]
// @Router /v1/reranks [post]
// @Router /images/generations [post]
// @Router /v1/images/generations [post]
// @Router /images/edits [post]
@@ -1085,17 +1091,25 @@ type taskResponsePlan struct {
func planTaskResponse(kind string, compatible bool, body map[string]any, r *http.Request) taskResponsePlan {
asyncMode := asyncRequest(r)
compatibleMode := compatible
if kind == "chat.completions" && !compatible {
if synchronousCompatibleKind(kind) && !compatible {
asyncMode = false
compatibleMode = true
}
return taskResponsePlan{
asyncMode: asyncMode,
compatibleMode: compatibleMode,
streamMode: boolValue(body, "stream"),
streamMode: streamCompatibleKind(kind) && boolValue(body, "stream"),
}
}
func synchronousCompatibleKind(kind string) bool {
return kind == "chat.completions" || kind == "embeddings" || kind == "reranks"
}
func streamCompatibleKind(kind string) bool {
return kind == "chat.completions" || kind == "responses"
}
func writeTaskAccepted(w http.ResponseWriter, task store.GatewayTask) {
writeJSON(w, http.StatusAccepted, map[string]any{
"taskId": task.ID,
@@ -1120,6 +1134,12 @@ func apiKeyScopeAllowed(user *auth.User, kind string) bool {
if required == "chat" && (scope == "text" || scope == "text_generate") {
return true
}
if required == "embedding" && scope == "text_embedding" {
return true
}
if required == "rerank" && scope == "text_rerank" {
return true
}
}
return false
}
@@ -1128,6 +1148,10 @@ func scopeForTaskKind(kind string) string {
switch kind {
case "chat.completions", "responses":
return "chat"
case "embeddings":
return "embedding"
case "reranks":
return "rerank"
case "images.generations", "images.edits":
return "image"
case "videos.generations":
@@ -1025,6 +1025,7 @@ func modelCatalogCapabilityDefinitions() []ModelCatalogFilterOption {
{Value: "text_to_speech", Label: "语音合成"},
{Value: "audio_understanding", Label: "音频理解"},
{Value: "text_embedding", Label: "Embedding"},
{Value: "text_rerank", Label: "重排序"},
{Value: "omni", Label: "全模态"},
{Value: "omni_video", Label: "全模态视频"},
{Value: "multimodal", Label: "多模态"},
@@ -1120,6 +1121,8 @@ func canonicalCapabilityFilterValue(value string) string {
switch normalized {
case "embedding":
return "text_embedding"
case "rerank", "reranks":
return "text_rerank"
case "model":
return "model_3d"
default:
@@ -1143,6 +1146,8 @@ func capabilityFilterValueForTag(tag string) string {
return "structured_output"
case "数字人":
return "digital_human"
case "重排序":
return "text_rerank"
case "3D 模型":
return "model_3d"
case "文生 3D":
@@ -1165,6 +1170,9 @@ func capabilityLabel(value string) string {
"responses": "Responses",
"text_embedding": "Embedding",
"embedding": "Embedding",
"text_rerank": "重排序",
"rerank": "重排序",
"reranks": "重排序",
"image_generate": "图像生成",
"image_edit": "图像编辑",
"image_analysis": "图像分析",
+6
View File
@@ -128,6 +128,8 @@ func NewServerWithContext(ctx context.Context, cfg config.Config, db *store.Stor
mux.Handle("GET /api/admin/runtime/model-rate-limits", server.requireAdmin(auth.PermissionPower, http.HandlerFunc(server.listModelRateLimitStatuses)))
mux.Handle("POST /api/v1/chat/completions", server.auth.Require(auth.PermissionBasic, server.createAPIV1ChatCompletions()))
mux.Handle("POST /api/v1/responses", server.auth.Require(auth.PermissionBasic, server.createTask("responses", false)))
mux.Handle("POST /api/v1/embeddings", server.auth.Require(auth.PermissionBasic, server.createTask("embeddings", false)))
mux.Handle("POST /api/v1/reranks", server.auth.Require(auth.PermissionBasic, server.createTask("reranks", false)))
mux.Handle("POST /api/v1/images/generations", server.auth.Require(auth.PermissionBasic, server.createTask("images.generations", false)))
mux.Handle("POST /api/v1/images/edits", server.auth.Require(auth.PermissionBasic, server.createTask("images.edits", false)))
mux.Handle("POST /api/v1/videos/generations", server.auth.Require(auth.PermissionBasic, server.createTask("videos.generations", false)))
@@ -140,6 +142,10 @@ func NewServerWithContext(ctx context.Context, cfg config.Config, db *store.Stor
mux.Handle("POST /v1/chat/completions", server.auth.Require(auth.PermissionBasic, server.createTask("chat.completions", true)))
mux.Handle("POST /responses", server.auth.Require(auth.PermissionBasic, server.createTask("responses", true)))
mux.Handle("POST /v1/responses", server.auth.Require(auth.PermissionBasic, server.createTask("responses", true)))
mux.Handle("POST /embeddings", server.auth.Require(auth.PermissionBasic, server.createTask("embeddings", true)))
mux.Handle("POST /v1/embeddings", server.auth.Require(auth.PermissionBasic, server.createTask("embeddings", true)))
mux.Handle("POST /reranks", server.auth.Require(auth.PermissionBasic, server.createTask("reranks", true)))
mux.Handle("POST /v1/reranks", server.auth.Require(auth.PermissionBasic, server.createTask("reranks", true)))
mux.Handle("POST /images/generations", server.auth.Require(auth.PermissionBasic, server.createTask("images.generations", true)))
mux.Handle("POST /v1/images/generations", server.auth.Require(auth.PermissionBasic, server.createTask("images.generations", true)))
mux.Handle("POST /images/edits", server.auth.Require(auth.PermissionBasic, server.createTask("images.edits", true)))