Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs-site/src/content/docs/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,12 @@ Select **Zhipu AI — BigModel Coding Plan (Responses)** (`zhipu-bigmodel-respon
for the `openai-responses` endpoint `https://open.bigmodel.cn/api/v1`. This is separate
from `zhipu-bigmodel-coding`, which uses Chat Completions at `/api/coding/paas/v4`.

Both Coding Plan presets use the existing domestic quota monitor with the raw API key
in `Authorization` and refuse redirects. A custom noncanonical destination remains
unsupported by that quota probe. Quota-reader support does not expand the Responses
model roster or establish Flash/model-discovery support; those require separate
endpoint-specific evidence.

The preset uses a **static roster** (`liveModels: false`) taken from the
[official BigModel Codex example](https://docs.bigmodel.cn/cn/coding-plan/tool/codex.md):

Expand Down
3 changes: 2 additions & 1 deletion src/providers/quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2906,7 +2906,8 @@ function keyQuotaReaderForProvider(name: string, provider: OcxProviderConfig): K
if (name === "deepseek" && isCanonicalDeepSeekBaseUrl(provider.baseUrl)) return fetchDeepSeekQuota;
if (name === "cline-pass" && isCanonicalClineBaseUrl(provider.baseUrl)) return fetchClineQuota;
if (isCanonicalOllamaCloudBaseUrl(provider.baseUrl ?? getProviderRegistryEntry(name)?.baseUrl)) return fetchOllamaCloudQuota;
if (["zai", "glm", "glm-cn", "zhipu-bigmodel-coding"].includes(name) && isCanonicalZaiBaseUrl(provider.baseUrl)) return fetchZaiQuota;
// The domestic Responses preset uses the same Coding Plan monitor, not model discovery.
if (["zai", "glm", "glm-cn", "zhipu-bigmodel-coding", "zhipu-bigmodel-responses"].includes(name) && isCanonicalZaiBaseUrl(provider.baseUrl)) return fetchZaiQuota;
if (["minimax", "minimax-cn"].includes(name) && isCanonicalMinimaxBaseUrl(provider.baseUrl)) return fetchMinimaxQuota;
if (name === "moonshot" && isCanonicalMoonshotBaseUrl(provider.baseUrl)) return fetchMoonshotQuota;
if (name === "venice" && isCanonicalVeniceBaseUrl(provider.baseUrl)) return fetchVeniceQuota;
Expand Down
30 changes: 23 additions & 7 deletions tests/providers/provider-quota.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1077,12 +1077,12 @@ describe("fetchProviderQuotaReports", () => {
expect(seen[0]?.url).toBe("https://api.z.ai/api/monitor/usage/quota/limit");
});

test("Z.AI quota probes the BigModel Responses endpoint at /api/v1", async () => {
const seen: Array<{ url: string; authorization?: string }> = [];
test.each(["zhipu-bigmodel-coding", "zhipu-bigmodel-responses"])("Z.AI quota probes BigModel /api/v1 for %s", async name => {
const seen: Array<{ url: string; authorization?: string; redirect?: RequestRedirect }> = [];
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
const headers = init?.headers as Record<string, string> | undefined;
seen.push({ url, authorization: headers?.Authorization });
seen.push({ url, authorization: headers?.Authorization, redirect: init?.redirect });
return new Response(JSON.stringify({
success: true,
data: {
Expand All @@ -1095,10 +1095,9 @@ describe("fetchProviderQuotaReports", () => {
}), { status: 200 });
}) as typeof fetch;

const result = await fetchProviderQuotaReports(
keyQuotaConfig("zhipu-bigmodel-coding", "https://open.bigmodel.cn/api/v1", "zai-secret"),
true,
);
const config = keyQuotaConfig(name, "https://open.bigmodel.cn/api/v1", "zai-secret");
config.providers[name]!.adapter = name === "zhipu-bigmodel-responses" ? "openai-responses" : "openai-chat";
const result = await fetchProviderQuotaReports(config, true);

expect(result.reports).toHaveLength(1);
expect(result.reports[0]?.source).toBe("zai:quota-limit");
Expand All @@ -1110,6 +1109,23 @@ describe("fetchProviderQuotaReports", () => {
expect(seen).toHaveLength(1);
expect(seen[0]?.url).toBe("https://open.bigmodel.cn/api/monitor/usage/quota/limit");
expect(seen[0]?.authorization).toBe("zai-secret");
expect(seen[0]?.redirect).toBe("error");
});

test.each([
"https://gateway.example.test/api/v1",
"https://open.bigmodel.cn/api/paas/v4",
])("BigModel Responses quota does not probe unsupported destination %s", async baseUrl => {
let calls = 0;
globalThis.fetch = (async () => {
calls += 1;
return new Response("unexpected", { status: 500 });
}) as typeof fetch;
const config = keyQuotaConfig("zhipu-bigmodel-responses", baseUrl);
config.providers["zhipu-bigmodel-responses"]!.adapter = "openai-responses";

expect((await fetchProviderQuotaReports(config, true)).reports).toEqual([]);
expect(calls).toBe(0);
});

test("Z.AI quota treats an unsuccessful payload as a no-report", async () => {
Expand Down
Loading