rate limit the quota update: once every 2 min
This commit is contained in:
parent
a1fe7c7a87
commit
ac29d26e12
@ -238,7 +238,8 @@ instead of being shown as zero.
|
|||||||
The sidebar refreshes after each completed turn (`session.idle`, plus `session.status`,
|
The sidebar refreshes after each completed turn (`session.idle`, plus `session.status`,
|
||||||
`session.updated`, and `message.updated` as fallbacks since `session.idle` is a server-plugin
|
`session.updated`, and `message.updated` as fallbacks since `session.idle` is a server-plugin
|
||||||
event that may never reach the TUI bus — all debounced) and when the active session changes,
|
event that may never reach the TUI bus — all debounced) and when the active session changes,
|
||||||
plus every 3 minutes as a fallback (`COMMANDCODE_QUOTA_INTERVAL_MS` overrides it) and the
|
but never more than once every 2 minutes; every 5 minutes as a fallback
|
||||||
|
(`COMMANDCODE_QUOTA_INTERVAL_MS` overrides the fallback interval) and the
|
||||||
countdown ticks every 30 seconds. Key resolution and fetch failures are redacted and shown
|
countdown ticks every 30 seconds. Key resolution and fetch failures are redacted and shown
|
||||||
inline; the panel never stays on `loading…` and never blocks the provider. Extra trace lines
|
inline; the panel never stays on `loading…` and never blocks the provider. Extra trace lines
|
||||||
(`tui-quota`) are appended to the debug log only when debug tracing is on.
|
(`tui-quota`) are appended to the debug log only when debug tracing is on.
|
||||||
|
|||||||
20
src/tui.tsx
20
src/tui.tsx
@ -69,9 +69,11 @@ type TuiApi = {
|
|||||||
const ID = "commandcode-toggles";
|
const ID = "commandcode-toggles";
|
||||||
const CATEGORY = "CommandCode";
|
const CATEGORY = "CommandCode";
|
||||||
const SIDEBAR_ORDER = 90;
|
const SIDEBAR_ORDER = 90;
|
||||||
const DEFAULT_QUOTA_INTERVAL_MS = 180_000;
|
const DEFAULT_QUOTA_INTERVAL_MS = 300_000;
|
||||||
|
const MIN_REFRESH_INTERVAL_MS = 120_000;
|
||||||
// Extra TUI-bus signals that mark the end of a turn. `session.idle` is a server
|
// Extra TUI-bus signals that mark the end of a turn. `session.idle` is a server
|
||||||
// plugin event and may never fire here; these keep the panel fresh regardless.
|
// plugin event and may never fire here; these keep the panel fresh regardless.
|
||||||
|
// All event-driven refreshes are floored to MIN_REFRESH_INTERVAL_MS (hard skip).
|
||||||
const QUOTA_TRIGGER_EVENTS = ["session.idle", "session.status", "session.updated", "message.updated"] as const;
|
const QUOTA_TRIGGER_EVENTS = ["session.idle", "session.status", "session.updated", "message.updated"] as const;
|
||||||
|
|
||||||
function safeEnv(name: string): string | undefined {
|
function safeEnv(name: string): string | undefined {
|
||||||
@ -168,6 +170,11 @@ function quotaIntervalMs(): number {
|
|||||||
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_QUOTA_INTERVAL_MS;
|
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_QUOTA_INTERVAL_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatClock(ms: number): string {
|
||||||
|
if (!(ms > 0)) return "--:--:--";
|
||||||
|
return new Date(ms).toTimeString().slice(0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
function shortWindow(w: QuotaWindow, nowMs: number): string {
|
function shortWindow(w: QuotaWindow, nowMs: number): string {
|
||||||
const label = w.id === "fiveHour" ? "5h" : w.id === "weekly" ? "7d" : "mo";
|
const label = w.id === "fiveHour" ? "5h" : w.id === "weekly" ? "7d" : "mo";
|
||||||
const reset = formatReset(w.resetAtMs, nowMs);
|
const reset = formatReset(w.resetAtMs, nowMs);
|
||||||
@ -180,9 +187,11 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
|||||||
const [toggles, setToggles] = createSignal(readToggles());
|
const [toggles, setToggles] = createSignal(readToggles());
|
||||||
const [quota, setQuota] = createSignal<QuotaResult | null>(null);
|
const [quota, setQuota] = createSignal<QuotaResult | null>(null);
|
||||||
const [now, setNow] = createSignal(Date.now());
|
const [now, setNow] = createSignal(Date.now());
|
||||||
|
const [updatedAt, setUpdatedAt] = createSignal(0);
|
||||||
|
|
||||||
let inflight = false;
|
let inflight = false;
|
||||||
let pending = false;
|
let pending = false;
|
||||||
|
let lastRefreshAt = 0;
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||||
let lastSessionId: string | undefined;
|
let lastSessionId: string | undefined;
|
||||||
|
|
||||||
@ -194,6 +203,11 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
|||||||
trace("coalesced", "inflight");
|
trace("coalesced", "inflight");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const since = Date.now() - lastRefreshAt;
|
||||||
|
if (lastRefreshAt > 0 && since < MIN_REFRESH_INTERVAL_MS) {
|
||||||
|
trace("rate-limited", `remainingMs=${MIN_REFRESH_INTERVAL_MS - since}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
inflight = true;
|
inflight = true;
|
||||||
try {
|
try {
|
||||||
const { key: apiKey, source } = resolveApiKeySource(api);
|
const { key: apiKey, source } = resolveApiKeySource(api);
|
||||||
@ -222,7 +236,9 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
|||||||
trace("fetch-throw", message);
|
trace("fetch-throw", message);
|
||||||
} finally {
|
} finally {
|
||||||
inflight = false;
|
inflight = false;
|
||||||
|
lastRefreshAt = Date.now();
|
||||||
setNow(Date.now());
|
setNow(Date.now());
|
||||||
|
setUpdatedAt(Date.now());
|
||||||
if (pending) {
|
if (pending) {
|
||||||
pending = false;
|
pending = false;
|
||||||
trace("flush-pending");
|
trace("flush-pending");
|
||||||
@ -284,6 +300,7 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
|||||||
) : quota() === null ? (
|
) : quota() === null ? (
|
||||||
<text fg={theme.current.textMuted}>quota: loading…</text>
|
<text fg={theme.current.textMuted}>quota: loading…</text>
|
||||||
) : null}
|
) : null}
|
||||||
|
<text fg={theme.current.textMuted}>updated @ {formatClock(updatedAt())}</text>
|
||||||
</box>
|
</box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -365,6 +382,7 @@ export const tui = async (api: TuiApi): Promise<void> => {
|
|||||||
}
|
}
|
||||||
setQuota(result);
|
setQuota(result);
|
||||||
setNow(Date.now());
|
setNow(Date.now());
|
||||||
|
setUpdatedAt(Date.now());
|
||||||
api.ui.toast({ title: CATEGORY, message: formatQuota(result.quota), variant: "info", duration: 15000 });
|
api.ui.toast({ title: CATEGORY, message: formatQuota(result.quota), variant: "info", duration: 15000 });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user