Feat: Add Aliyun (DashScope) as new reranker model provider#81
Feat: Add Aliyun (DashScope) as new reranker model provider#81leejangthegreat wants to merge 28 commits intolfnovo:mainfrom
Conversation
There was a problem hiding this comment.
6 issues found across 8 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/esperanto/model_discovery.py">
<violation number="1" location="src/esperanto/model_discovery.py:490">
P2: DashScope models are implemented but the provider registry omits a "dashscope" entry, so model discovery cannot route this provider through PROVIDER_MODELS_REGISTRY.</violation>
<violation number="2" location="src/esperanto/model_discovery.py:520">
P2: DashScope Qwen3-VL reranker context window is documented as 32k, but the hardcoded value is 800k. This mismatch can mislead callers into sending inputs far beyond supported limits.</violation>
</file>
<file name="docs/providers/README.md">
<violation number="1" location="docs/providers/README.md:19">
P3: DashScope is marked as a reranking provider in the support matrix but is missing from the “All Reranking Providers” list, leaving the selection guide inconsistent with the matrix.</violation>
<violation number="2" location="docs/providers/README.md:19">
P2: New documentation link points to ./dashscope.md, but docs/providers/dashscope.md is missing, so the link will be broken.</violation>
</file>
<file name="src/esperanto/providers/reranker/dashscope.py">
<violation number="1" location="src/esperanto/providers/reranker/dashscope.py:306">
P2: RerankResult.index is declared as a non-negative int, but _parse_response appends results even when doc_idx is None or out of bounds, which will raise a validation error and break parsing. Skip invalid results or coerce to a valid index before constructing RerankResult.</violation>
<violation number="2" location="src/esperanto/providers/reranker/dashscope.py:390">
P2: `arerank` declares `fps` as Optional[str] but passes it into `_validate_inputs`, which compares it to floats. If callers pass a string per the type hint (e.g., "0.5"), the comparison will raise a TypeError. Make the async signature accept Optional[float] to match `_validate_inputs` and the sync `rerank`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| id="gte-rerank-v2", owned_by="dashscope", context_window=30_000 | ||
| ), | ||
| Model( | ||
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000 |
There was a problem hiding this comment.
P2: DashScope Qwen3-VL reranker context window is documented as 32k, but the hardcoded value is 800k. This mismatch can mislead callers into sending inputs far beyond supported limits.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/esperanto/model_discovery.py, line 520:
<comment>DashScope Qwen3-VL reranker context window is documented as 32k, but the hardcoded value is 800k. This mismatch can mislead callers into sending inputs far beyond supported limits.</comment>
<file context>
@@ -487,6 +487,46 @@ def get_jina_models(
+ id="gte-rerank-v2", owned_by="dashscope", context_window=30_000
+ ),
+ Model(
+ id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000
+ )
+ ]
</file context>
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000 | |
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=32_000 |
| | [Ollama](./ollama.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | ||
| | [Mistral](./mistral.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | | ||
| | [DeepSeek](./deepseek.md) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | | ||
| | [DashScope](./dashscope.md) | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | |
There was a problem hiding this comment.
P2: New documentation link points to ./dashscope.md, but docs/providers/dashscope.md is missing, so the link will be broken.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/providers/README.md, line 19:
<comment>New documentation link points to ./dashscope.md, but docs/providers/dashscope.md is missing, so the link will be broken.</comment>
<file context>
@@ -16,6 +16,7 @@ Welcome to the Esperanto provider guide. This page helps you choose the right AI
| [Ollama](./ollama.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| [Mistral](./mistral.md) | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| [DeepSeek](./deepseek.md) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
+| [DashScope](./dashscope.md) | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| [Perplexity](./perplexity.md) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| [xAI](./xai.md) | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
</file context>
|
|
||
| for result, norm_score in zip(raw_results, normalized_scores): | ||
| doc_idx = result.get("index") | ||
| if doc_idx is None or doc_idx < -0 or doc_idx >= len(documents): |
There was a problem hiding this comment.
P2: RerankResult.index is declared as a non-negative int, but _parse_response appends results even when doc_idx is None or out of bounds, which will raise a validation error and break parsing. Skip invalid results or coerce to a valid index before constructing RerankResult.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/esperanto/providers/reranker/dashscope.py, line 306:
<comment>RerankResult.index is declared as a non-negative int, but _parse_response appends results even when doc_idx is None or out of bounds, which will raise a validation error and break parsing. Skip invalid results or coerce to a valid index before constructing RerankResult.</comment>
<file context>
@@ -0,0 +1,478 @@
+
+ for result, norm_score in zip(raw_results, normalized_scores):
+ doc_idx = result.get("index")
+ if doc_idx is None or doc_idx < -0 or doc_idx >= len(documents):
+ cur_doc = ""
+ else:
</file context>
There was a problem hiding this comment.
Pull request overview
Adds a new reranker provider implementation for Aliyun DashScope and wires it into the factory, with accompanying docs and tests.
Changes:
- Implement
DashScopeRerankerModel(sync/async, payload building, response parsing, LangChain adapter). - Register the provider in
AIFactoryand add a static model discovery function. - Add provider documentation and a dedicated test suite for DashScope reranking.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/providers/reranker/test_dashscope.py | Adds unit tests for DashScope reranker init/validation/payload/response parsing. |
| src/esperanto/providers/reranker/dashscope.py | Implements the DashScope reranker provider (including partial multimodal support). |
| src/esperanto/model_discovery.py | Adds a get_dashscope_models() discovery function (currently hardcoded). |
| src/esperanto/factory.py | Registers "dashscope" under reranker providers. |
| docs/providers/dashscope.md | Adds DashScope provider documentation and examples (including multimodal). |
| docs/providers/README.md | Adds DashScope to provider support matrix and provider list. |
| docs/capabilities/reranking.md | Mentions DashScope in reranker provider selection guidance. |
| README.md | Adds DashScope to the main provider list/support matrix and example provider output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| |------------|-----------|-------| | ||
| | Language Models (LLM) | ❌ | Not available | | ||
| | Embeddings | ❌ | Not available | | ||
| | Reranking | ⚠️ | qwen3-rerank, gte-rerank-v2, * qwen3-vl-rerank | |
There was a problem hiding this comment.
In the Supported Capabilities table, the Notes cell contains * qwen3-vl-rerank, which won’t render as intended (the leading * with a space isn’t italic/footnote). Consider removing the stray * or formatting it as a proper footnote/italic so the table renders cleanly.
| | Reranking | ⚠️ | qwen3-rerank, gte-rerank-v2, * qwen3-vl-rerank | | |
| | Reranking | ⚠️ | qwen3-rerank, gte-rerank-v2, qwen3-vl-rerank | |
| ] | ||
|
|
||
| responses = reranker.rerank( | ||
| query=query, |
There was a problem hiding this comment.
The multi-modal reranker example has a tab before query=query, which makes the code block mis-indented and harder to copy/paste. Normalize indentation to spaces so the snippet is valid Python as-is.
| query=query, | |
| query=query, |
| for result, norm_score in zip(raw_results, normalized_scores): | ||
| doc_idx = result.get("index") | ||
| if doc_idx is None or doc_idx < -0 or doc_idx >= len(documents): | ||
| cur_doc = "" | ||
| else: | ||
| cur_doc = str(documents[doc_idx]) | ||
|
|
||
| # NOTE: For vl models, get a str for "{'text' | 'image' | 'video': value}" | ||
| rerank_results.append(RerankResult( | ||
| index=doc_idx, | ||
| document=cur_doc, | ||
| relevance_score=norm_score | ||
| )) |
There was a problem hiding this comment.
In _parse_response, when doc_idx is None or out of range you set cur_doc to "", but still pass index=doc_idx into RerankResult. RerankResult.index is an int with ge=0, so None or negative indices will raise a Pydantic validation error and break reranking. Consider skipping invalid results or coercing to a safe non-negative index before constructing RerankResult.
|
|
||
| return models | ||
|
|
||
|
|
There was a problem hiding this comment.
get_dashscope_models is added, but DashScope is not registered in PROVIDER_MODELS_REGISTRY, so AIFactory.get_provider_models('dashscope', ...) will still raise "Provider not supported". Add an entry mapping "dashscope" -> get_dashscope_models so model discovery works as advertised.
| # Register DashScope provider in the global provider models registry | |
| PROVIDER_MODELS_REGISTRY["dashscope"] = get_dashscope_models |
| assert len(reranker.models) > 0 | ||
| # Model type is None when not explicitly provided by the API | ||
| assert all(model.type is None for model in reranker.models) |
There was a problem hiding this comment.
This test uses the .models property, which is deprecated in RerankerModel and emits a DeprecationWarning (scheduled for removal in v3.0). Consider switching the test to use AIFactory.get_provider_models('dashscope', ...) (once DashScope is registered) or the provider’s internal _get_models() to avoid locking tests onto deprecated API.
| assert len(reranker.models) > 0 | |
| # Model type is None when not explicitly provided by the API | |
| assert all(model.type is None for model in reranker.models) | |
| models = reranker._get_models() | |
| assert len(models) > 0 | |
| # Model type is None when not explicitly provided by the API | |
| assert all(model.type is None for model in models) |
| __MULTI_MODAL = ["qwen3-vl-rerank"] | ||
|
|
||
| def __post_init__(self): | ||
| """Initialize Jina reranker after dataclass initialization.""" |
There was a problem hiding this comment.
__post_init__ docstring says "Initialize Jina reranker" but this is the DashScope provider. This looks like a copy/paste and should be corrected to avoid misleading documentation/search results.
| """Initialize Jina reranker after dataclass initialization.""" | |
| """Initialize DashScope reranker after dataclass initialization.""" |
| ```python | ||
| import asyncio | ||
|
|
||
| async def rerank_async(): | ||
| reranker = AIFactory.create_reranker("dashscope", "qwen3-rerank") | ||
| results = await reranker.arerank(query, documents, top_k=3) | ||
| return results |
There was a problem hiding this comment.
The async example imports only asyncio but uses AIFactory without importing it in that snippet. Either add from esperanto.factory import AIFactory to the code block or include the import in the snippet header so it’s copy/pasteable.
There was a problem hiding this comment.
3 issues found across 8 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/esperanto/model_discovery.py">
<violation number="1" location="src/esperanto/model_discovery.py:519">
P2: DashScope Qwen3-VL reranker context_window is set to 800,000, but public Qwen3-VL-Reranker docs indicate a ~32k context length. This misrepresents the model’s limits and can lead to incorrect token handling.</violation>
</file>
<file name="src/esperanto/providers/reranker/dashscope.py">
<violation number="1" location="src/esperanto/providers/reranker/dashscope.py:400">
P2: arerank declares fps as Optional[str] but _validate_inputs compares fps numerically, so passing a string (as the type hint allows) will raise a TypeError. Align the async signature with the sync one (Optional[float]).</violation>
</file>
<file name="docs/providers/dashscope.md">
<violation number="1" location="docs/providers/dashscope.md:194">
P3: Python example uses a tab for indentation while other lines use spaces, which can cause IndentationError if copied.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| id="gte-rerank-v2", owned_by="dashscope", context_window=30_000 | ||
| ), | ||
| Model( | ||
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000 |
There was a problem hiding this comment.
P2: DashScope Qwen3-VL reranker context_window is set to 800,000, but public Qwen3-VL-Reranker docs indicate a ~32k context length. This misrepresents the model’s limits and can lead to incorrect token handling.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/esperanto/model_discovery.py, line 519:
<comment>DashScope Qwen3-VL reranker context_window is set to 800,000, but public Qwen3-VL-Reranker docs indicate a ~32k context length. This misrepresents the model’s limits and can lead to incorrect token handling.</comment>
<file context>
@@ -487,6 +487,45 @@ def get_jina_models(
+ id="gte-rerank-v2", owned_by="dashscope", context_window=30_000
+ ),
+ Model(
+ id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000
+ )
+ ]
</file context>
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=800_000 | |
| id="qwen3-vl-rerank", owned_by="dashscope", context_window=32_000 |
| ] | ||
|
|
||
| responses = reranker.rerank( | ||
| query=query, |
There was a problem hiding this comment.
P3: Python example uses a tab for indentation while other lines use spaces, which can cause IndentationError if copied.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/providers/dashscope.md, line 194:
<comment>Python example uses a tab for indentation while other lines use spaces, which can cause IndentationError if copied.</comment>
<file context>
@@ -0,0 +1,233 @@
+]
+
+responses = reranker.rerank(
+ query=query,
+ documents=documents,
+ top_k=2,
</file context>
…odel Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
leejangthegreat
left a comment
There was a problem hiding this comment.
Done fixing some typo and docstring mismatches
Reference Issues/PRs
Feature #78
What does this implement/fix? Explain your changes.
This change implements support for Aliyun (DashScope) as a new provider for reranker models. Changes include:
.providers.rerankerfactoryandmodel_discoveryAny other comments?
NOTE: This implementation includes partial support for a multi-modal reranker
qwen3-vl-rerank. Currently this is done by extending interface ofDashScopeRerankerModel.rerankwithout modifying its base class. As a result, the multi-modal capabilities are limited.This should be re-implemented in future if we want better support for general multi-modal LLMs.