fix: support both Bailian Rerank API formats based on URL endpoint#7250
fix: support both Bailian Rerank API formats based on URL endpoint#7250Neko-Yukari wants to merge 4 commits intoAstrBotDevs:masterfrom
Conversation
阿里云百炼有两个不同的 rerank API 端点:
- /compatible-api/v1/reranks: 使用扁平请求格式 {model, query, documents}
- /api/v1/services/rerank/...: 需要 input 包装 {model, input: {...}}
之前代码只根据模型名判断格式,导致 qwen3-rerank + compatible-api 组合失败。
修复内容:
- _build_payload(): 根据 URL 是否含 'compatible-api' 决定请求格式
- _parse_results(): 根据 URL 判断响应中 results 的位置
Fixes AstrBotDevs#7161
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- Using
"compatible-api" in self.base_urlto switch request/response formats is brittle; consider parsing the URL path or matching against explicit known endpoints to avoid false positives or future URL changes breaking the behavior. - The construction of the
paramsdictionary in_build_payloadis duplicated in both branches; factor it out once before theif is_compatible_apito reduce repetition and keep the behavior consistent. - In
_parse_results, the compatible API path treats any presence ofcodeas an error; if the API can return acodefield on success (e.g.,"200"), you may want to align the condition with the non-compatible branch and only raise whencode != "200"or per the official spec.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Using `"compatible-api" in self.base_url` to switch request/response formats is brittle; consider parsing the URL path or matching against explicit known endpoints to avoid false positives or future URL changes breaking the behavior.
- The construction of the `params` dictionary in `_build_payload` is duplicated in both branches; factor it out once before the `if is_compatible_api` to reduce repetition and keep the behavior consistent.
- In `_parse_results`, the compatible API path treats any presence of `code` as an error; if the API can return a `code` field on success (e.g., `"200"`), you may want to align the condition with the non-compatible branch and only raise when `code != "200"` or per the official spec.
## Individual Comments
### Comment 1
<location path="astrbot/core/provider/sources/bailian_rerank_source.py" line_range="91" />
<code_context>
- # qwen3-rerank follows a model-specific payload:
- # query/documents/top_n/instruct should be at the top level.
- if normalized_model == self.QWEN3_RERANK_MODEL:
+ is_compatible_api = "compatible-api" in self.base_url
+ if normalized_model == self.QWEN3_RERANK_MODEL and is_compatible_api:
payload = {
</code_context>
<issue_to_address>
**issue (complexity):** Consider centralizing the compatibility-specific logic in `_build_payload` and `_parse_results` so shared behavior (params building, error handling, and result extraction) is implemented once instead of duplicated across branches.
You can simplify both `_build_payload` and `_parse_results` without changing behavior by centralizing the compatibility differences.
### `_build_payload`
The `params` dict is built twice with identical logic, and the only structural difference is where `query`/`documents` live. You can compute `params` once and make the `base` shape conditional:
```python
def _build_payload(self, query: str, documents: list[str], top_n: int | None = None) -> dict:
normalized_model = self.model.strip().lower()
normalized_top_n = top_n if top_n is not None and top_n > 0 else None
is_compatible_api = "compatible-api" in self.base_url
# qwen3-rerank follows a model-specific payload when using the compatible API
if normalized_model == self.QWEN3_RERANK_MODEL and is_compatible_api:
payload = {
"model": self.model,
"query": query,
"documents": documents,
}
if normalized_top_n is not None:
payload["top_n"] = normalized_top_n
if self.instruct:
payload["instruct"] = self.instruct
if self.return_documents:
logger.warning(
"qwen3-rerank does not support return_documents; "
"this option will be ignored."
)
return payload
payload_input = {"query": query, "documents": documents}
base: dict[str, Any] = {"model": self.model}
if is_compatible_api:
base.update(payload_input) # top-level query/documents
else:
base["input"] = payload_input # nested input
params = {
k: v
for k, v in [
("top_n", normalized_top_n),
("return_documents", True if self.return_documents else None),
]
if v is not None
}
if params:
base["parameters"] = params
return base
```
This keeps all semantics while removing the duplicated `params` block and branching.
### `_parse_results`
You can normalize the two response shapes into local variables first, then have a single error‑handling and extraction path:
```python
def _parse_results(self, data: dict) -> list[RerankResult]:
is_compatible_api = "compatible-api" in self.base_url
if is_compatible_api:
code = data.get("code") # None on success, set on error
message = data.get("message", "")
results = data.get("results", [])
has_error = bool(code)
else:
code = data.get("code", "200") # "200" on success
message = data.get("message", "")
results = data.get("output", {}).get("results", [])
has_error = code != "200"
if has_error:
raise BailianAPIError(f"百炼 API 错误: {code} – {message}")
# 转换为RerankResult对象,使用.get()避免KeyError
rerank_results: list[RerankResult] = []
for idx, result in enumerate(results):
try:
index = result.get("index", idx)
relevance_score = result.get("relevance_score", 0.0)
if relevance_score is None:
logger.warning(f"结果 {idx} 缺少 relevance_score,使用默认值 0.0")
relevance_score = 0.0
rerank_results.append(
RerankResult(
index=index,
relevance_score=relevance_score,
document=result.get("document"),
)
)
except Exception as e:
logger.error(f"解析结果 {idx} 时发生错误: {e},原始结果: {result}")
return rerank_results
```
This keeps the compatibility-specific semantics (different success conditions and response shapes) but centralizes error handling and result extraction, reducing branching and making future changes safer.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request introduces support for a 'compatible-api' version of the Bailian rerank source by conditionally adjusting the payload structure and response parsing based on the base URL. The review identifies opportunities to reduce code duplication in the _build_payload and _parse_results methods by extracting shared logic for parameter construction and error handling.
There was a problem hiding this comment.
Pull request overview
This PR fixes Bailian (阿里云百炼) rerank integration by selecting request/response formats based on which rerank API endpoint is configured, addressing incompatibilities between the compatible-api endpoint and the legacy service endpoint.
Changes:
- Update
_build_payload()to choose between flat vsinput-wrapped request bodies based on whetherrerank_api_basecontainscompatible-api. - Update
_parse_results()to read rerank results from eitherresults(compatible endpoint) oroutput.results(legacy endpoint). - Adjust error handling logic to differ between compatible and legacy endpoints.
Comments suppressed due to low confidence (1)
astrbot/core/provider/sources/bailian_rerank_source.py:139
- For the
compatible-apiendpoint branch,top_n/return_documentsare currently placed underparameters(viabase["parameters"] = params). The PR description indicates the compatible endpoint expects a flat payload, so these options should likely be top-level fields (as already done in the qwen3-specific branch) rather than nested underparameters, otherwise non-qwen3 models on the compatible endpoint may receive an incompatible request body.
if params:
base["parameters"] = params
…ndling - Extract params building outside the if-else branch - Add back empty results warning log - Simplify error handling variable assignment
回复评论感谢各位的代码审查! 已采纳的修改
未采纳的问题关于
关于 compatible-api 上的 parameters 问题:Copilot 提到 non-qwen3 模型在 compatible-api 上 测试验证两种端点格式均已验证通过: |
关联 PR #7217本 PR 与 he-yufeng 的 PR #7217 互补:
为什么需要本 PR即使合并 PR #7217,使用
评论指出的
|
qwen3-rerank always uses flat format regardless of API endpoint. Other models (gte-rerank-v2, etc.) use input wrapper format. This simplifies the logic and correctly handles all model/URL combinations. Tested: qwen3-rerank accepts both formats, gte-rerank-v2 only supports input wrapper.
Summary
阿里云百炼 rerank provider 存在两个 API 端点格式不兼容的问题。
Bug 触发方式:
rerank_api_base为https://dashscope.aliyuncs.com/compatible-api/v1/reranksqwen3-rerank根因:阿里云百炼有两个不同的 rerank API 端点,请求/响应格式不同:
/compatible-api/v1/reranks{model, query, documents}results在根级别/api/v1/services/rerank/...input包装{model, input: {...}}output.results代码之前只根据模型名判断格式,导致 qwen3-rerank + compatible-api 组合失败。
相关 PR:PR #7217 已修复响应解析,但请求格式仍有问题。
Changes
_build_payload(): 根据 URL 是否含compatible-api决定请求格式_parse_results(): 根据 URL 判断响应中 results 的位置Test
https://dashscope.aliyuncs.com/compatible-api/v1/reranksFixes #7161
Summary by Sourcery
Support both Bailian Rerank API formats by switching request and response handling based on the configured endpoint URL.
Bug Fixes:
Enhancements: