fix: propagate Xinference rerank failures instead of returning empty results (#10051)

The Xinference rerank provider swallowed upstream failures and an
uninitialized model by returning an empty list, which the retrieval
manager read as a legitimate empty rerank and used to overwrite the
fused candidates with nothing. Other providers (e.g. TEI) raise, and
the manager only preserves the fused results on that path, so a
transient Xinference outage silently degraded every knowledge-base
query to zero hits. Raise instead, matching the provider contract and
letting the manager's degradation keep the unreranked results.

Fixes #10000
This commit is contained in:
Yufeng He
2026-09-19 12:40:42 +08:00
committed by GitHub
parent 8492df45e0
commit 081261664c
2 changed files with 43 additions and 3 deletions
@@ -91,8 +91,7 @@ class XinferenceRerankProvider(RerankProvider):
top_n: int | None = None,
) -> list[RerankResult]:
if not self.model:
logger.error("Xinference rerank model is not initialized.")
return []
raise RuntimeError("Xinference rerank model is not initialized")
try:
response = await self.model.rerank(documents, query, top_n)
results = response.get("results", [])
@@ -111,9 +110,12 @@ class XinferenceRerankProvider(RerankProvider):
for result in results
]
except Exception as e:
# An empty list reads as a legitimate empty rerank to the caller,
# which would overwrite the fused candidates with nothing (#10000).
# Propagate so the retrieval manager keeps the unreranked results.
logger.error(f"Xinference rerank failed: {e}")
logger.debug(f"Xinference rerank failed with exception: {e}", exc_info=True)
return []
raise
async def terminate(self) -> None:
"""关闭客户端会话"""
+38
View File
@@ -0,0 +1,38 @@
"""Regression tests for the Xinference rerank provider failure contract (#10000).
The provider used to swallow upstream failures and return an empty list,
which the retrieval manager read as a legitimate empty rerank result and
used to overwrite the fused candidates. Failures now propagate so the
manager can fall back to the unreranked results.
"""
from unittest.mock import AsyncMock, Mock
import pytest
from astrbot.core.provider.sources.xinference_rerank_source import (
XinferenceRerankProvider,
)
@pytest.fixture
def provider() -> XinferenceRerankProvider:
instance = XinferenceRerankProvider.__new__(XinferenceRerankProvider)
instance.model = Mock()
return instance
@pytest.mark.asyncio
async def test_rerank_failure_propagates_instead_of_empty_list(provider):
provider.model.rerank = AsyncMock(side_effect=RuntimeError("upstream down"))
with pytest.raises(RuntimeError, match="upstream down"):
await provider.rerank(query="q", documents=["a", "b"])
@pytest.mark.asyncio
async def test_uninitialized_model_raises_instead_of_empty_list(provider):
provider.model = None
with pytest.raises(RuntimeError, match="not initialized"):
await provider.rerank(query="q", documents=["a"])