From ed2e22197a08c11f5d6d7f0ab94fccee483186b9 Mon Sep 17 00:00:00 2001 From: lorenzozane Date: Thu, 17 Sep 2026 00:07:39 +0800 Subject: [PATCH] fix(providers): add missing modelsUrl for qwen-cloud-token-plan (#13764) Merged. One missing field, one very concrete symptom: without `modelsUrl` the import silently fell back to the local catalog with "API unavailable". The test asserts both the registry entry and the derivation, so a future registry edit cannot drop it again. Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thank you. --- .../registry/qwen-cloud-token-plan/index.ts | 1 + .../qwen-cloud-token-plan-discovery.test.ts | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/unit/qwen-cloud-token-plan-discovery.test.ts diff --git a/open-sse/config/providers/registry/qwen-cloud-token-plan/index.ts b/open-sse/config/providers/registry/qwen-cloud-token-plan/index.ts index 591332e983..a0daa9048e 100644 --- a/open-sse/config/providers/registry/qwen-cloud-token-plan/index.ts +++ b/open-sse/config/providers/registry/qwen-cloud-token-plan/index.ts @@ -7,6 +7,7 @@ export const qwen_cloud_token_planProvider: RegistryEntry = { executor: "default", baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions", + modelsUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/models", authType: "apikey", authHeader: "bearer", models: [ diff --git a/tests/unit/qwen-cloud-token-plan-discovery.test.ts b/tests/unit/qwen-cloud-token-plan-discovery.test.ts new file mode 100644 index 0000000000..9a97d73688 --- /dev/null +++ b/tests/unit/qwen-cloud-token-plan-discovery.test.ts @@ -0,0 +1,47 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { qwen_cloud_token_planProvider } from "../../open-sse/config/providers/registry/qwen-cloud-token-plan/index.ts"; +import { deriveConfigFromRegistryModelsUrl } from "../../src/app/api/providers/[id]/models/discoveryConfig.ts"; + +const SPEC_MODELS_URL = + "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/models"; + +test("qwen-cloud-token-plan registry entry exposes the live /v1/models URL", () => { + assert.equal(qwen_cloud_token_planProvider.id, "qwen-cloud-token-plan"); + // Without modelsUrl, deriveConfigFromRegistryModelsUrl() returns undefined and + // model discovery falls back to the local catalog + // ("API unavailable — using local catalog", issue #13506). + assert.equal(qwen_cloud_token_planProvider.modelsUrl, SPEC_MODELS_URL); +}); + +test("deriveConfigFromRegistryModelsUrl covers qwen-cloud-token-plan", () => { + const config = deriveConfigFromRegistryModelsUrl("qwen-cloud-token-plan"); + assert.ok(config, "expected a discovery config derived from the registry modelsUrl"); + assert.equal(config.url, SPEC_MODELS_URL); + assert.equal(config.method, "GET"); + assert.equal(config.authHeader, "Authorization"); + assert.equal(config.authPrefix, "Bearer "); +}); + +test("derived config parses the token-plan OpenAI-style list response", () => { + const config = deriveConfigFromRegistryModelsUrl("qwen-cloud-token-plan"); + assert.ok(config); + // Shape verified manually against the live endpoint in issue #13506. + const live = { + first_id: "model-id-0", + data: [ + { created: 1785722409, owned_by: "system", id: "qwen3.8-max", object: "model" }, + { created: 1787731683, owned_by: "system", id: "qwen3.8-flash", object: "model" }, + ], + last_id: "model-id-11", + has_more: false, + object: "list", + }; + const models = config.parseResponse(live) as Array<{ id: string }>; + assert.equal(models.length, 2); + assert.deepEqual( + models.map((m) => m.id), + ["qwen3.8-max", "qwen3.8-flash"] + ); +});