fix(usage): console-aware Token Plan guidance + subscription hint on bailian 401 (#12288)

* fix(usage): console-aware Token Plan guidance and subscription hint on bailian 401

The personal Token Plan is sold through two consoles with different portals,
gateway hosts and login tickets. Two operator-facing messages ignored the split:

- The quota guidance always said 'get the cookie at home.qwencloud.com', even
  for connections served by the Alibaba Model Studio console — following it
  verbatim produces a cookie the gateway rejects (console mismatch →
  BailianGateway.Login.NotLogined). The guidance now derives the console from
  the provider via resolveConsoleSite, matching what the fetcher will do with
  the pasted cookie.
- Key validation mapped upstream 401 to a bare 'Invalid API key'. An expired
  Token Plan subscription produces the exact same upstream 401 (observed live
  2026-09-01: subscription ended 08-23, the working key started failing), so
  the message now names the subscription as a cause worth checking.

* test(providers): align the remaining bailian 401/403 message pins to prefix match

search-provider-validation.test.ts pinned the exact 'Invalid API key' string for
the bailian validator; the message now also names an expired Token Plan
subscription. Same property asserted (401/403 => invalid), prefix match.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-09-01 11:40:40 -03:00
committed by GitHub
parent 9d04995950
commit accdfa9f33
5 changed files with 121 additions and 11 deletions

View File

@@ -11,6 +11,7 @@
import {
fetchQwenTokenPlanQuota,
resolveConsoleSite,
QWEN_TOKEN_PLAN_WINDOW_5H,
QWEN_TOKEN_PLAN_WINDOW_WEEKLY,
type QwenTokenPlanQuota,
@@ -54,13 +55,26 @@ export async function getQwenTokenPlanUsage(
});
if (!quota) {
// The same plan is sold through two consoles with different portals, gateway
// hosts and login tickets — instructions for the wrong console produce a cookie
// the gateway rejects (console mismatch → NotLogined). With no cookie stored the
// console is inferred from the provider id, same rule the fetcher applies.
const site = resolveConsoleSite("", provider);
const guide =
site.consoleSite === "ALIYUN"
? "Get it at modelstudio.console.alibabacloud.com (logged in): F12 Network, " +
"reload, filter by api.json, click a request to " +
"bailian-singapore-cs.alibabacloud.com and copy the whole Cookie value from " +
"Request Headers (it contains login_aliyunid_ticket)."
: "Get it at home.qwencloud.com Billing Subscription (logged in): F12 " +
"Network, reload, filter by api.json, click a request to " +
"cs-data.qwencloud.com and copy the whole Cookie value from Request Headers " +
"(it contains login_qwencloud_ticket).";
const brand = site.consoleSite === "ALIYUN" ? "Alibaba" : "Qwen";
return {
message:
"Qwen Token Plan connected. Quota needs a console session cookie — the inference " +
"API key cannot read it. Get it at home.qwencloud.com Billing Subscription " +
"(logged in): F12 Network, reload, filter by api.json, click a request to " +
"cs-data.qwencloud.com and copy the whole Cookie value from Request Headers " +
"(it contains login_qwencloud_ticket). Paste it into the connection's " +
`${brand} Token Plan connected. Quota needs a console session cookie — the ` +
`inference API key cannot read it. ${guide} Paste it into the connection's ` +
"'Qwen / Model Studio console cookie' field, or set QWEN_CLOUD_COOKIE. " +
"The cookie expires with the browser session — re-paste it when this message returns.",
};

View File

@@ -302,9 +302,16 @@ export async function validateBailianCodingPlanProvider({
}),
});
// 401/403 => invalid key
// 401/403 => invalid key. An expired/lapsed Token Plan subscription yields the
// exact same upstream 401 invalid_api_key (observed live 2026-09-01: subscription
// ended 08-23, the previously working key started failing), so name it as a cause.
if (response.status === 401 || response.status === 403) {
return { valid: false, error: "Invalid API key" };
return {
valid: false,
error:
"Invalid API key — or the Token Plan subscription is expired/inactive; " +
"check it in the Model Studio console",
};
}
// Non-auth 4xx (e.g., 400 bad request) means auth passed but request was malformed

View File

