mirror of
https://github.com/AstrBotDevs/AstrBot.git
synced 2026-10-16 15:06:04 +08:00
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:
@@ -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:
|
||||
"""关闭客户端会话"""
|
||||
|
||||
@@ -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"])
|
||||
Reference in New Issue
Block a user