Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36b7a4c0e4 | ||
|
|
5085047eb7 |
@@ -247,6 +247,11 @@ ENV_RERANKER_ZEROENTROPY_API_KEY = "HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY"
|
||||
ENV_RERANKER_ZEROENTROPY_MODEL = "HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL"
|
||||
ENV_RERANKER_ZEROENTROPY_BASE_URL = "HINDSIGHT_API_RERANKER_ZEROENTROPY_BASE_URL"
|
||||
|
||||
# SiliconFlow configuration (reranker only; Cohere-compatible /rerank endpoint)
|
||||
ENV_RERANKER_SILICONFLOW_API_KEY = "HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY"
|
||||
ENV_RERANKER_SILICONFLOW_MODEL = "HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL"
|
||||
ENV_RERANKER_SILICONFLOW_BASE_URL = "HINDSIGHT_API_RERANKER_SILICONFLOW_BASE_URL"
|
||||
|
||||
# Google Discovery Engine reranker configuration
|
||||
ENV_RERANKER_GOOGLE_MODEL = "HINDSIGHT_API_RERANKER_GOOGLE_MODEL"
|
||||
ENV_RERANKER_GOOGLE_PROJECT_ID = "HINDSIGHT_API_RERANKER_GOOGLE_PROJECT_ID"
|
||||
@@ -474,6 +479,9 @@ DEFAULT_RERANKER_OPENROUTER_MODEL = "cohere/rerank-v3.5"
|
||||
|
||||
DEFAULT_RERANKER_ZEROENTROPY_MODEL = "zerank-2"
|
||||
|
||||
DEFAULT_RERANKER_SILICONFLOW_MODEL = "BAAI/bge-reranker-v2-m3"
|
||||
DEFAULT_RERANKER_SILICONFLOW_BASE_URL = "https://api.siliconflow.cn/v1"
|
||||
|
||||
DEFAULT_RERANKER_GOOGLE_MODEL = "semantic-ranker-default-004"
|
||||
|
||||
# Vector extension (pgvector, vchord, or pgvectorscale)
|
||||
@@ -828,6 +836,9 @@ class HindsightConfig:
|
||||
reranker_zeroentropy_api_key: str | None
|
||||
reranker_zeroentropy_model: str
|
||||
reranker_zeroentropy_base_url: str | None
|
||||
reranker_siliconflow_api_key: str | None
|
||||
reranker_siliconflow_model: str
|
||||
reranker_siliconflow_base_url: str
|
||||
reranker_google_model: str
|
||||
reranker_google_project_id: str | None
|
||||
reranker_google_service_account_key: str | None
|
||||
@@ -984,6 +995,7 @@ class HindsightConfig:
|
||||
"reranker_tei_base_url",
|
||||
"reranker_cohere_base_url",
|
||||
"reranker_zeroentropy_base_url",
|
||||
"reranker_siliconflow_base_url",
|
||||
# Service Account Keys
|
||||
"llm_vertexai_service_account_key",
|
||||
"embeddings_vertexai_service_account_key",
|
||||
@@ -1352,6 +1364,12 @@ class HindsightConfig:
|
||||
reranker_zeroentropy_api_key=os.getenv(ENV_RERANKER_ZEROENTROPY_API_KEY),
|
||||
reranker_zeroentropy_model=os.getenv(ENV_RERANKER_ZEROENTROPY_MODEL, DEFAULT_RERANKER_ZEROENTROPY_MODEL),
|
||||
reranker_zeroentropy_base_url=os.getenv(ENV_RERANKER_ZEROENTROPY_BASE_URL) or None,
|
||||
# SiliconFlow reranker (Cohere-compatible /rerank endpoint)
|
||||
reranker_siliconflow_api_key=os.getenv(ENV_RERANKER_SILICONFLOW_API_KEY),
|
||||
reranker_siliconflow_model=os.getenv(ENV_RERANKER_SILICONFLOW_MODEL, DEFAULT_RERANKER_SILICONFLOW_MODEL),
|
||||
reranker_siliconflow_base_url=os.getenv(
|
||||
ENV_RERANKER_SILICONFLOW_BASE_URL, DEFAULT_RERANKER_SILICONFLOW_BASE_URL
|
||||
),
|
||||
# Google Discovery Engine reranker (with fallback to LLM Vertex AI keys)
|
||||
reranker_google_model=os.getenv(ENV_RERANKER_GOOGLE_MODEL, DEFAULT_RERANKER_GOOGLE_MODEL),
|
||||
reranker_google_project_id=os.getenv(ENV_RERANKER_GOOGLE_PROJECT_ID)
|
||||
|
||||
@@ -30,6 +30,8 @@ from ..config import (
|
||||
DEFAULT_RERANKER_LOCAL_MODEL,
|
||||
DEFAULT_RERANKER_LOCAL_TRUST_REMOTE_CODE,
|
||||
DEFAULT_RERANKER_PROVIDER,
|
||||
DEFAULT_RERANKER_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_RERANKER_SILICONFLOW_MODEL,
|
||||
DEFAULT_RERANKER_TEI_BATCH_SIZE,
|
||||
DEFAULT_RERANKER_TEI_MAX_CONCURRENT,
|
||||
DEFAULT_RERANKER_ZEROENTROPY_MODEL,
|
||||
@@ -44,6 +46,7 @@ from ..config import (
|
||||
ENV_RERANKER_LOCAL_MODEL,
|
||||
ENV_RERANKER_LOCAL_TRUST_REMOTE_CODE,
|
||||
ENV_RERANKER_PROVIDER,
|
||||
ENV_RERANKER_SILICONFLOW_API_KEY,
|
||||
ENV_RERANKER_TEI_BATCH_SIZE,
|
||||
ENV_RERANKER_TEI_MAX_CONCURRENT,
|
||||
ENV_RERANKER_TEI_URL,
|
||||
@@ -518,6 +521,84 @@ class RemoteTEICrossEncoder(CrossEncoderModel):
|
||||
return await self._predict_async(pairs)
|
||||
|
||||
|
||||
class _CohereCompatibleRerankClient:
|
||||
"""
|
||||
Internal HTTP client for Cohere-compatible /rerank endpoints.
|
||||
|
||||
Shared by all providers that speak the Cohere rerank wire format —
|
||||
{model, query, documents[, top_n]} request and
|
||||
{results: [{index, relevance_score}, ...]} response. This covers
|
||||
SiliconFlow, ZeroEntropy, Jina, Voyage, BGE self-hosted, and Cohere
|
||||
itself when reached via a custom base_url (e.g. Azure AI Foundry).
|
||||
|
||||
Not a CrossEncoderModel — providers compose it and expose their own
|
||||
provider_name / initialization logging.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
model: str,
|
||||
rerank_url: str,
|
||||
timeout: float = 60.0,
|
||||
include_top_n: bool = True,
|
||||
):
|
||||
self.api_key = api_key
|
||||
self.model = model
|
||||
self.rerank_url = rerank_url
|
||||
self.timeout = timeout
|
||||
self.include_top_n = include_top_n
|
||||
self._async_client: httpx.AsyncClient | None = None
|
||||
|
||||
async def initialize(self) -> None:
|
||||
if self._async_client is not None:
|
||||
return
|
||||
self._async_client = httpx.AsyncClient(
|
||||
timeout=self.timeout,
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
|
||||
async def predict(self, pairs: list[tuple[str, str]]) -> list[float]:
|
||||
if self._async_client is None:
|
||||
raise RuntimeError("Reranker not initialized. Call initialize() first.")
|
||||
|
||||
if not pairs:
|
||||
return []
|
||||
|
||||
query_groups: dict[str, list[tuple[int, str]]] = {}
|
||||
for idx, (query, text) in enumerate(pairs):
|
||||
query_groups.setdefault(query, []).append((idx, text))
|
||||
|
||||
all_scores = [0.0] * len(pairs)
|
||||
|
||||
for query, indexed_texts in query_groups.items():
|
||||
texts = [text for _, text in indexed_texts]
|
||||
indices = [idx for idx, _ in indexed_texts]
|
||||
|
||||
body: dict[str, object] = {
|
||||
"model": self.model,
|
||||
"query": query,
|
||||
"documents": texts,
|
||||
"return_documents": False,
|
||||
}
|
||||
if self.include_top_n:
|
||||
body["top_n"] = len(texts)
|
||||
|
||||
response = await self._async_client.post(self.rerank_url, json=body)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
for item in result.get("results", []):
|
||||
original_idx = item["index"]
|
||||
score = item["relevance_score"]
|
||||
all_scores[indices[original_idx]] = score
|
||||
|
||||
return all_scores
|
||||
|
||||
|
||||
class CohereCrossEncoder(CrossEncoderModel):
|
||||
"""
|
||||
Cohere cross-encoder implementation using the Cohere Rerank API.
|
||||
@@ -546,7 +627,20 @@ class CohereCrossEncoder(CrossEncoderModel):
|
||||
self.base_url = base_url
|
||||
self.timeout = timeout
|
||||
self._client = None
|
||||
self._httpx_client: httpx.Client | None = None
|
||||
# Used when base_url is set (Azure AI Foundry and other Cohere-compatible hosts).
|
||||
# Azure endpoints already include the full invoke path, so rerank_url == base_url
|
||||
# and top_n is omitted to match the existing Azure contract.
|
||||
self._http_client: _CohereCompatibleRerankClient | None = (
|
||||
_CohereCompatibleRerankClient(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
rerank_url=base_url,
|
||||
timeout=timeout,
|
||||
include_top_n=False,
|
||||
)
|
||||
if base_url
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def provider_name(self) -> str:
|
||||
@@ -554,23 +648,15 @@ class CohereCrossEncoder(CrossEncoderModel):
|
||||
|
||||
async def initialize(self) -> None:
|
||||
"""Initialize the Cohere client."""
|
||||
if self._client is not None or self._httpx_client is not None:
|
||||
if self._client is not None or (self._http_client and self._http_client._async_client):
|
||||
return
|
||||
|
||||
base_url_msg = f" at {self.base_url}" if self.base_url else ""
|
||||
logger.info(f"Reranker: initializing Cohere provider with model {self.model}{base_url_msg}")
|
||||
|
||||
if self.base_url:
|
||||
# For custom endpoints (Azure AI Foundry), use httpx directly to avoid SDK path appending
|
||||
# Azure endpoints already include the full path (e.g., /models/.../invoke)
|
||||
self._httpx_client = httpx.Client(
|
||||
timeout=self.timeout,
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
logger.info("Reranker: Cohere provider initialized (using httpx for custom endpoint)")
|
||||
if self._http_client is not None:
|
||||
await self._http_client.initialize()
|
||||
logger.info("Reranker: Cohere provider initialized (Cohere-compatible HTTP endpoint)")
|
||||
else:
|
||||
# For native Cohere API, use the official SDK
|
||||
try:
|
||||
@@ -591,25 +677,24 @@ class CohereCrossEncoder(CrossEncoderModel):
|
||||
Returns:
|
||||
List of relevance scores
|
||||
"""
|
||||
if self._client is None and self._httpx_client is None:
|
||||
if self._client is None and self._http_client is None:
|
||||
raise RuntimeError("Reranker not initialized. Call initialize() first.")
|
||||
|
||||
if not pairs:
|
||||
return []
|
||||
|
||||
# Run sync Cohere API calls in thread pool
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, self._predict_sync, pairs)
|
||||
if self._http_client is not None:
|
||||
return await self._http_client.predict(pairs)
|
||||
|
||||
def _predict_sync(self, pairs: list[tuple[str, str]]) -> list[float]:
|
||||
"""Synchronous predict implementation for Cohere API."""
|
||||
# Group pairs by query for efficient batching
|
||||
# Cohere rerank expects one query with multiple documents
|
||||
# Run sync Cohere SDK calls in thread pool
|
||||
loop = asyncio.get_event_loop()
|
||||
return await loop.run_in_executor(None, self._predict_sync_sdk, pairs)
|
||||
|
||||
def _predict_sync_sdk(self, pairs: list[tuple[str, str]]) -> list[float]:
|
||||
"""Synchronous predict using the native Cohere SDK."""
|
||||
query_groups: dict[str, list[tuple[int, str]]] = {}
|
||||
for idx, (query, text) in enumerate(pairs):
|
||||
if query not in query_groups:
|
||||
query_groups[query] = []
|
||||
query_groups[query].append((idx, text))
|
||||
query_groups.setdefault(query, []).append((idx, text))
|
||||
|
||||
all_scores = [0.0] * len(pairs)
|
||||
|
||||
@@ -617,40 +702,17 @@ class CohereCrossEncoder(CrossEncoderModel):
|
||||
texts = [text for _, text in indexed_texts]
|
||||
indices = [idx for idx, _ in indexed_texts]
|
||||
|
||||
if self._httpx_client:
|
||||
# Direct HTTP request for custom endpoints (Azure AI Foundry)
|
||||
response = self._httpx_client.post(
|
||||
self.base_url,
|
||||
json={
|
||||
"model": self.model,
|
||||
"query": query,
|
||||
"documents": texts,
|
||||
"return_documents": False,
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
response = self._client.rerank(
|
||||
query=query,
|
||||
documents=texts,
|
||||
model=self.model,
|
||||
return_documents=False,
|
||||
)
|
||||
|
||||
# Map scores back to original positions
|
||||
# Azure Cohere response format: {"results": [{"index": 0, "relevance_score": 0.9}, ...]}
|
||||
for item in result.get("results", []):
|
||||
original_idx = item["index"]
|
||||
score = item["relevance_score"]
|
||||
all_scores[indices[original_idx]] = score
|
||||
else:
|
||||
# Native Cohere SDK for standard API
|
||||
response = self._client.rerank(
|
||||
query=query,
|
||||
documents=texts,
|
||||
model=self.model,
|
||||
return_documents=False,
|
||||
)
|
||||
|
||||
# Map scores back to original positions
|
||||
for result in response.results:
|
||||
original_idx = result.index
|
||||
score = result.relevance_score
|
||||
all_scores[indices[original_idx]] = score
|
||||
for result in response.results:
|
||||
original_idx = result.index
|
||||
score = result.relevance_score
|
||||
all_scores[indices[original_idx]] = score
|
||||
|
||||
return all_scores
|
||||
|
||||
@@ -673,89 +735,70 @@ class ZeroEntropyCrossEncoder(CrossEncoderModel):
|
||||
base_url: str | None = None,
|
||||
timeout: float = 60.0,
|
||||
):
|
||||
"""
|
||||
Initialize ZeroEntropy cross-encoder client.
|
||||
|
||||
Args:
|
||||
api_key: ZeroEntropy API key
|
||||
model: ZeroEntropy rerank model name (default: zerank-2)
|
||||
base_url: Custom base URL for ZeroEntropy-compatible API (e.g., mock server or proxy)
|
||||
timeout: Request timeout in seconds (default: 60.0)
|
||||
"""
|
||||
self.api_key = api_key
|
||||
self.model = model
|
||||
self.base_url = base_url.rstrip("/") if base_url else self.DEFAULT_BASE_URL
|
||||
self.rerank_url = f"{self.base_url}{self.RERANK_PATH}"
|
||||
self.timeout = timeout
|
||||
self._async_client: httpx.AsyncClient | None = None
|
||||
self._client = _CohereCompatibleRerankClient(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
rerank_url=f"{self.base_url}{self.RERANK_PATH}",
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@property
|
||||
def provider_name(self) -> str:
|
||||
return "zeroentropy"
|
||||
|
||||
async def initialize(self) -> None:
|
||||
"""Initialize the async HTTP client."""
|
||||
if self._async_client is not None:
|
||||
if self._client._async_client is not None:
|
||||
return
|
||||
|
||||
logger.info(f"Reranker: initializing ZeroEntropy provider with model {self.model}")
|
||||
self._async_client = httpx.AsyncClient(
|
||||
timeout=self.timeout,
|
||||
headers={
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
await self._client.initialize()
|
||||
logger.info("Reranker: ZeroEntropy provider initialized")
|
||||
|
||||
async def predict(self, pairs: list[tuple[str, str]]) -> list[float]:
|
||||
"""
|
||||
Score query-document pairs using the ZeroEntropy Rerank API.
|
||||
return await self._client.predict(pairs)
|
||||
|
||||
Args:
|
||||
pairs: List of (query, document) tuples to score
|
||||
|
||||
Returns:
|
||||
List of relevance scores
|
||||
"""
|
||||
if self._async_client is None:
|
||||
raise RuntimeError("Reranker not initialized. Call initialize() first.")
|
||||
class SiliconFlowCrossEncoder(CrossEncoderModel):
|
||||
"""
|
||||
SiliconFlow cross-encoder implementation.
|
||||
|
||||
if not pairs:
|
||||
return []
|
||||
SiliconFlow (https://siliconflow.cn) exposes a Cohere-compatible /rerank
|
||||
endpoint. Shares the HTTP client with ZeroEntropy/Cohere-custom-endpoint
|
||||
via _CohereCompatibleRerankClient.
|
||||
"""
|
||||
|
||||
# Group pairs by query for efficient batching
|
||||
query_groups: dict[str, list[tuple[int, str]]] = {}
|
||||
for idx, (query, text) in enumerate(pairs):
|
||||
if query not in query_groups:
|
||||
query_groups[query] = []
|
||||
query_groups[query].append((idx, text))
|
||||
RERANK_PATH = "/rerank"
|
||||
|
||||
all_scores = [0.0] * len(pairs)
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
model: str = DEFAULT_RERANKER_SILICONFLOW_MODEL,
|
||||
base_url: str = DEFAULT_RERANKER_SILICONFLOW_BASE_URL,
|
||||
timeout: float = 60.0,
|
||||
):
|
||||
self.model = model
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self._client = _CohereCompatibleRerankClient(
|
||||
api_key=api_key,
|
||||
model=model,
|
||||
rerank_url=f"{self.base_url}{self.RERANK_PATH}",
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
for query, indexed_texts in query_groups.items():
|
||||
texts = [text for _, text in indexed_texts]
|
||||
indices = [idx for idx, _ in indexed_texts]
|
||||
@property
|
||||
def provider_name(self) -> str:
|
||||
return "siliconflow"
|
||||
|
||||
response = await self._async_client.post(
|
||||
self.rerank_url,
|
||||
json={
|
||||
"model": self.model,
|
||||
"query": query,
|
||||
"documents": texts,
|
||||
"top_n": len(texts),
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
async def initialize(self) -> None:
|
||||
if self._client._async_client is not None:
|
||||
return
|
||||
logger.info(f"Reranker: initializing SiliconFlow provider at {self.base_url} with model {self.model}")
|
||||
await self._client.initialize()
|
||||
logger.info("Reranker: SiliconFlow provider initialized")
|
||||
|
||||
# Map scores back to original positions
|
||||
for item in result.get("results", []):
|
||||
original_idx = item["index"]
|
||||
score = item["relevance_score"]
|
||||
all_scores[indices[original_idx]] = score
|
||||
|
||||
return all_scores
|
||||
async def predict(self, pairs: list[tuple[str, str]]) -> list[float]:
|
||||
return await self._client.predict(pairs)
|
||||
|
||||
|
||||
class RRFPassthroughCrossEncoder(CrossEncoderModel):
|
||||
@@ -1530,6 +1573,17 @@ def create_cross_encoder_from_env() -> CrossEncoderModel:
|
||||
api_key=api_key,
|
||||
model=config.reranker_zeroentropy_model,
|
||||
)
|
||||
elif provider == "siliconflow":
|
||||
api_key = config.reranker_siliconflow_api_key
|
||||
if not api_key:
|
||||
raise ValueError(
|
||||
f"{ENV_RERANKER_SILICONFLOW_API_KEY} is required when {ENV_RERANKER_PROVIDER} is 'siliconflow'"
|
||||
)
|
||||
return SiliconFlowCrossEncoder(
|
||||
api_key=api_key,
|
||||
model=config.reranker_siliconflow_model,
|
||||
base_url=config.reranker_siliconflow_base_url,
|
||||
)
|
||||
elif provider == "google":
|
||||
project_id = config.reranker_google_project_id
|
||||
if not project_id:
|
||||
@@ -1548,5 +1602,5 @@ def create_cross_encoder_from_env() -> CrossEncoderModel:
|
||||
return JinaMLXCrossEncoder()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unknown reranker provider: {provider}. Supported: 'local', 'tei', 'cohere', 'zeroentropy', 'google', 'flashrank', 'litellm', 'litellm-sdk', 'rrf', 'jina-mlx'"
|
||||
f"Unknown reranker provider: {provider}. Supported: 'local', 'tei', 'cohere', 'zeroentropy', 'siliconflow', 'google', 'flashrank', 'litellm', 'litellm-sdk', 'rrf', 'jina-mlx'"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ Tests the Cohere cross-encoder implementation, including Azure AI Foundry endpoi
|
||||
"""
|
||||
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@@ -28,7 +28,7 @@ class TestCohereCrossEncoder:
|
||||
assert encoder.api_key == "test_key"
|
||||
assert encoder.model == "rerank-english-v3.0"
|
||||
assert encoder._client is None
|
||||
assert encoder._httpx_client is None
|
||||
assert encoder._http_client is None
|
||||
|
||||
# Mock the cohere import
|
||||
mock_cohere = MagicMock()
|
||||
@@ -36,7 +36,7 @@ class TestCohereCrossEncoder:
|
||||
with patch.dict("sys.modules", {"cohere": mock_cohere}):
|
||||
await encoder.initialize()
|
||||
assert encoder._client is not None
|
||||
assert encoder._httpx_client is None
|
||||
assert encoder._http_client is None
|
||||
mock_cohere.Client.assert_called_once_with(api_key="test_key", timeout=60.0)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -52,9 +52,14 @@ class TestCohereCrossEncoder:
|
||||
|
||||
await encoder.initialize()
|
||||
|
||||
assert encoder._httpx_client is not None
|
||||
assert encoder._http_client is not None
|
||||
assert encoder._client is None
|
||||
assert isinstance(encoder._httpx_client, httpx.Client)
|
||||
assert isinstance(encoder._http_client._async_client, httpx.AsyncClient)
|
||||
assert encoder._http_client.include_top_n is False
|
||||
assert (
|
||||
encoder._http_client.rerank_url
|
||||
== "https://my-endpoint.inference.ai.azure.com/models/cohere-rerank-v3-english/invoke"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_initialization_missing_package(self):
|
||||
@@ -150,7 +155,7 @@ class TestCohereCrossEncoder:
|
||||
|
||||
await encoder.initialize()
|
||||
|
||||
# Mock httpx response
|
||||
# Mock async httpx response
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = {
|
||||
"results": [
|
||||
@@ -159,8 +164,9 @@ class TestCohereCrossEncoder:
|
||||
{"index": 2, "relevance_score": 0.5},
|
||||
]
|
||||
}
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
encoder._httpx_client.post = MagicMock(return_value=mock_response)
|
||||
encoder._http_client._async_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
pairs = [
|
||||
("What is Python?", "Python is a programming language"),
|
||||
@@ -174,13 +180,15 @@ class TestCohereCrossEncoder:
|
||||
assert scores == [0.9, 0.7, 0.5]
|
||||
|
||||
# Verify httpx.post was called with correct URL and payload
|
||||
encoder._httpx_client.post.assert_called_once()
|
||||
call_args = encoder._httpx_client.post.call_args
|
||||
encoder._http_client._async_client.post.assert_called_once()
|
||||
call_args = encoder._http_client._async_client.post.call_args
|
||||
assert call_args[0][0] == "https://my-endpoint.inference.ai.azure.com/models/cohere-rerank-v3-english/invoke"
|
||||
assert call_args.kwargs["json"]["model"] == "cohere-rerank-v3-english"
|
||||
assert call_args.kwargs["json"]["query"] == "What is Python?"
|
||||
assert len(call_args.kwargs["json"]["documents"]) == 3
|
||||
assert call_args.kwargs["json"]["return_documents"] is False
|
||||
# Azure endpoints expect no top_n in the body
|
||||
assert "top_n" not in call_args.kwargs["json"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_predict_multiple_queries(self):
|
||||
@@ -281,7 +289,7 @@ class TestCohereCrossEncoder:
|
||||
request=MagicMock(),
|
||||
response=MagicMock(status_code=404),
|
||||
)
|
||||
encoder._httpx_client.post = MagicMock(return_value=mock_response)
|
||||
encoder._http_client._async_client.post = AsyncMock(return_value=mock_response)
|
||||
|
||||
pairs = [("What is Python?", "Python is a programming language")]
|
||||
|
||||
|
||||
@@ -15,7 +15,12 @@ import pytest
|
||||
from sqlalchemy import create_engine, text
|
||||
|
||||
from hindsight_api import MemoryEngine, RequestContext
|
||||
from hindsight_api.engine.cross_encoder import CohereCrossEncoder, LocalSTCrossEncoder, ZeroEntropyCrossEncoder
|
||||
from hindsight_api.engine.cross_encoder import (
|
||||
CohereCrossEncoder,
|
||||
LocalSTCrossEncoder,
|
||||
SiliconFlowCrossEncoder,
|
||||
ZeroEntropyCrossEncoder,
|
||||
)
|
||||
from hindsight_api.engine.embeddings import CohereEmbeddings, LocalSTEmbeddings, OpenAIEmbeddings
|
||||
from hindsight_api.engine.query_analyzer import DateparserQueryAnalyzer
|
||||
from hindsight_api.engine.task_backend import SyncTaskBackend
|
||||
@@ -739,3 +744,58 @@ class TestZeroEntropyCrossEncoder:
|
||||
assert all(isinstance(s, float) for s in scores)
|
||||
# The first result should be most relevant
|
||||
assert scores[0] > scores[2], "Direct answer should score higher than unrelated text"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# SiliconFlow Reranker Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def has_siliconflow_api_key() -> bool:
|
||||
"""Check if SiliconFlow API key is available."""
|
||||
return bool(os.environ.get("SILICONFLOW_API_KEY"))
|
||||
|
||||
|
||||
def get_siliconflow_api_key() -> str:
|
||||
"""Get SiliconFlow API key from environment."""
|
||||
return os.environ.get("SILICONFLOW_API_KEY", "")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def siliconflow_cross_encoder():
|
||||
"""Create SiliconFlow cross-encoder instance."""
|
||||
if not has_siliconflow_api_key():
|
||||
pytest.skip("SiliconFlow API key not available (set SILICONFLOW_API_KEY)")
|
||||
|
||||
cross_encoder = SiliconFlowCrossEncoder(
|
||||
api_key=get_siliconflow_api_key(),
|
||||
model="BAAI/bge-reranker-v2-m3",
|
||||
)
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
loop.run_until_complete(cross_encoder.initialize())
|
||||
finally:
|
||||
loop.close()
|
||||
return cross_encoder
|
||||
|
||||
|
||||
class TestSiliconFlowCrossEncoder:
|
||||
"""Tests for SiliconFlow cross-encoder/reranker."""
|
||||
|
||||
def test_siliconflow_cross_encoder_initialization(self, siliconflow_cross_encoder):
|
||||
"""Test that SiliconFlow cross-encoder initializes correctly."""
|
||||
assert siliconflow_cross_encoder.provider_name == "siliconflow"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_siliconflow_cross_encoder_predict(self, siliconflow_cross_encoder):
|
||||
"""Test that SiliconFlow cross-encoder can score pairs."""
|
||||
pairs = [
|
||||
("What is the capital of France?", "Paris is the capital of France."),
|
||||
("What is the capital of France?", "The Eiffel Tower is in Paris."),
|
||||
("What is the capital of France?", "Python is a programming language."),
|
||||
]
|
||||
scores = await siliconflow_cross_encoder.predict(pairs)
|
||||
|
||||
assert len(scores) == 3
|
||||
assert all(isinstance(s, float) for s in scores)
|
||||
assert scores[0] > scores[2], "Direct answer should score higher than unrelated text"
|
||||
|
||||
@@ -520,7 +520,7 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `HINDSIGHT_API_RERANKER_PROVIDER` | Provider: `local`, `tei`, `cohere`, `openrouter`, `zeroentropy`, `google`, `flashrank`, `litellm`, `litellm-sdk`, `jina-mlx`, or `rrf` | `local` |
|
||||
| `HINDSIGHT_API_RERANKER_PROVIDER` | Provider: `local`, `tei`, `cohere`, `openrouter`, `zeroentropy`, `siliconflow`, `google`, `flashrank`, `litellm`, `litellm-sdk`, `jina-mlx`, or `rrf` | `local` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_MODEL` | Model for local provider | `cross-encoder/ms-marco-MiniLM-L-6-v2` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT` | Max concurrent local reranking (prevents CPU thrashing under load) | `4` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_TRUST_REMOTE_CODE` | Allow loading models with custom code (security risk, disabled by default) | `false` |
|
||||
@@ -535,7 +535,7 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
| `HINDSIGHT_API_RERANKER_OPENROUTER_MODEL` | OpenRouter rerank model | `cohere/rerank-v3.5` |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_API_KEY` | Cohere API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_MODEL` | Cohere rerank model | `rerank-english-v3.0` |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_BASE_URL` | Custom base URL for Cohere-compatible API (e.g., Azure-hosted) | - |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_BASE_URL` | Custom base URL for any Cohere-compatible `/rerank` endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, etc.). When set, the `cohere` provider bypasses the Cohere SDK and calls the endpoint directly via HTTP. | - |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_API_BASE` | LiteLLM proxy base URL for reranking | `http://localhost:4000` |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_API_KEY` | LiteLLM proxy API key for reranking (optional, depends on proxy config) | - |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_MODEL` | LiteLLM **proxy** rerank model (use provider prefix, e.g., `cohere/rerank-english-v3.0`) | `cohere/rerank-english-v3.0` |
|
||||
@@ -546,6 +546,9 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY` | ZeroEntropy API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL` | ZeroEntropy rerank model (`zerank-2`, `zerank-2-small`) | `zerank-2` |
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_BASE_URL` | Custom base URL for ZeroEntropy-compatible API (e.g., mock server, proxy, or self-hosted deployment) | `https://api.zeroentropy.dev` |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY` | SiliconFlow API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL` | SiliconFlow rerank model (e.g., `BAAI/bge-reranker-v2-m3`) | `BAAI/bge-reranker-v2-m3` |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_BASE_URL` | Base URL for the SiliconFlow `/rerank` endpoint | `https://api.siliconflow.cn/v1` |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_PROJECT_ID` | Google Cloud project ID for Discovery Engine reranking (falls back to `HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID`) | - |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_MODEL` | Google Discovery Engine ranking model | `semantic-ranker-default-004` |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_SERVICE_ACCOUNT_KEY` | Path to service account JSON key (falls back to `HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY`). If unset, uses ADC. | - |
|
||||
@@ -578,11 +581,23 @@ export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
|
||||
# Azure-hosted Cohere - reranking via custom endpoint
|
||||
# Any Cohere-compatible /rerank endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, etc.)
|
||||
#
|
||||
# Setting HINDSIGHT_API_RERANKER_COHERE_BASE_URL switches the `cohere` provider
|
||||
# off the Cohere SDK and onto a plain HTTP client that speaks the standard
|
||||
# Cohere rerank wire format:
|
||||
# Request: POST {base_url} (or {base_url}/rerank, depending on host)
|
||||
# Authorization: Bearer <api_key>
|
||||
# {"model": "...", "query": "...", "documents": [...], "return_documents": false}
|
||||
# Response: {"results": [{"index": 0, "relevance_score": 0.9}, ...]}
|
||||
#
|
||||
# Any service implementing this contract works here. For Azure AI Foundry the
|
||||
# base_url is the full invoke URL; for SiliconFlow you can also use the
|
||||
# dedicated `siliconflow` provider below.
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-azure-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-azure-cohere-endpoint.com
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0 # whatever model the endpoint serves
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-cohere-compatible-endpoint.com
|
||||
|
||||
# ZeroEntropy - cloud-based reranking (state-of-the-art accuracy)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
|
||||
@@ -590,6 +605,12 @@ export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # or zerank-2-small
|
||||
# export HINDSIGHT_API_RERANKER_ZEROENTROPY_BASE_URL=https://your-custom-endpoint.com # optional
|
||||
|
||||
# SiliconFlow - cloud reranking via SiliconFlow's Cohere-compatible /rerank endpoint
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=siliconflow
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL=BAAI/bge-reranker-v2-m3
|
||||
# export HINDSIGHT_API_RERANKER_SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1 # default
|
||||
|
||||
# LiteLLM proxy - unified gateway for multiple reranking providers (requires running LiteLLM proxy server)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
|
||||
export HINDSIGHT_API_RERANKER_LITELLM_API_BASE=http://localhost:4000
|
||||
|
||||
@@ -476,6 +476,7 @@ Reranks initial search results to improve precision.
|
||||
| `local` | SentenceTransformers CrossEncoder (default) | Development, low latency |
|
||||
| `cohere` | Cohere rerank API | Production, high quality |
|
||||
| `zeroentropy` | ZeroEntropy rerank API (zerank-2) | Production, state-of-the-art accuracy |
|
||||
| `siliconflow` | SiliconFlow rerank API (Cohere-compatible `/rerank` endpoint) | Users in China or anyone on SiliconFlow's platform |
|
||||
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
|
||||
| `flashrank` | FlashRank (lightweight, fast) | Resource-constrained environments |
|
||||
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
|
||||
@@ -504,6 +505,15 @@ Reranks initial search results to improve precision.
|
||||
| `zerank-2` | Flagship multilingual reranker (default) |
|
||||
| `zerank-2-small` | Faster, lighter variant |
|
||||
|
||||
### SiliconFlow Models
|
||||
|
||||
SiliconFlow hosts a range of open-weight rerankers behind a Cohere-compatible `/rerank` endpoint:
|
||||
|
||||
| Model | Use Case |
|
||||
|-------|----------|
|
||||
| `BAAI/bge-reranker-v2-m3` | Multilingual, strong default |
|
||||
| `Qwen/Qwen3-Reranker-8B` | Larger, higher accuracy |
|
||||
|
||||
### LiteLLM Supported Providers
|
||||
|
||||
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
|
||||
@@ -528,11 +538,27 @@ export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
|
||||
# Cohere-compatible endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, ...)
|
||||
# Setting COHERE_BASE_URL switches the provider off the Cohere SDK and onto a
|
||||
# plain HTTP client that speaks the standard rerank wire format:
|
||||
# POST {base_url} Authorization: Bearer <key>
|
||||
# {"model","query","documents","return_documents":false}
|
||||
# -> {"results":[{"index","relevance_score"}, ...]}
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-v3.5 # whatever model the endpoint serves
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-endpoint.example/rerank
|
||||
|
||||
# ZeroEntropy (state-of-the-art accuracy)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # default, can omit
|
||||
|
||||
# SiliconFlow (Cohere-compatible /rerank endpoint)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=siliconflow
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL=BAAI/bge-reranker-v2-m3 # default, can omit
|
||||
|
||||
# TEI (self-hosted)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=tei
|
||||
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
|
||||
|
||||
@@ -520,7 +520,7 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `HINDSIGHT_API_RERANKER_PROVIDER` | Provider: `local`, `tei`, `cohere`, `openrouter`, `zeroentropy`, `google`, `flashrank`, `litellm`, `litellm-sdk`, `jina-mlx`, or `rrf` | `local` |
|
||||
| `HINDSIGHT_API_RERANKER_PROVIDER` | Provider: `local`, `tei`, `cohere`, `openrouter`, `zeroentropy`, `siliconflow`, `google`, `flashrank`, `litellm`, `litellm-sdk`, `jina-mlx`, or `rrf` | `local` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_MODEL` | Model for local provider | `cross-encoder/ms-marco-MiniLM-L-6-v2` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_MAX_CONCURRENT` | Max concurrent local reranking (prevents CPU thrashing under load) | `4` |
|
||||
| `HINDSIGHT_API_RERANKER_LOCAL_TRUST_REMOTE_CODE` | Allow loading models with custom code (security risk, disabled by default) | `false` |
|
||||
@@ -535,7 +535,7 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
| `HINDSIGHT_API_RERANKER_OPENROUTER_MODEL` | OpenRouter rerank model | `cohere/rerank-v3.5` |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_API_KEY` | Cohere API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_MODEL` | Cohere rerank model | `rerank-english-v3.0` |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_BASE_URL` | Custom base URL for Cohere-compatible API (e.g., Azure-hosted) | - |
|
||||
| `HINDSIGHT_API_RERANKER_COHERE_BASE_URL` | Custom base URL for any Cohere-compatible `/rerank` endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, etc.). When set, the `cohere` provider bypasses the Cohere SDK and calls the endpoint directly via HTTP. | - |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_API_BASE` | LiteLLM proxy base URL for reranking | `http://localhost:4000` |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_API_KEY` | LiteLLM proxy API key for reranking (optional, depends on proxy config) | - |
|
||||
| `HINDSIGHT_API_RERANKER_LITELLM_MODEL` | LiteLLM **proxy** rerank model (use provider prefix, e.g., `cohere/rerank-english-v3.0`) | `cohere/rerank-english-v3.0` |
|
||||
@@ -546,6 +546,9 @@ Google's `gemini-embedding-001` produces 3072 dimensions natively but supports c
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY` | ZeroEntropy API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL` | ZeroEntropy rerank model (`zerank-2`, `zerank-2-small`) | `zerank-2` |
|
||||
| `HINDSIGHT_API_RERANKER_ZEROENTROPY_BASE_URL` | Custom base URL for ZeroEntropy-compatible API (e.g., mock server, proxy, or self-hosted deployment) | `https://api.zeroentropy.dev` |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY` | SiliconFlow API key for reranking | - |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL` | SiliconFlow rerank model (e.g., `BAAI/bge-reranker-v2-m3`) | `BAAI/bge-reranker-v2-m3` |
|
||||
| `HINDSIGHT_API_RERANKER_SILICONFLOW_BASE_URL` | Base URL for the SiliconFlow `/rerank` endpoint | `https://api.siliconflow.cn/v1` |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_PROJECT_ID` | Google Cloud project ID for Discovery Engine reranking (falls back to `HINDSIGHT_API_LLM_VERTEXAI_PROJECT_ID`) | - |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_MODEL` | Google Discovery Engine ranking model | `semantic-ranker-default-004` |
|
||||
| `HINDSIGHT_API_RERANKER_GOOGLE_SERVICE_ACCOUNT_KEY` | Path to service account JSON key (falls back to `HINDSIGHT_API_LLM_VERTEXAI_SERVICE_ACCOUNT_KEY`). If unset, uses ADC. | - |
|
||||
@@ -578,11 +581,23 @@ export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
|
||||
# Azure-hosted Cohere - reranking via custom endpoint
|
||||
# Any Cohere-compatible /rerank endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, etc.)
|
||||
#
|
||||
# Setting HINDSIGHT_API_RERANKER_COHERE_BASE_URL switches the `cohere` provider
|
||||
# off the Cohere SDK and onto a plain HTTP client that speaks the standard
|
||||
# Cohere rerank wire format:
|
||||
# Request: POST {base_url} (or {base_url}/rerank, depending on host)
|
||||
# Authorization: Bearer <api_key>
|
||||
# {"model": "...", "query": "...", "documents": [...], "return_documents": false}
|
||||
# Response: {"results": [{"index": 0, "relevance_score": 0.9}, ...]}
|
||||
#
|
||||
# Any service implementing this contract works here. For Azure AI Foundry the
|
||||
# base_url is the full invoke URL; for SiliconFlow you can also use the
|
||||
# dedicated `siliconflow` provider below.
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-azure-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-azure-cohere-endpoint.com
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0 # whatever model the endpoint serves
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-cohere-compatible-endpoint.com
|
||||
|
||||
# ZeroEntropy - cloud-based reranking (state-of-the-art accuracy)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
|
||||
@@ -590,6 +605,12 @@ export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # or zerank-2-small
|
||||
# export HINDSIGHT_API_RERANKER_ZEROENTROPY_BASE_URL=https://your-custom-endpoint.com # optional
|
||||
|
||||
# SiliconFlow - cloud reranking via SiliconFlow's Cohere-compatible /rerank endpoint
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=siliconflow
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL=BAAI/bge-reranker-v2-m3
|
||||
# export HINDSIGHT_API_RERANKER_SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1 # default
|
||||
|
||||
# LiteLLM proxy - unified gateway for multiple reranking providers (requires running LiteLLM proxy server)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=litellm
|
||||
export HINDSIGHT_API_RERANKER_LITELLM_API_BASE=http://localhost:4000
|
||||
|
||||
@@ -465,6 +465,7 @@ Reranks initial search results to improve precision.
|
||||
| `local` | SentenceTransformers CrossEncoder (default) | Development, low latency |
|
||||
| `cohere` | Cohere rerank API | Production, high quality |
|
||||
| `zeroentropy` | ZeroEntropy rerank API (zerank-2) | Production, state-of-the-art accuracy |
|
||||
| `siliconflow` | SiliconFlow rerank API (Cohere-compatible `/rerank` endpoint) | Users in China or anyone on SiliconFlow's platform |
|
||||
| `tei` | HuggingFace Text Embeddings Inference | Production, self-hosted |
|
||||
| `flashrank` | FlashRank (lightweight, fast) | Resource-constrained environments |
|
||||
| `litellm` | LiteLLM proxy (unified gateway) | Multi-provider setups |
|
||||
@@ -493,6 +494,15 @@ Reranks initial search results to improve precision.
|
||||
| `zerank-2` | Flagship multilingual reranker (default) |
|
||||
| `zerank-2-small` | Faster, lighter variant |
|
||||
|
||||
### SiliconFlow Models
|
||||
|
||||
SiliconFlow hosts a range of open-weight rerankers behind a Cohere-compatible `/rerank` endpoint:
|
||||
|
||||
| Model | Use Case |
|
||||
|-------|----------|
|
||||
| `BAAI/bge-reranker-v2-m3` | Multilingual, strong default |
|
||||
| `Qwen/Qwen3-Reranker-8B` | Larger, higher accuracy |
|
||||
|
||||
### LiteLLM Supported Providers
|
||||
|
||||
LiteLLM supports multiple reranking providers via the `/rerank` endpoint:
|
||||
@@ -517,11 +527,27 @@ export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-english-v3.0
|
||||
|
||||
# Cohere-compatible endpoint (Azure AI Foundry, Jina, Voyage, self-hosted BGE, ...)
|
||||
# Setting COHERE_BASE_URL switches the provider off the Cohere SDK and onto a
|
||||
# plain HTTP client that speaks the standard rerank wire format:
|
||||
# POST {base_url} Authorization: Bearer <key>
|
||||
# {"model","query","documents","return_documents":false}
|
||||
# -> {"results":[{"index","relevance_score"}, ...]}
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=cohere
|
||||
export HINDSIGHT_API_RERANKER_COHERE_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_COHERE_MODEL=rerank-v3.5 # whatever model the endpoint serves
|
||||
export HINDSIGHT_API_RERANKER_COHERE_BASE_URL=https://your-endpoint.example/rerank
|
||||
|
||||
# ZeroEntropy (state-of-the-art accuracy)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=zeroentropy
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_ZEROENTROPY_MODEL=zerank-2 # default, can omit
|
||||
|
||||
# SiliconFlow (Cohere-compatible /rerank endpoint)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=siliconflow
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_API_KEY=your-api-key
|
||||
export HINDSIGHT_API_RERANKER_SILICONFLOW_MODEL=BAAI/bge-reranker-v2-m3 # default, can omit
|
||||
|
||||
# TEI (self-hosted)
|
||||
export HINDSIGHT_API_RERANKER_PROVIDER=tei
|
||||
export HINDSIGHT_API_RERANKER_TEI_URL=http://localhost:8081
|
||||
|
||||
Reference in New Issue
Block a user