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
2 changes: 1 addition & 1 deletion docs-site/src/content/docs/guides/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ is visible) and never forces an upstream refresh.
| **Startup safety** | Show whether injected Codex routing survives a restart, with separate service and launcher-shim health plus exact repair commands. |
| **Windows tray** | Install a per-user login tray for one-click proxy start, stop, restart, dashboard access, and status. The tray is a controller, not a proxy restart service. |
| **Codex autostart** | Allow an already-installed Codex launcher shim to run `ocx ensure`. This toggle does not install a shim or background service. |
| **Providers** | Add, edit, set the default (enabled providers only), enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Removing the current default switches to the first remaining enabled provider when one exists; otherwise deletion is refused and the current default is kept. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. For Claude (Anthropic) OAuth pools, each logged-in account shows its own 5-hour and weekly rate-limit bars (usage is per credential); a failed probe keeps the last-known bars and marks them unavailable until the next successful refresh. The Provider Overview shown when no provider is selected carries a **Refresh all quotas** control that forces one server-side re-read of every configured provider; a provider whose upstream probe fails keeps its last-good row, so the status line reports that the check completed rather than claiming every value is fresh, and each row's own age stays the per-provider freshness signal. |
| **Providers** | Add, edit, set the default (enabled providers only), enable/disable, and remove providers; manage OAuth account pools and API-key pools where supported. Removing the current default switches to the first remaining enabled provider when one exists; otherwise deletion is refused and the current default is kept. Provider Settings can disable live model discovery for endpoints with missing, slow, or oversized `/models` catalogs. For Claude (Anthropic) OAuth pools, each logged-in account shows its own 5-hour and weekly rate-limit bars (usage is per credential); a failed probe keeps the last-known bars and marks them unavailable until the next successful refresh. Other OAuth providers with a logged-in account (Google Antigravity, xAI, Kiro, Cursor and the rest) show an **Account pool** card. Its switch controls the pre-dispatch account preference; a 429 still fails over either way. The strategy is marked **Saved, not live** and cannot be changed there until `pool.kernel` is on, and a strategy set only from the CLI, such as Kiro's `least-loaded`, is shown by name and kept when you toggle the pool. The Provider Overview shown when no provider is selected carries a **Refresh all quotas** control that forces one server-side re-read of every configured provider; a provider whose upstream probe fails keeps its last-good row, so the status line reports that the check completed rather than claiming every value is fresh, and each row's own age stays the per-provider freshness signal. |
| **Add provider** | One search above the tabs reaches all four at once — Accounts, Free, Local, Paid. While a query is live the results are grouped by tab with a count each, and the selected tab stays put rather than jumping. Press ArrowDown in the search box to focus the first available result action, skipping disabled buttons; if no action is available, focus stays in the search box. Local runtimes (Ollama, vLLM, LM Studio, LiteLLM) have their own tab, and a long provider note clamps to two lines with the full text one click away. Claude Code CLI (subscription) sits under Paid with a Subscription CLI badge, and selecting it warns that it bills the signed-in Claude subscription through `claude -p` and never uses an API key (use `anthropic-apikey` for key billing). |
| **Codex Auth** | Add ChatGPT/Codex pool accounts, select the next-session account, refresh 5h / weekly / 30d quotas, enable or disable quota auto-switch, set its 1–100% threshold, and configure transient-failure failover. |
| **Subagents** | Feature up to five bare native or namespaced routed models in the `spawn_agent` override list. |
Expand Down
32 changes: 25 additions & 7 deletions gui/src/components/AccountPoolStrategyControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export interface AccountPoolStrategyControlsProps {
strategy: AccountPoolStrategy;
codex?: boolean;
threshold?: number;
allowResetFirst?: boolean;
/** A stored strategy outside this dashboard's model, shown verbatim and never rewritten. */
unmodeledStrategy?: string | null;
compact?: boolean;
stickyDraft: string;
disabled?: boolean;
strategySelectId?: string;
Expand All @@ -47,6 +51,9 @@ export default function AccountPoolStrategyControls({
strategy,
codex = false,
threshold,
allowResetFirst = false,
unmodeledStrategy = null,
compact = false,
stickyDraft,
disabled = false,
strategySelectId = "account-pool-strategy",
Expand All @@ -56,10 +63,13 @@ export default function AccountPoolStrategyControls({
onStickyCommit,
}: AccountPoolStrategyControlsProps) {
const t = useT();
const strategyOptions = ACCOUNT_POOL_STRATEGIES.filter(value => codex || value !== "reset-first").map((value) => ({
const strategyOptions = ACCOUNT_POOL_STRATEGIES.filter(value => (
codex || allowResetFirst || value !== "reset-first" || strategy === value
)).map((value) => ({
value,
label: t(STRATEGY_LABEL_KEYS[value]),
}));
if (unmodeledStrategy) strategyOptions.unshift({ value: unmodeledStrategy as AccountPoolStrategy, label: unmodeledStrategy });

const thresholdSummary = (() => {
if (threshold === undefined) return null;
Expand Down Expand Up @@ -92,18 +102,26 @@ export default function AccountPoolStrategyControls({
<div className="setting-row">
<div className="setting-label">
<span className="title" id={`${strategySelectId}-label`}>{t("accountPool.strategy")}</span>
<span className="desc">{t("accountPool.strategyDesc")}</span>
<span className="desc">{t(STRATEGY_HINT_KEYS[strategy])}</span>
<span className="desc">{t("accountPool.unboundDefinition")}</span>
{compact ? (unmodeledStrategy ? null : (
<span className="desc">
{strategy === "reset-first" ? t("genericPool.visualResetFirst") : t(STRATEGY_HINT_KEYS[strategy])}
</span>
)) : (
<>
<span className="desc">{t("accountPool.strategyDesc")}</span>
<span className="desc">{t(STRATEGY_HINT_KEYS[strategy])}</span>
<span className="desc">{t("accountPool.unboundDefinition")}</span>
</>
)}
</div>
<div className="setting-controls">
<Select
id={strategySelectId}
value={strategy}
value={unmodeledStrategy ?? strategy}
options={strategyOptions}
disabled={disabled}
label={t("accountPool.strategy")}
onChange={(next) => onStrategyChange(next as AccountPoolStrategy)}
onChange={(next) => { if (next !== unmodeledStrategy) onStrategyChange(next as AccountPoolStrategy); }}
/>
{thresholdSummary && (
<span className="badge badge-muted account-pool-threshold-badge" data-testid="account-pool-threshold-summary">
Expand All @@ -112,7 +130,7 @@ export default function AccountPoolStrategyControls({
)}
</div>
</div>
{strategy === "round-robin" && (
{strategy === "round-robin" && !unmodeledStrategy && (
<div className="setting-row">
<label className="setting-label" htmlFor={stickyInputId}>
<span className="title">{t("accountPool.stickyLimit")}</span>
Expand Down
Loading
Loading