Skip to content

Commit ab97f55

Browse files
committed
fix(reranking): use DashScope native API format
- Use native endpoint /api/v1/services/rerank/text-rerank/text-rerank - Update request body to { model, input, parameters } format - Update response parsing for { output, request_id } format - Add documentation for Chinese users using domestic endpoint
1 parent 379a98a commit ab97f55

8 files changed

Lines changed: 198 additions & 48 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ yarn-error.log*
2626
*.backup
2727
CLAUDE.md
2828
AGENTS.md
29+
test-rerank.ts

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# qwen-ai-provider-v5
22

3+
## 2.0.2
4+
5+
### Patch Changes
6+
7+
- Fix reranking API endpoint and request format
8+
- Use DashScope native API endpoint `/api/v1/services/rerank/text-rerank/text-rerank` instead of OpenAI compatible mode
9+
- Update request body format to `{ model, input: { query, documents }, parameters: { top_n } }`
10+
- Update response parsing for `{ output: { results: [...] }, request_id }` format
11+
- Add documentation for Chinese users using domestic endpoint
12+
313
## 2.0.1
414

515
### Patch Changes

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,18 @@ const modelLegacy = qwen.textEmbeddingModel("text-embedding-v3")
222222

223223
You can create models that call the [Qwen reranking API](https://qwenlm.github.io/blog/qwen3-embedding/) using the `.rerankingModel()` factory method. Reranking models improve search relevance by reordering documents based on their semantic similarity to a query.
224224

225+
> **Note for Chinese users (中国用户)**
226+
>
227+
> If you're using a Chinese API key from `dashscope.aliyuncs.com`, you need to specify the domestic endpoint:
228+
>
229+
> ```ts
230+
> import { createQwen } from "qwen-ai-provider-v5"
231+
>
232+
> const qwen = createQwen({
233+
> baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
234+
> })
235+
> ```
236+
225237
```ts
226238
const model = qwen.rerankingModel("gte-rerank-v2")
227239
```
@@ -381,7 +393,7 @@ console.log(JSON.stringify(result.object.recipe, null, 2))
381393

382394
### generate-obj-reasoning-mdl.ts
383395

384-
```typescript
396+
````typescript
385397
import { generateObject, generateText } from "ai"
386398
### generate-object-reasoning-mdl.ts
387399

@@ -421,7 +433,7 @@ async function main() {
421433
}
422434
423435
main().catch(console.error)
424-
```
436+
````
425437
426438
### embed-text.ts
427439

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "qwen-ai-provider-v5",
33
"type": "commonjs",
4-
"version": "2.0.1",
4+
"version": "2.0.2",
55
"packageManager": "pnpm@10.2.0",
66
"description": "Vercel AI Provider v6 for running Qwen AI models",
77
"author": "bolechen <avenger@php.net>",
@@ -64,6 +64,8 @@
6464
"@types/node": "^22.13.1",
6565
"@vitest/coverage-v8": "^3.0.5",
6666
"@vitest/ui": "^3.0.5",
67+
"ai": "^6.0.3",
68+
"dotenv": "^17.2.3",
6769
"eslint": "^9.19.0",
6870
"eslint-plugin-format": "^1.0.1",
6971
"husky": "^9.1.7",

pnpm-lock.yaml

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__tests__/qwen-reranking-model.test.ts

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@ const testDocuments = [
1616
]
1717

1818
describe("doRerank", () => {
19+
let requestUrl: string
1920
let requestBody: any
2021
let requestHeaders: Record<string, string>
2122
let responseBody: any
2223
let responseHeaders: Record<string, string>
2324

2425
beforeEach(() => {
26+
requestUrl = ""
2527
requestBody = undefined
2628
requestHeaders = {}
29+
// DashScope native API response format
2730
responseBody = {
28-
results: dummyRankingResults,
31+
output: {
32+
results: dummyRankingResults,
33+
},
2934
usage: { total_tokens: 100 },
35+
request_id: "test-request-id-123",
3036
}
3137
responseHeaders = {
3238
"content-type": "application/json",
@@ -35,13 +41,14 @@ describe("doRerank", () => {
3541

3642
function createTestProvider(overrides?: any) {
3743
return createQwen({
38-
baseURL: "https://my.api.com/v1/",
44+
baseURL: "https://my.api.com/compatible-mode/v1",
3945
headers: {
4046
Authorization: `Bearer test-api-key`,
4147
},
4248
...overrides,
43-
fetch: async (_url, init) => {
49+
fetch: async (url, init) => {
4450
// Capture request
51+
requestUrl = url as string
4552
if (init?.body) {
4653
requestBody = JSON.parse(init.body as string)
4754
}
@@ -61,6 +68,21 @@ describe("doRerank", () => {
6168
})
6269
}
6370

71+
it("should use DashScope native API endpoint (not compatible mode)", async () => {
72+
const provider = createTestProvider()
73+
const model = provider.rerankingModel("gte-rerank-v2")
74+
75+
await model.doRerank({
76+
documents: { type: "text", values: testDocuments },
77+
query: "talk about rain",
78+
})
79+
80+
// Should use native API endpoint, not compatible mode
81+
expect(requestUrl).toBe(
82+
"https://my.api.com/api/v1/services/rerank/text-rerank/text-rerank",
83+
)
84+
})
85+
6486
it("should extract ranking results", async () => {
6587
const provider = createTestProvider()
6688
const model = provider.rerankingModel("gte-rerank-v2")
@@ -77,6 +99,18 @@ describe("doRerank", () => {
7799
])
78100
})
79101

102+
it("should return request_id in response", async () => {
103+
const provider = createTestProvider()
104+
const model = provider.rerankingModel("gte-rerank-v2")
105+
106+
const { response } = await model.doRerank({
107+
documents: { type: "text", values: testDocuments },
108+
query: "talk about rain",
109+
})
110+
111+
expect(response?.id).toBe("test-request-id-123")
112+
})
113+
80114
it("should expose the raw response headers", async () => {
81115
responseHeaders = {
82116
...responseHeaders,
@@ -97,7 +131,7 @@ describe("doRerank", () => {
97131
})
98132
})
99133

100-
it("should pass the model, query, and documents", async () => {
134+
it("should pass the model, query, and documents in DashScope native format", async () => {
101135
const provider = createTestProvider()
102136
const model = provider.rerankingModel("gte-rerank-v2")
103137

@@ -108,12 +142,15 @@ describe("doRerank", () => {
108142

109143
expect(requestBody).toStrictEqual({
110144
model: "gte-rerank-v2",
111-
query: "talk about rain",
112-
documents: testDocuments,
145+
input: {
146+
query: "talk about rain",
147+
documents: testDocuments,
148+
},
149+
parameters: {},
113150
})
114151
})
115152

116-
it("should pass topN parameter", async () => {
153+
it("should pass topN parameter in parameters object", async () => {
117154
const provider = createTestProvider()
118155
const model = provider.rerankingModel("gte-rerank-v2")
119156

@@ -125,13 +162,17 @@ describe("doRerank", () => {
125162

126163
expect(requestBody).toStrictEqual({
127164
model: "gte-rerank-v2",
128-
query: "talk about rain",
129-
documents: testDocuments,
130-
top_n: 2,
165+
input: {
166+
query: "talk about rain",
167+
documents: testDocuments,
168+
},
169+
parameters: {
170+
top_n: 2,
171+
},
131172
})
132173
})
133174

134-
it("should pass returnDocuments setting", async () => {
175+
it("should pass returnDocuments setting in parameters object", async () => {
135176
const provider = createTestProvider()
136177
const model = provider.rerankingModel("gte-rerank-v2", {
137178
returnDocuments: true,
@@ -144,9 +185,13 @@ describe("doRerank", () => {
144185

145186
expect(requestBody).toStrictEqual({
146187
model: "gte-rerank-v2",
147-
query: "talk about rain",
148-
documents: testDocuments,
149-
return_documents: true,
188+
input: {
189+
query: "talk about rain",
190+
documents: testDocuments,
191+
},
192+
parameters: {
193+
return_documents: true,
194+
},
150195
})
151196
})
152197

@@ -164,7 +209,7 @@ describe("doRerank", () => {
164209
query: "talk about rain",
165210
})
166211

167-
expect(requestBody.documents).toStrictEqual([
212+
expect(requestBody.input.documents).toStrictEqual([
168213
JSON.stringify(objectDocs[0]),
169214
JSON.stringify(objectDocs[1]),
170215
])

src/qwen-provider.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,22 @@ export function createQwen(options: QwenProviderSettings = {}): QwenProvider {
193193
)
194194

195195
// Create a reranking model instance.
196+
// Note: Reranking uses DashScope native API, not OpenAI compatible mode.
197+
// The baseURL is extracted without /compatible-mode/v1 suffix.
196198
const createRerankingModel = (
197199
modelId: QwenRerankingModelId,
198200
settings: QwenRerankingSettings = {},
199-
) =>
200-
new QwenRerankingModel(modelId, settings, getCommonModelConfig("reranking"))
201+
) => {
202+
// Extract the base domain from the baseURL (remove /compatible-mode/v1 if present)
203+
const rerankBaseURL = (baseURL ?? "https://dashscope-intl.aliyuncs.com/compatible-mode/v1")
204+
.replace(/\/compatible-mode\/v1$/, "")
205+
return new QwenRerankingModel(modelId, settings, {
206+
provider: "qwen.reranking",
207+
baseURL: rerankBaseURL,
208+
headers: getHeaders,
209+
fetch: options.fetch,
210+
})
211+
}
201212

202213
// Default provider returns a chat model.
203214
const provider = (modelId: QwenChatModelId, settings?: QwenChatSettings) =>

0 commit comments

Comments
 (0)