mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
fix(providers): validate bailian-coding-plan against the Token Plan host (#10634)
* fix(providers): validate bailian-coding-plan against the Token Plan host The catalog entry is the personal Alibaba Token Plan, but the region map still resolved the retired Coding Plan hosts. #10290 moved only the open-sse registry (inference) to token-plan.ap-southeast-1.maas.aliyuncs.com, leaving the dashboard's key validation pointed at coding-intl.dashscope.aliyuncs.com. That host rejects Token Plan keys with 401, and validateBailianCodingPlanProvider maps 401/403 to "Invalid API key" — so adding a working key failed at the modal while the same key served inference fine. Verified live 2026-08-18 with a valid key: legacy host 401 invalid_api_key, Token Plan host 429 quota (auth OK). - point both regions of ALIBABA_PROVIDER_ENDPOINTS at the Token Plan hosts, matching what docs/providers/ALIBABA-QWEN-PROVIDER-FAMILIES.md already stated - keep the retired hosts recognized as presets, so connections saved with the old URL still follow the region selector instead of being pinned to a dead host - keep image/video generation on the DashScope AIGC hosts, which the Token Plan host does not serve - probe with a model this plan actually serves (qwen3-coder-plus was Coding Plan) * test(providers): compare parsed hostnames in the legacy-host guard CodeQL flags URL .includes() checks as js/incomplete-url-substring-sanitization. The guard is an assertion, not a sanitizer, but comparing new URL().hostname is strictly more precise anyway — same coverage, no substring pattern. --------- Co-authored-by: Xiangzhe <bakryun0718@proton.me>
This commit is contained in:
committed by
GitHub
parent
cd091ab878
commit
c164ed962b
@@ -32,7 +32,7 @@ innych rodzin endpointów, więc wszystkie cztery produkty pozostają osobnymi I
|
||||
| Rodzina providera | `global-sg` | `china-beijing` | Format wire |
|
||||
| ----------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------- | ----------- |
|
||||
| `alibaba` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | OpenAI |
|
||||
| `bailian-coding-plan` | `https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1` | `https://coding.dashscope.aliyuncs.com/apps/anthropic/v1` | Anthropic |
|
||||
| `bailian-coding-plan` | `https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1` | `https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic/v1` | Anthropic |
|
||||
| `qwen-cloud` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | OpenAI |
|
||||
| `qwen-cloud-token-plan` | `https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1` | `https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1` | OpenAI |
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ export const CONFIGURABLE_BASE_URL_PROVIDERS = new Set([
|
||||
export const DEFAULT_PROVIDER_BASE_URLS: Record<string, string> = {
|
||||
"azure-openai": "https://example-resource.openai.azure.com",
|
||||
"azure-ai": "https://example-resource.services.ai.azure.com/openai/v1",
|
||||
"bailian-coding-plan": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"bailian-coding-plan": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
"xiaomi-mimo": "https://token-plan-sgp.xiaomimimo.com/v1",
|
||||
siliconflow: "https://api.siliconflow.com/v1",
|
||||
"searxng-search": "http://localhost:8888/search",
|
||||
|
||||
@@ -294,7 +294,9 @@ export async function validateBailianCodingPlanProvider({
|
||||
providerSpecificData
|
||||
),
|
||||
body: JSON.stringify({
|
||||
model: "qwen3-coder-plus",
|
||||
// qwen3-coder-plus belonged to the retired Coding Plan host and is absent from
|
||||
// BAILIAN_CODING_PLAN_MODELS; probe with a model this plan actually serves.
|
||||
model: providerSpecificData.validationModelId || "qwen3.7-max",
|
||||
max_tokens: 1,
|
||||
messages: [{ role: "user", content: "test" }],
|
||||
}),
|
||||
|
||||
@@ -11,9 +11,14 @@ export const ALIBABA_PROVIDER_ENDPOINTS: Readonly<
|
||||
"global-sg": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
||||
"china-beijing": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
},
|
||||
// The catalog entry is the personal TOKEN Plan (see providers/apikey/regional.ts:
|
||||
// name "Alibaba Token Plan"). The legacy coding-intl/coding hosts serve the separate
|
||||
// Coding Plan product and reject Token Plan keys with 401 invalid_api_key — verified
|
||||
// live 2026-08-18 against the same key that returns 429 (quota) on the host below.
|
||||
// Keeps /apps/anthropic/v1 because the registry entry is format "claude".
|
||||
"bailian-coding-plan": {
|
||||
"global-sg": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"china-beijing": "https://coding.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"global-sg": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
"china-beijing": "https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
"qwen-cloud": {
|
||||
"global-sg": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
||||
@@ -74,11 +79,48 @@ function normalizeEndpoint(value: string): string {
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Preset hosts this family used to ship. They must keep counting as presets: a connection
|
||||
* saved while a preset was current carries that URL in providerSpecificData.baseUrl, and if
|
||||
* a retired preset were mistaken for a deliberate custom URL the connection would stay
|
||||
* pinned to a host that no longer accepts its key, deaf to the region selector.
|
||||
*/
|
||||
const LEGACY_FAMILY_PRESETS: Readonly<Record<AlibabaProviderFamily, readonly string[]>> = {
|
||||
alibaba: [],
|
||||
// Retired 2026-08-18 — Coding Plan hosts, wrong product for this Token Plan entry.
|
||||
"bailian-coding-plan": [
|
||||
"https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"https://coding.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
],
|
||||
"qwen-cloud": [],
|
||||
"qwen-cloud-token-plan": [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Media (AIGC) roots, when they differ from the chat root.
|
||||
*
|
||||
* Only bailian-coding-plan diverges: its CHAT traffic moved to the Token Plan host
|
||||
* (2026-08), but image/video generation keeps running on the DashScope AIGC service
|
||||
* (`/api/v1/services/aigc/…`) — see imageRegistry.ts / videoRegistry.ts, which pin those
|
||||
* hosts literally. Deriving media from the chat root would have silently repointed every
|
||||
* Bailian image/video call at a host that does not serve AIGC.
|
||||
*/
|
||||
const ALIBABA_PROVIDER_MEDIA_OVERRIDES: Partial<
|
||||
Record<AlibabaProviderFamily, Readonly<Record<AlibabaProviderRegion, string>>>
|
||||
> = {
|
||||
"bailian-coding-plan": {
|
||||
"global-sg": "https://coding-intl.dashscope.aliyuncs.com/api/v1",
|
||||
"china-beijing": "https://coding.dashscope.aliyuncs.com/api/v1",
|
||||
},
|
||||
};
|
||||
|
||||
function isFamilyPresetUrl(family: AlibabaProviderFamily, value: string): boolean {
|
||||
const normalized = normalizeEndpoint(value);
|
||||
return ALIBABA_PROVIDER_REGION_VALUES.some(
|
||||
const isCurrentPreset = ALIBABA_PROVIDER_REGION_VALUES.some(
|
||||
(region) => normalizeEndpoint(ALIBABA_PROVIDER_ENDPOINTS[family][region]) === normalized
|
||||
);
|
||||
if (isCurrentPreset) return true;
|
||||
return LEGACY_FAMILY_PRESETS[family].some((preset) => normalizeEndpoint(preset) === normalized);
|
||||
}
|
||||
|
||||
export function isAlibabaRegionalProvider(providerId: string | null | undefined): boolean {
|
||||
@@ -167,6 +209,22 @@ export function resolveAlibabaProviderMediaBaseUrl(
|
||||
providerSpecificData?: unknown,
|
||||
fallback = ""
|
||||
): string {
|
||||
const family = canonicalProviderFamily(providerId);
|
||||
const data = asRecord(providerSpecificData);
|
||||
const configuredBaseUrl =
|
||||
typeof data.baseUrl === "string" && data.baseUrl.trim() ? data.baseUrl.trim() : "";
|
||||
const mediaOverride = family ? ALIBABA_PROVIDER_MEDIA_OVERRIDES[family] : undefined;
|
||||
|
||||
// A custom base URL still drives media, as before — the override only replaces the
|
||||
// preset-derived host.
|
||||
if (
|
||||
family &&
|
||||
mediaOverride &&
|
||||
(!configuredBaseUrl || isFamilyPresetUrl(family, configuredBaseUrl))
|
||||
) {
|
||||
return mediaOverride[resolveAlibabaProviderRegion(providerId, data)];
|
||||
}
|
||||
|
||||
return stripTrailingSlashes(
|
||||
resolveAlibabaProviderBaseUrl(providerId, providerSpecificData, fallback).trim()
|
||||
)
|
||||
|
||||
@@ -38,7 +38,8 @@ export const PROVIDER_ENDPOINTS = {
|
||||
helixmind: "https://helixmind.online/v1/chat/completions",
|
||||
glm: "https://api.z.ai/api/anthropic/v1/messages",
|
||||
glmt: "https://api.z.ai/api/anthropic/v1/messages",
|
||||
"bailian-coding-plan": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
"bailian-coding-plan":
|
||||
"https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
"qwen-cloud": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions",
|
||||
"qwen-cloud-token-plan":
|
||||
"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1/chat/completions",
|
||||
|
||||
@@ -3,7 +3,8 @@ import { gotoDashboardRoute } from "./helpers/dashboardAuth";
|
||||
|
||||
// #7882 replaced this provider's free-text Base URL field with a region step:
|
||||
// the endpoint is now derived from the choice ("global-sg" ->
|
||||
// coding-intl.dashscope.aliyuncs.com, "china-beijing" -> coding.dashscope.aliyuncs.com,
|
||||
// token-plan.ap-southeast-1.maas.aliyuncs.com, "china-beijing" ->
|
||||
// token-plan.cn-beijing.maas.aliyuncs.com,
|
||||
// see src/shared/constants/alibabaProviderRegions.ts), so the modal persists
|
||||
// providerSpecificData.region instead of a baseUrl. A per-connection base-URL
|
||||
// override still exists, but it moved to Advanced in the edit-connection modal.
|
||||
@@ -120,7 +121,7 @@ test.describe("Bailian Coding Plan Provider", () => {
|
||||
// free-text Base URL field, which #7882 removed for this provider — an invalid
|
||||
// URL is no longer reachable from this modal. Replaced with the other half of
|
||||
// the region contract: the China-mainland choice must persist as typed, since
|
||||
// that is what selects the coding.dashscope.aliyuncs.com endpoint.
|
||||
// that is what selects the token-plan.cn-beijing.maas.aliyuncs.com endpoint.
|
||||
test("region step persists the China-mainland (Beijing) choice", async ({ page }) => {
|
||||
const capturedPayloads: { createProvider?: Record<string, unknown> } = {};
|
||||
|
||||
@@ -222,6 +223,8 @@ test.describe("Bailian Coding Plan Provider", () => {
|
||||
expect(capturedPayloads.createProvider).toBeDefined();
|
||||
const payload = capturedPayloads.createProvider;
|
||||
expect(payload?.providerSpecificData).toBeDefined();
|
||||
expect((payload?.providerSpecificData as Record<string, unknown>)?.region).toBe("china-beijing");
|
||||
expect((payload?.providerSpecificData as Record<string, unknown>)?.region).toBe(
|
||||
"china-beijing"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,8 +24,8 @@ test("Alibaba-family endpoint matrix keeps product and region boundaries distinc
|
||||
"china-beijing": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
},
|
||||
"bailian-coding-plan": {
|
||||
"global-sg": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"china-beijing": "https://coding.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
"global-sg": "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
"china-beijing": "https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
"qwen-cloud": {
|
||||
"global-sg": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
|
||||
@@ -92,7 +92,7 @@ test("DefaultExecutor applies the regional endpoint to normal requests", () => {
|
||||
codingPlan.buildUrl("qwen3.7-plus", true, 0, {
|
||||
providerSpecificData: { region: "china-beijing" },
|
||||
}),
|
||||
"https://coding.dashscope.aliyuncs.com/apps/anthropic/v1/messages"
|
||||
"https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic/v1/messages"
|
||||
);
|
||||
|
||||
const qwenCloud = new DefaultExecutor("qwen-cloud");
|
||||
@@ -133,7 +133,11 @@ test("provider validation probes the selected Coding Plan region", async () => {
|
||||
},
|
||||
});
|
||||
assert.equal(result.valid, true);
|
||||
assert.deepEqual(urls, ["https://coding.dashscope.aliyuncs.com/apps/anthropic/v1/messages"]);
|
||||
// The stored URL is a RETIRED preset, so it must not pin the connection: the
|
||||
// china-beijing selector still wins and routes to the Token Plan CN host.
|
||||
assert.deepEqual(urls, [
|
||||
"https://token-plan.cn-beijing.maas.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
]);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ test("bailian-coding-plan not in OAUTH_PROVIDERS", () => {
|
||||
});
|
||||
|
||||
// Schema validation tests for providerSpecificData.baseUrl
|
||||
const VALID_BAILIAN_URL = "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1";
|
||||
const VALID_BAILIAN_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1";
|
||||
|
||||
test("createProviderSchema accepts valid baseUrl in providerSpecificData", () => {
|
||||
const validation = validateBody(createProviderSchema, {
|
||||
@@ -427,7 +427,7 @@ test("validateProviderApiKey returns invalid for 401 response (bailian-coding-pl
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "invalid-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -452,7 +452,7 @@ test("validateProviderApiKey returns invalid for 403 response (bailian-coding-pl
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "forbidden-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -479,7 +479,7 @@ test("validateProviderApiKey returns valid for 400 response (bailian-coding-plan
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "valid-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -508,7 +508,7 @@ test("validateProviderApiKey returns valid for 200 response (bailian-coding-plan
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "valid-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -533,7 +533,7 @@ test("validateProviderApiKey returns invalid for 500 response (bailian-coding-pl
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "bad-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -561,7 +561,7 @@ test("validateProviderApiKey avoids double /messages suffix for bailian-coding-p
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "valid-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -569,7 +569,7 @@ test("validateProviderApiKey avoids double /messages suffix for bailian-coding-p
|
||||
assert.equal(urls.length, 1);
|
||||
assert.equal(
|
||||
urls[0],
|
||||
"https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
"https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
"Should probe exactly one /messages suffix"
|
||||
);
|
||||
} finally {
|
||||
@@ -588,7 +588,7 @@ test("POST /api/providers validation: bailian-coding-plan with baseUrl passes sc
|
||||
apiKey: "sk-placeholder-key",
|
||||
name: "Test Bailian Provider",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -597,7 +597,7 @@ test("POST /api/providers validation: bailian-coding-plan with baseUrl passes sc
|
||||
assert.equal(validation.data.provider, "bailian-coding-plan");
|
||||
assert.equal(
|
||||
validation.data.providerSpecificData?.baseUrl,
|
||||
"https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1"
|
||||
"https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
102
tests/unit/bailian-token-plan-endpoint-parity.test.ts
Normal file
102
tests/unit/bailian-token-plan-endpoint-parity.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* bailian-coding-plan ("Alibaba Token Plan") pointed inference and validation at two
|
||||
* DIFFERENT hosts.
|
||||
*
|
||||
* #10290 moved the open-sse registry to the Token Plan host, but the dashboard's key
|
||||
* validation resolves its URL through ALIBABA_PROVIDER_REGION_ENDPOINTS, which still held
|
||||
* the legacy Coding Plan host. Verified live 2026-08-18 with a valid Token Plan key:
|
||||
*
|
||||
* coding-intl.dashscope.aliyuncs.com → 401 invalid_api_key
|
||||
* token-plan.ap-southeast-1.maas... → 429 Throttling.AllocationQuota (auth OK)
|
||||
*
|
||||
* validateBailianCodingPlanProvider maps 401/403 to "Invalid API key", so a perfectly
|
||||
* good key was rejected at add-connection time while the very same key worked for
|
||||
* inference. These tests pin the two paths together.
|
||||
*/
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { REGISTRY } from "../../open-sse/config/providers/index.ts";
|
||||
import { PROVIDER_ENDPOINTS } from "../../src/shared/constants/config.ts";
|
||||
import { DEFAULT_PROVIDER_BASE_URLS } from "../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts";
|
||||
import {
|
||||
ALIBABA_PROVIDER_ENDPOINTS,
|
||||
resolveAlibabaProviderBaseUrl,
|
||||
} from "../../src/shared/constants/alibabaProviderRegions.ts";
|
||||
|
||||
const LEGACY_CODING_PLAN_HOST = "coding-intl.dashscope.aliyuncs.com";
|
||||
|
||||
test("validation resolves the same host the inference registry dispatches to", () => {
|
||||
const registryBaseUrl = REGISTRY["bailian-coding-plan"].baseUrl;
|
||||
const resolved = resolveAlibabaProviderBaseUrl("bailian-coding-plan", {
|
||||
region: "global-sg",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
resolved,
|
||||
registryBaseUrl,
|
||||
"the dashboard would validate the key against a different host than inference uses"
|
||||
);
|
||||
});
|
||||
|
||||
test("no default endpoint still points at the Coding Plan host", () => {
|
||||
// The catalog entry is a TOKEN Plan; Coding Plan keys are a different product and the
|
||||
// legacy host rejects Token Plan keys outright. Compare parsed hostnames, not URL
|
||||
// substrings (CodeQL js/incomplete-url-substring-sanitization).
|
||||
assert.notEqual(
|
||||
new URL(PROVIDER_ENDPOINTS["bailian-coding-plan"]).hostname,
|
||||
LEGACY_CODING_PLAN_HOST,
|
||||
"PROVIDER_ENDPOINTS still defaults to the legacy Coding Plan host"
|
||||
);
|
||||
assert.notEqual(
|
||||
new URL(DEFAULT_PROVIDER_BASE_URLS["bailian-coding-plan"]).hostname,
|
||||
LEGACY_CODING_PLAN_HOST,
|
||||
"the dashboard base-URL placeholder still shows the legacy Coding Plan host"
|
||||
);
|
||||
for (const region of ["global-sg", "china-beijing"] as const) {
|
||||
assert.notEqual(
|
||||
new URL(ALIBABA_PROVIDER_ENDPOINTS["bailian-coding-plan"][region]).hostname,
|
||||
LEGACY_CODING_PLAN_HOST,
|
||||
`region ${region} still maps to the legacy Coding Plan host`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("both regions keep the Anthropic-compatible path the claude format requires", () => {
|
||||
// format: "claude" + chatPath "/messages" — a compatible-mode URL here would 404.
|
||||
for (const region of ["global-sg", "china-beijing"] as const) {
|
||||
assert.ok(
|
||||
ALIBABA_PROVIDER_ENDPOINTS["bailian-coding-plan"][region].endsWith("/apps/anthropic/v1"),
|
||||
`region ${region} must keep the /apps/anthropic/v1 root`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("a saved legacy preset URL still follows the region selector", () => {
|
||||
// Migration guard: connections created before the fix carry the legacy host in
|
||||
// providerSpecificData.baseUrl. isFamilyPresetUrl must keep recognizing it as a
|
||||
// preset — otherwise it is treated as a deliberate custom URL and the connection
|
||||
// stays pinned to the host that rejects its key, with no way out but manual editing.
|
||||
const legacyPreset = "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1";
|
||||
|
||||
assert.equal(
|
||||
resolveAlibabaProviderBaseUrl("bailian-coding-plan", {
|
||||
region: "global-sg",
|
||||
baseUrl: legacyPreset,
|
||||
}),
|
||||
ALIBABA_PROVIDER_ENDPOINTS["bailian-coding-plan"]["global-sg"],
|
||||
"a stored legacy preset must not pin the connection to the dead host"
|
||||
);
|
||||
});
|
||||
|
||||
test("a genuinely custom base URL still wins over the region preset", () => {
|
||||
const custom = "https://my-gateway.internal/apps/anthropic/v1";
|
||||
assert.equal(
|
||||
resolveAlibabaProviderBaseUrl("bailian-coding-plan", {
|
||||
region: "global-sg",
|
||||
baseUrl: custom,
|
||||
}),
|
||||
custom
|
||||
);
|
||||
});
|
||||
@@ -220,10 +220,10 @@ test("DefaultExecutor.buildUrl normalizes configurable chat-openai-compat base U
|
||||
assert.equal(
|
||||
bailian.buildUrl("qwen3-coder-plus", true, 0, {
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1",
|
||||
},
|
||||
}),
|
||||
"https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages"
|
||||
"https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1/messages"
|
||||
);
|
||||
assert.equal(
|
||||
heroku.buildUrl("claude-4-sonnet", true, 0, {
|
||||
|
||||
@@ -1451,7 +1451,9 @@ test("specialty validators cover remaining status branches for Deepgram, Assembl
|
||||
if (target.match(/inworld/i)) {
|
||||
throw new Error("inworld offline");
|
||||
}
|
||||
if (target.match(/dashscope\.aliyuncs\.com/i)) {
|
||||
// Alibaba-family hosts: dashscope.aliyuncs.com (pay-as-you-go / AIGC) and
|
||||
// *.maas.aliyuncs.com (Token Plan).
|
||||
if (target.match(/(?:dashscope|maas)\.aliyuncs\.com/i)) {
|
||||
return new Response(JSON.stringify({ error: "server" }), { status: 500 });
|
||||
}
|
||||
if (target.match(/longcat/i)) {
|
||||
@@ -1468,7 +1470,7 @@ test("specialty validators cover remaining status branches for Deepgram, Assembl
|
||||
provider: "bailian-coding-plan",
|
||||
apiKey: "bailian-key",
|
||||
providerSpecificData: {
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
baseUrl: "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
},
|
||||
});
|
||||
const longcatInvalid = await validateProviderApiKey({ provider: "longcat", apiKey: "lc-key" });
|
||||
|
||||
Reference in New Issue
Block a user