diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 341d82220..a9c8e8758 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -24,6 +24,26 @@ Auth page can restore it: absent rows are created from the canonical preset, dis rows are re-enabled without replacing saved mode or model settings, and noncanonical `openai` rows are not offered that recovery path. +### Providers overview pool capacity + +For Codex login in Pool mode, the Providers overview shows a configured-weight estimate of the +pool's used capacity rather than presenting one arbitrary account as the provider total. The same +row also shows the current effective account's raw quota percentage, so you can distinguish the +pool estimate from the account that a new request would use. + +When reset information is available, the overview shows the next reset time and the capacity that +reset is expected to recover as `+N% pool capacity`. **Incomplete coverage** means one or more pool +accounts could not safely contribute to the estimate, for example because their plan or quota is +unknown, their reading is stale, or the account is paused or needs reauthentication. + +A **partial window coverage** warning means some included accounts reported one quota window but +not another. The overview keeps those windows separate and marks each affected window incomplete +instead of treating the missing reading as usage for that window. + +This estimate is display-only. It does not change account selection, session affinity, automatic +switching, cooldowns, or any other routing decision. Use the [Codex Auth account pool](/guides/web-dashboard/#codex-auth-and-account-pools) +for the individual account state and routing controls. + Shipped v1 configs migrate automatically to marker 2 and one option-aware row. The original config is retained once at `~/.opencodex/config.json.pre-openai-tiers-v2.bak`; restore it with `cp ~/.opencodex/config.json.pre-openai-tiers-v2.bak ~/.opencodex/config.json`. diff --git a/docs-site/src/content/docs/guides/web-dashboard.md b/docs-site/src/content/docs/guides/web-dashboard.md index a4198dbe5..04d74877c 100644 --- a/docs-site/src/content/docs/guides/web-dashboard.md +++ b/docs-site/src/content/docs/guides/web-dashboard.md @@ -100,6 +100,11 @@ catalog entry. The **Codex Auth** page manages the native ChatGPT/Codex route: +The Providers overview separately summarizes Pool-mode usage as a display-only weighted capacity +estimate, alongside the effective account's raw quota and the next capacity recovery. See +[Providers overview pool capacity](/guides/providers/#providers-overview-pool-capacity) for the +visible fields, incomplete-coverage meaning, and routing boundary. + - Manually choosing an account changes the next new Codex session; an already-bound thread keeps its current account for that manual switch. - Thread affinity prevents per-request flapping. With quota auto-switch enabled, a long-running diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index 2005aa329..98a5a0c57 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -20,6 +20,25 @@ max input 922,000 で `*-pro` virtual ID は公開状態を維持し、wire で 組み込み `openai` が欠落または無効な場合、ダッシュボードの Accounts ピッカーと Codex Auth から復元できます。欠落行は正規プリセットから作成され、正規の無効行は保存済みのモードやモデル設定を置き換えずに再有効化され、非正規の `openai` 行にはその復元経路は出ません。 +### プロバイダー概要のプール容量 + +Codex login を Pool モードで使うと、Providers の概要には任意の 1 アカウントではなく、 +プール全体の使用済み容量の推定値が表示されます。同じ行には現在の有効アカウントの +生のクォータ使用率も表示されるため、プールの推定値と次のリクエストで使われる +アカウントの状態を区別できます。 + +リセット情報がある場合は、次のリセット時刻と、その時点で回復するプール容量が表示されます。 +**対象範囲が不完全**という警告は、プランやクォータが不明、読み取りが古い、アカウントが +一時停止中、または再認証が必要などの理由で、安全に推定へ含められないアカウントがあることを示します。 + +**期間別の対象範囲が一部不完全**という警告は、含まれるアカウントの一部が、表示中の +クォータ期間のうち一部だけを報告したことを示します。概要では各期間を分けたまま、影響を受ける +期間を個別に不完全と表示し、欠けた値をその期間の使用量として扱いません。 + +この推定値は表示専用です。アカウント選択、セッション affinity、自動切り替え、cooldown、 +その他のルーティング判断には影響しません。個別アカウントの状態とルーティング設定は +[Codex Auth のアカウントプール](/ja/guides/web-dashboard/#codex-auth-and-account-pools)を参照してください。 + 出荷版 v1 config は marker 2 の単一オプション行に自動移行されます。オリジナルは `~/.opencodex/config.json.pre-openai-tiers-v2.bak` に一度保存され、次のコマンドで復元します: `cp ~/.opencodex/config.json.pre-openai-tiers-v2.bak ~/.opencodex/config.json`。 diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index f7cf96e29..582461491 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -20,6 +20,24 @@ max input 922,000이며 `*-pro` virtual id는 공개 상태에 유지되고 wire 내장 `openai` 제공자가 없거나 비활성화된 경우 대시보드 Accounts 선택기와 Codex Auth 페이지에서 복구할 수 있습니다. 없는 항목은 정규 프리셋으로 만들고, 비활성화된 정규 항목은 저장된 모드/모델 설정을 바꾸지 않고 다시 켜며, 비정규 `openai` 항목에는 그 복구 경로를 제공하지 않습니다. +### 프로바이더 개요의 풀 용량 + +Codex 로그인을 Pool 모드로 사용하면 Providers 개요에는 임의의 한 계정이 아니라 풀 전체의 +사용 용량 추정치가 표시됩니다. 같은 행에는 현재 유효 계정의 원본 quota 사용률도 표시되므로, +풀 추정치와 다음 요청에서 사용할 계정의 상태를 구분할 수 있습니다. + +리셋 정보가 있으면 다음 리셋 시각과 그때 회복되는 풀 용량을 표시합니다. **불완전한 범위**는 +요금제나 quota를 알 수 없거나, 측정값이 오래되었거나, 계정이 일시 중지되었거나 재인증이 필요한 +등의 이유로 일부 계정을 안전하게 추정치에 포함하지 못했음을 뜻합니다. + +**일부 기간의 범위가 불완전함**은 포함된 계정 중 일부가 표시된 quota 기간을 모두 보고하지 +않았음을 뜻합니다. 개요는 각 기간을 서로 분리한 채 영향을 받은 기간을 개별적으로 불완전하다고 +표시하며, 누락된 값을 해당 기간의 사용량으로 간주하지 않습니다. + +이 추정치는 표시 전용입니다. 계정 선택, 세션 affinity, 자동 전환, cooldown 또는 다른 라우팅 +판단을 변경하지 않습니다. 개별 계정 상태와 라우팅 제어는 +[Codex Auth 계정 풀](/ko/guides/web-dashboard/#codex-auth-and-account-pools)을 참고하세요. + shipped v1 config는 marker 2의 단일 옵션 행으로 자동 이관됩니다. 원본은 `~/.opencodex/config.json.pre-openai-tiers-v2.bak`에 한 번 보존되며 다음 명령으로 복원합니다: `cp ~/.opencodex/config.json.pre-openai-tiers-v2.bak ~/.opencodex/config.json`. diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index 1ed4d5cbc..6476a60f8 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -25,6 +25,27 @@ description: Все способы, которыми opencodex аутентиф канонические записи включаются без замены сохранённого режима и настроек моделей, а неканонические записи `openai` этот путь восстановления не получают. +### Ёмкость пула в обзоре провайдеров + +Для входа Codex в режиме Pool обзор Providers показывает оценку использованной ёмкости всего пула, +а не показатель произвольного аккаунта. В той же строке отображается исходный процент квоты текущего +активного аккаунта, поэтому оценку пула можно отличить от состояния аккаунта, который будет использован +для следующего запроса. + +Когда доступны сведения о сбросе, обзор показывает время следующего сброса и ёмкость пула, которая +восстановится в этот момент. **Неполное покрытие** означает, что некоторые аккаунты нельзя безопасно +включить в оценку, например из-за неизвестного плана или квоты, устаревшего показания, приостановки +аккаунта либо необходимости повторной аутентификации. + +Предупреждение **о частичном покрытии окон** означает, что некоторые включённые аккаунты сообщили данные +только для части показанных окон квоты. Обзор сохраняет окна раздельными, отмечает каждое затронутое +окно как неполное и не считает отсутствующее значение использованием в этом окне. + +Эта оценка предназначена только для отображения. Она не меняет выбор аккаунта, привязку сессии, +автоматическое переключение, cooldown или другие решения маршрутизации. Состояние отдельных аккаунтов +и настройки маршрутизации описаны в разделе +[пула аккаунтов Codex Auth](/ru/guides/web-dashboard/#codex-auth-and-account-pools). + Поставляемые v1-конфигурации автоматически мигрируют на маркер 2 и одну строку с поддержкой опций. Исходная конфигурация один раз сохраняется в `~/.opencodex/config.json.pre-openai-tiers-v2.bak`; восстановить её можно командой diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index 6e6e4796f..e78a29ae8 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -19,6 +19,22 @@ bare `gpt-5.6-sol` 遵循 Providers 页面中的 Pool/Direct 选项, 若内置 `openai` 提供商缺失或已禁用,可在仪表盘 Accounts 选择器或 Codex Auth 页面恢复:缺失行会从规范预设创建,已禁用的规范行会在不替换已保存模式/模型设置的情况下重新启用,非规范的 `openai` 行不会提供该恢复路径。 +### 提供商概览中的账户池容量 + +Codex 登录使用 Pool 模式时,Providers 概览显示整个账户池的已用容量估算,而不是任意一个 +账户的数值。同一行还会显示当前有效账户的原始配额使用率,便于区分账户池估算与下一次请求 +将使用的账户状态。 + +如果有重置信息,概览会显示下一次重置时间以及届时恢复的账户池容量。**覆盖不完整**表示某些 +账户无法安全计入估算,例如套餐或配额未知、读数过旧、账户已暂停或需要重新认证。 + +**部分窗口覆盖不完整**表示某些已计入账户只报告了部分显示的配额窗口。概览会保持各窗口相互 +独立,逐一标记受影响的窗口,并且不会把缺失值当作该窗口的使用量。 + +此估算仅用于显示,不会改变账户选择、会话关联、自动切换、cooldown 或任何其他路由决策。 +各账户状态和路由控制请参阅 +[Codex Auth 账户池](/zh-cn/guides/web-dashboard/#codex-auth-and-account-pools)。 + shipped v1 配置自动迁移到 marker 2 的单一选项行。原配置只保留一次到 `~/.opencodex/config.json.pre-openai-tiers-v2.bak`;恢复命令: `cp ~/.opencodex/config.json.pre-openai-tiers-v2.bak ~/.opencodex/config.json`。 diff --git a/gui/src/components/QuotaBars.tsx b/gui/src/components/QuotaBars.tsx index 8669deafb..e90f40a1c 100644 --- a/gui/src/components/QuotaBars.tsx +++ b/gui/src/components/QuotaBars.tsx @@ -7,7 +7,15 @@ import { type AccountQuota, normalizeQuotaForPlan } from "../codex-quota-utils"; /* Helpers are co-located with QuotaBars for overview sorting / stacked layout. */ /* eslint-disable react-refresh/only-export-components */ -export type QuotaBarRow = { label: string; limitLabel: string; percent: number; resetAt?: number }; +export type QuotaWindowKey = "fiveHour" | "weekly" | "monthly"; +export type QuotaBarRow = { + windowKey?: QuotaWindowKey; + customLabel?: string; + label: string; + limitLabel: string; + percent: number; + resetAt?: number; +}; /** * Window ordering is computed from RAW wire identities BEFORE localization @@ -43,6 +51,7 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | ranked.push({ rank: 0, row: { + windowKey: "fiveHour", label: t("codexAuth.fiveHour"), limitLabel: t("quota.fiveHourLimit"), percent: displayQuota.fiveHourPercent, @@ -54,6 +63,7 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | ranked.push({ rank: 1, row: { + windowKey: "weekly", label: t("codexAuth.weekly"), limitLabel: t("quota.weeklyLimit"), percent: displayQuota.weeklyPercent, @@ -65,6 +75,7 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | ranked.push({ rank: 4, row: { + windowKey: "monthly", label: t("codexAuth.monthly"), limitLabel: t("quota.monthlyLimit"), percent: displayQuota.monthlyPercent, @@ -76,7 +87,13 @@ export function buildQuotaRows(quota: AccountQuota | null, plan: string | null | const localized = localizeCustomQuotaLabel(w.label, t); ranked.push({ rank: rawCustomWindowRank(w.label), - row: { label: localized, limitLabel: localized, percent: w.percent, resetAt: w.resetAt }, + row: { + customLabel: w.label, + label: localized, + limitLabel: localized, + percent: w.percent, + resetAt: w.resetAt, + }, }); } return ranked.sort((a, b) => a.rank - b.rank).map(entry => entry.row); @@ -138,7 +155,17 @@ function barFillStyle(percent: number): CSSProperties { return { ["--bar-scale" as string]: String(barWidth(percent) / 100) }; } -export default function QuotaBars({ quota, plan, threshold, t, className, layout = "compact", pending = false }: { +export default function QuotaBars({ + quota, + plan, + threshold, + t, + className, + layout = "compact", + pending = false, + incompleteWindowKeys, + incompleteCustomWindowLabels, +}: { quota: AccountQuota | null; plan?: string | null; threshold: number; @@ -151,6 +178,9 @@ export default function QuotaBars({ quota, plan, threshold, t, className, layout * bar slot so deferred fill does not shove the page down. */ pending?: boolean; + /** Optional overview-only coverage status. Other quota surfaces remain unchanged when omitted. */ + incompleteWindowKeys?: ReadonlySet; + incompleteCustomWindowLabels?: ReadonlySet; }) { const { locale } = useI18n(); const rows = buildQuotaRows(quota, plan, t); @@ -201,7 +231,16 @@ export default function QuotaBars({ quota, plan, threshold, t, className, layout return (
{rows.map(row => ( - + ))}
); @@ -254,11 +293,12 @@ function QuotaRow({ label, percent, resetAt, threshold, t, locale }: { ); } -function StackedQuotaRow({ row, threshold, t, locale }: { +function StackedQuotaRow({ row, threshold, t, locale, incomplete }: { row: QuotaBarRow; threshold: number; t: TFn; locale: Locale; + incomplete: boolean; }) { const exhausted = isQuotaExhausted(row.percent); const warn = isQuotaWarn(row.percent, threshold); @@ -267,7 +307,18 @@ function StackedQuotaRow({ row, threshold, t, locale }: { return (
- {row.limitLabel} + + {row.limitLabel} + {incomplete && ( + + {t("pws.capacity.windowPartial")} + + )} + {resetText}
diff --git a/gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx b/gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx index b4d188da5..b2741a851 100644 --- a/gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx +++ b/gui/src/components/provider-workspace/ProviderOverviewDashboard.tsx @@ -7,7 +7,12 @@ import { useMemo } from "react"; import { useT, useI18n } from "../../i18n/shared"; import { IconAlert, IconChevron } from "../../icons"; import type { WorkspaceSections, WorkspaceItem } from "../../provider-workspace/catalog"; -import { accountQuotaFromReport, type ProviderQuotaReportView } from "../../provider-workspace/report"; +import { + accountQuotaFromReport, + capacityAggregationFromReport, + type CapacityWindowView, + type ProviderQuotaReportView, +} from "../../provider-workspace/report"; import { attentionReasonKey, buildAttentionItems, @@ -17,7 +22,7 @@ import { relativeTimeLabelsFromT, type ProviderUsageTotals, } from "../../provider-workspace/usage"; -import { maxQuotaUtilisation } from "../QuotaBars"; +import { maxQuotaUtilisation, type QuotaWindowKey } from "../QuotaBars"; import { ProviderIcon } from "./ProviderRail"; import { formatProviderDisplayName } from "../../provider-icons"; import QuotaBars from "../QuotaBars"; @@ -64,8 +69,9 @@ export default function ProviderOverviewDashboard({ for (const item of allItems) { const report = quotaReports[item.name]; const quota = report ? accountQuotaFromReport(report) : null; - if (report && quota) { - result.push({ item, report, urgency: maxQuotaUtilisation(quota) }); + const aggregation = report ? capacityAggregationFromReport(report) : null; + if (report && (quota || aggregation?.presentation === "coverage-only")) { + result.push({ item, report, urgency: quota ? maxQuotaUtilisation(quota) : -1 }); } } return result.sort((a, b) => b.urgency - a.urgency || a.item.name.localeCompare(b.item.name)); @@ -162,13 +168,7 @@ export default function ProviderOverviewDashboard({