From 3ac697adc73bb2cb89fc983f873a686db1ea276c Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 19 May 2026 14:49:03 +0300 Subject: [PATCH] fix: remove duplicate Zhipu usage provider Keeps one Zhipu AI Coding Plan entry in usage settings Preserves existing Zhipu aliases and config lookup Removes duplicate backend quota registration --- packages/ui/src/lib/quota/providers/index.ts | 1 - packages/ui/src/types/quota.ts | 3 +- .../web/server/lib/quota/DOCUMENTATION.md | 3 +- .../web/server/lib/quota/providers/index.js | 9 +- .../quota/providers/zhipuai-coding-plan.js | 34 +++++- .../web/server/lib/quota/providers/zhipuai.js | 114 ------------------ 6 files changed, 31 insertions(+), 133 deletions(-) delete mode 100644 packages/web/server/lib/quota/providers/zhipuai.js diff --git a/packages/ui/src/lib/quota/providers/index.ts b/packages/ui/src/lib/quota/providers/index.ts index bcb942d1..e191950f 100644 --- a/packages/ui/src/lib/quota/providers/index.ts +++ b/packages/ui/src/lib/quota/providers/index.ts @@ -18,7 +18,6 @@ export const QUOTA_PROVIDERS: QuotaProviderMeta[] = [ { id: 'minimax-cn-coding-plan', name: 'MiniMax Coding Plan (minimaxi.com)' }, { id: 'minimax-coding-plan', name: 'MiniMax Coding Plan (minimax.io)' }, { id: 'ollama-cloud', name: 'Ollama Cloud' }, - { id: 'zhipuai-coding-plan', name: 'ZhipuAI' }, ]; export const QUOTA_PROVIDER_MAP = QUOTA_PROVIDERS.reduce< diff --git a/packages/ui/src/types/quota.ts b/packages/ui/src/types/quota.ts index 1ba803bb..362d37b5 100644 --- a/packages/ui/src/types/quota.ts +++ b/packages/ui/src/types/quota.ts @@ -12,8 +12,7 @@ export type QuotaProviderId = | 'zhipuai-coding-plan' | 'minimax-coding-plan' | 'minimax-cn-coding-plan' - | 'ollama-cloud' - | 'zhipuai-coding-plan'; + | 'ollama-cloud'; export interface UsageWindow { usedPercent: number | null; diff --git a/packages/web/server/lib/quota/DOCUMENTATION.md b/packages/web/server/lib/quota/DOCUMENTATION.md index d1006119..67b42d96 100644 --- a/packages/web/server/lib/quota/DOCUMENTATION.md +++ b/packages/web/server/lib/quota/DOCUMENTATION.md @@ -26,11 +26,10 @@ These provider IDs are currently dispatchable via `fetchQuotaForProvider(provide | `nano-gpt` | NanoGPT | `providers/nanogpt.js` | `nano-gpt`, `nanogpt`, `nano_gpt` | | `openrouter` | OpenRouter | `providers/openrouter.js` | `openrouter` | | `zai-coding-plan` | z.ai | `providers/zai.js` | `zai-coding-plan`, `zai`, `z.ai` | -| `zhipuai-coding-plan` | Zhipu AI Coding Plan | `providers/zhipuai-coding-plan.js` | `zhipuai-coding-plan` | +| `zhipuai-coding-plan` | Zhipu AI Coding Plan | `providers/zhipuai-coding-plan.js` | `zhipuai-coding-plan`, `zhipuai`, `zhipu` | | `minimax-coding-plan` | MiniMax Coding Plan (minimax.io) | `providers/minimax-coding-plan.js` | `minimax-coding-plan` | | `minimax-cn-coding-plan` | MiniMax Coding Plan (minimaxi.com) | `providers/minimax-cn-coding-plan.js` | `minimax-cn-coding-plan` | | `ollama-cloud` | Ollama Cloud | `providers/ollama-cloud.js` | Cookie file at `~/.config/ollama-quota/cookie` (raw session cookie string) | -| `zhipuai-coding-plan` | ZhipuAI | `providers/zhipuai.js` | `zhipuai-coding-plan`, `zhipuai`, `zhipu` | ## Internal-only provider module - `providers/openai.js` exists for logic parity/reuse but is intentionally not registered for dispatcher ID routing. diff --git a/packages/web/server/lib/quota/providers/index.js b/packages/web/server/lib/quota/providers/index.js index 55fe8cfe..cfb78e62 100644 --- a/packages/web/server/lib/quota/providers/index.js +++ b/packages/web/server/lib/quota/providers/index.js @@ -20,7 +20,6 @@ import * as zhipuaiCodingPlan from './zhipuai-coding-plan.js'; import * as minimaxCodingPlan from './minimax-coding-plan.js'; import * as minimaxCnCodingPlan from './minimax-cn-coding-plan.js'; import * as ollamaCloud from './ollama-cloud.js'; -import * as zhipuai from './zhipuai.js'; const registry = { claude: { @@ -100,12 +99,6 @@ const registry = { providerName: ollamaCloud.providerName, isConfigured: ollamaCloud.isConfigured, fetchQuota: ollamaCloud.fetchQuota - }, - 'zhipuai-coding-plan': { - providerId: zhipuai.providerId, - providerName: zhipuai.providerName, - isConfigured: zhipuai.isConfigured, - fetchQuota: zhipuai.fetchQuota } }; @@ -165,4 +158,4 @@ export const fetchNanoGptQuota = nanogpt.fetchQuota; export const fetchMinimaxCodingPlanQuota = minimaxCodingPlan.fetchQuota; export const fetchMinimaxCnCodingPlanQuota = minimaxCnCodingPlan.fetchQuota; export const fetchOllamaCloudQuota = ollamaCloud.fetchQuota; -export const fetchZhipuaiQuota = zhipuai.fetchQuota; +export const fetchZhipuaiQuota = zhipuaiCodingPlan.fetchQuota; diff --git a/packages/web/server/lib/quota/providers/zhipuai-coding-plan.js b/packages/web/server/lib/quota/providers/zhipuai-coding-plan.js index 82a0614e..fbc84606 100644 --- a/packages/web/server/lib/quota/providers/zhipuai-coding-plan.js +++ b/packages/web/server/lib/quota/providers/zhipuai-coding-plan.js @@ -26,6 +26,7 @@ * @property {Array<{modelCode: string, usage: number}>} [usageDetails] */ import { readAuthFile } from '../../opencode/auth.js'; +import { readConfigLayers } from '../../opencode/shared.js'; import { getAuthEntry, normalizeAuthEntry, @@ -37,18 +38,39 @@ import { export const providerId = 'zhipuai-coding-plan'; export const providerName = 'Zhipu AI Coding Plan'; -export const aliases = ['zhipuai-coding-plan']; +export const aliases = ['zhipuai-coding-plan', 'zhipuai', 'zhipu']; -export const isConfigured = () => { +function getApiKey() { const auth = readAuthFile(); const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); - return Boolean(entry?.key || entry?.token); + const apiKeyFromAuth = entry?.key ?? entry?.token; + + if (apiKeyFromAuth) { + return apiKeyFromAuth; + } + + try { + const { mergedConfig } = readConfigLayers(); + + for (const alias of aliases) { + const providerConfig = mergedConfig?.provider?.[alias]; + if (providerConfig?.options?.apiKey) { + return providerConfig.options.apiKey; + } + } + } catch { + // Ignore config read errors; the provider will be treated as not configured. + } + + return null; +} + +export const isConfigured = () => { + return Boolean(getApiKey()); }; export const fetchQuota = async () => { - const auth = readAuthFile(); - const entry = normalizeAuthEntry(getAuthEntry(auth, aliases)); - const apiKey = entry?.key ?? entry?.token; + const apiKey = getApiKey(); if (!apiKey) { return buildResult({ diff --git a/packages/web/server/lib/quota/providers/zhipuai.js b/packages/web/server/lib/quota/providers/zhipuai.js deleted file mode 100644 index fe236bd0..00000000 --- a/packages/web/server/lib/quota/providers/zhipuai.js +++ /dev/null @@ -1,114 +0,0 @@ -import { readAuthFile } from '../../opencode/auth.js'; -import { readConfigLayers } from '../../opencode/shared.js'; -import { - getAuthEntry, - normalizeAuthEntry, - buildResult, - toUsageWindow, - toNumber, - toTimestamp, - resolveWindowSeconds, - resolveWindowLabel, - normalizeTimestamp -} from '../utils/index.js'; - -export const providerId = 'zhipuai-coding-plan'; -export const providerName = 'ZhipuAI'; -export const aliases = ['zhipuai-coding-plan', 'zhipuai', 'zhipu']; - -function getApiKey() { - const auth = readAuthFile(); - const oldEntry = normalizeAuthEntry(getAuthEntry(auth, aliases)); - const apiKeyFromOld = oldEntry?.key ?? oldEntry?.token; - - if (apiKeyFromOld) { - return apiKeyFromOld; - } - - try { - const layers = readConfigLayers(); - const { mergedConfig } = layers; - - for (const alias of aliases) { - const providerConfig = mergedConfig?.provider?.[alias]; - if (providerConfig?.options?.apiKey) { - return providerConfig.options.apiKey; - } - } - } catch (error) { - // Ignore read errors - } - - return null; -} - -export const isConfigured = () => { - return Boolean(getApiKey()); -}; - -export const fetchQuota = async () => { - const apiKey = getApiKey(); - - if (!apiKey) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: false, - error: 'Not configured' - }); - } - - try { - const response = await fetch('https://open.bigmodel.cn/api/monitor/usage/quota/limit', { - method: 'GET', - headers: { - Authorization: `Bearer ${apiKey}`, - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: `API error: ${response.status}` - }); - } - - const payload = await response.json(); - const limits = Array.isArray(payload?.data?.limits) ? payload.data.limits : []; - const tokensLimit = limits.find((limit) => limit?.type === 'TOKENS_LIMIT'); - const windowSeconds = resolveWindowSeconds(tokensLimit); - const windowLabel = resolveWindowLabel(windowSeconds); - const resetAt = tokensLimit?.nextResetTime ? normalizeTimestamp(tokensLimit.nextResetTime) : null; - const usedPercent = typeof tokensLimit?.percentage === 'number' ? tokensLimit.percentage : null; - - const windows = {}; - if (tokensLimit) { - windows[windowLabel] = toUsageWindow({ - usedPercent, - windowSeconds, - resetAt - }); - } - - return buildResult({ - providerId, - providerName, - ok: true, - configured: true, - usage: { windows } - }); - } catch (error) { - return buildResult({ - providerId, - providerName, - ok: false, - configured: true, - error: error instanceof Error ? error.message : 'Request failed' - }); - } -};