@@ -432,7 +432,11 @@ test("validateProviderApiKey returns invalid for 401 response (bailian-coding-pl
});
assert.equal(result.valid, false, "Should return invalid for 401");
assert.equal(result.error, "Invalid API key", "Error should be 'Invalid API key'");
assert.match(
String(result.error),
/^Invalid API key/,
"Error should start with 'Invalid API key'"
);
} finally {
globalThis.fetch = originalFetch;
}
@@ -457,7 +461,11 @@ test("validateProviderApiKey returns invalid for 403 response (bailian-coding-pl
});
assert.equal(result.valid, false, "Should return invalid for 403");
assert.equal(result.error, "Invalid API key", "Error should be 'Invalid API key'");
assert.match(
String(result.error),
/^Invalid API key/,
"Error should start with 'Invalid API key'"
);
} finally {
globalThis.fetch = originalFetch;
}

View File

@@ -133,7 +133,9 @@ test("bailian-coding-plan validation rejects 401 as invalid key", async () => {
});
assert.equal(result.valid, false);
assert.equal(result.error, "Invalid API key");
// Prefix match: the message now also names an expired Token Plan subscription,
// which yields the identical upstream 401/403.
assert.match(String(result.error), /^Invalid API key/);
} finally {
globalThis.fetch = originalFetch;
}
@@ -155,7 +157,9 @@ test("bailian-coding-plan validation rejects 403 as invalid key", async () => {
});
assert.equal(result.valid, false);
assert.equal(result.error, "Invalid API key");
// Prefix match: the message now also names an expired Token Plan subscription,
// which yields the identical upstream 401/403.
assert.match(String(result.error), /^Invalid API key/);
} finally {
globalThis.fetch = originalFetch;
}

View File

@@ -0,0 +1,77 @@
/**
* The personal Token Plan is sold through two consoles (QwenCloud and Alibaba Model
* Studio) with different hosts, gateways and login tickets. Two operator-facing
* messages ignored that split (both bit the operator in the 2026-08/09 audits):
*
* 1. The quota guidance always said "get the cookie at home.qwencloud.com", even for
* connections served by the Alibaba console — whose cookie comes from
* modelstudio.console.alibabacloud.com and carries login_aliyunid_ticket. Following
* the instructions verbatim produced a cookie the gateway rejects (console
* mismatch → BailianGateway.Login.NotLogined).
* 2. Key validation mapped upstream 401 to a bare "Invalid API key". For Token Plan
* keys, an expired/lapsed subscription produces the exact same upstream 401
* (observed live 2026-09-01: subscription ended 08-23, key started failing), so
* the message must point at the subscription as a cause worth checking.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { getQwenTokenPlanUsage } from "../../open-sse/services/usage/qwen-token-plan.ts";
import { validateProviderApiKey } from "../../src/lib/providers/validation.ts";
test("cookie guidance points Alibaba-console connections at the Model Studio console", async () => {
const result = await getQwenTokenPlanUsage(
"conn-guidance-aliyun",
"sk-sp-any",
{},
"bailian-coding-plan"
);
assert.ok("message" in result, "no cookie stored → guidance message expected");
const message = (result as { message: string }).message;
assert.match(message, /modelstudio\.console\.alibabacloud\.com/);
assert.match(message, /bailian-singapore-cs\.alibabacloud\.com/);
assert.match(message, /login_aliyunid_ticket/);
assert.doesNotMatch(
message,
/login_qwencloud_ticket/,
"Alibaba guidance must not tell the operator to hunt for the QwenCloud ticket"
);
});
test("cookie guidance keeps the QwenCloud instructions for the QwenCloud console", async () => {
const result = await getQwenTokenPlanUsage(
"conn-guidance-qwen",
"sk-sp-any",
{},
"qwen-cloud-token-plan"
);
assert.ok("message" in result, "no cookie stored → guidance message expected");
const message = (result as { message: string }).message;
assert.match(message, /home\.qwencloud\.com/);
assert.match(message, /cs-data\.qwencloud\.com/);
assert.match(message, /login_qwencloud_ticket/);
});
test("bailian 401 mentions the subscription as a possible cause", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(JSON.stringify({ error: { code: "invalid_api_key" } }), { status: 401 });
try {
const result = await validateProviderApiKey({
provider: "bailian-coding-plan",
apiKey: "sk-sp-expired-subscription",
});
assert.equal(result.valid, false);
assert.match(String(result.error), /Invalid API key/);
assert.match(
String(result.error),
/subscription/i,
"an expired Token Plan subscription yields the same upstream 401 — say so"
);
} finally {
globalThis.fetch = originalFetch;
}
});