From 2ad854c586354ea2f4c5d3f8c24439a3f8e92abd Mon Sep 17 00:00:00 2001 From: Joseff Date: Fri, 8 May 2026 08:21:04 -0400 Subject: [PATCH] Go: implement Rerank in Aliyun driver (#14676) ### What problem does this PR solve? The Aliyun Go driver has a stub `Rerank` method that always returns `"Aliyun, Rerank not implemented"`. DashScope exposes an OpenAI-compatible rerank endpoint (`compatible-mode/v1/rerank`) and hosts dedicated bilingual rerankers (`gte-rerank-v2`, `gte-rerank`) that are a natural pairing with the embedding models already in `aliyun.json`. Without this, Aliyun users cannot use reranking within RAGFlow. Closes #14675 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- conf/models/aliyun.json | 8 +++ internal/entity/models/aliyun.go | 103 ++++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/conf/models/aliyun.json b/conf/models/aliyun.json index 32a5221da4..51adef5d74 100644 --- a/conf/models/aliyun.json +++ b/conf/models/aliyun.json @@ -8,6 +8,7 @@ "url_suffix": { "chat": "compatible-mode/v1/chat/completions", "embedding": "compatible-mode/v1/embeddings", + "rerank": "compatible-api/v1/reranks", "models": "api/v1/deployments/models" }, "models": [ @@ -31,6 +32,13 @@ "model_types": [ "embedding" ] + }, + { + "name": "qwen3-rerank", + "max_tokens": 8192, + "model_types": [ + "rerank" + ] } ], "features": { diff --git a/internal/entity/models/aliyun.go b/internal/entity/models/aliyun.go index c1cd7d5564..1778fc1960 100644 --- a/internal/entity/models/aliyun.go +++ b/internal/entity/models/aliyun.go @@ -458,9 +458,108 @@ func (z *AliyunModel) Encode(modelName *string, texts []string, apiConfig *APICo return embeddings, nil } -// Rerank calculates similarity scores between query and texts +type aliyunRerankRequest struct { + Model string `json:"model"` + Query string `json:"query"` + Documents []string `json:"documents"` + TopN int `json:"top_n"` + ReturnDocuments bool `json:"return_documents"` +} + +type aliyunRerankResponse struct { + Results []struct { + Index int `json:"index"` + RelevanceScore float64 `json:"relevance_score"` + } `json:"results"` +} + func (z *AliyunModel) Rerank(modelName *string, query string, texts []string, apiConfig *APIConfig) ([]float64, error) { - return nil, fmt.Errorf("%s, Rerank not implemented", z.Name()) + if len(texts) == 0 { + return []float64{}, nil + } + if apiConfig == nil || apiConfig.ApiKey == nil || *apiConfig.ApiKey == "" { + return nil, fmt.Errorf("api key is required") + } + if modelName == nil || *modelName == "" { + return nil, fmt.Errorf("model name is required") + } + + region := "default" + if apiConfig.Region != nil && *apiConfig.Region != "" { + region = *apiConfig.Region + } + + baseURL := z.BaseURL["default"] + if region != "default" { + if regional, ok := z.BaseURL[region]; ok && regional != "" { + baseURL = regional + } + } + if baseURL == "" { + return nil, fmt.Errorf("aliyun: no base URL configured for default region") + } + + url := fmt.Sprintf("%s/%s", strings.TrimSuffix(baseURL, "/"), z.URLSuffix.Rerank) + + reqBody := aliyunRerankRequest{ + Model: *modelName, + Query: query, + Documents: texts, + TopN: len(texts), + ReturnDocuments: false, + } + + jsonData, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiConfig.ApiKey)) + + resp, err := z.httpClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to send request: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Aliyun rerank API error: %s, body: %s", resp.Status, string(body)) + } + + var rerankResp aliyunRerankResponse + if err = json.Unmarshal(body, &rerankResp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + scores := make([]float64, len(texts)) + seen := make([]bool, len(texts)) + for _, r := range rerankResp.Results { + if r.Index < 0 || r.Index >= len(texts) { + return nil, fmt.Errorf("aliyun rerank: result index %d out of range for %d documents", r.Index, len(texts)) + } + if seen[r.Index] { + return nil, fmt.Errorf("aliyun rerank: duplicate result index %d", r.Index) + } + scores[r.Index] = r.RelevanceScore + seen[r.Index] = true + } + + if len(rerankResp.Results) != len(texts) { + return nil, fmt.Errorf("aliyun rerank: expected %d results, got %d", len(texts), len(rerankResp.Results)) + } + + return scores, nil } type AliyunModelItem struct {