///|
/// Registry-driven quota segments on the status bar.
///
/// The router pulls the active provider's registered `QuotaSource` from the
/// devkit quota registry and publishes the readings as status segments:
/// balance readings render a `balance` segment with `" "`,
/// window readings render the window name with `"% left"` (codex CLI
/// remaining convention). DeepSeek's balance and the coding-plan windows
/// (zai/kimi) ride this generic path; codex has no registry source, so the
/// router clears its own quota segments for it and the bar stays owned by
/// `CodexQuotaStatus` (distinct source; provider scoping keeps them mutually
/// exclusive). With `bus = None` every publication is a silent no-op, and no
/// failure here ever escapes: refresh runs after the chat response and
/// captures every error into a backoff.
///|
/// A successful registry pull throttles re-reads for 30 minutes.
let quota_refresh_interval_ms : Int64 = 1800000L
///|
/// A failed registry pull backs off for 5 minutes so a bad endpoint never
/// slows chat.
let quota_failure_backoff_ms : Int64 = 300000L
///|
/// Bar placement of the router's quota segments (same slot as the codex
/// windows; provider scoping keeps the two publishers mutually exclusive).
let quota_segment_priority : Int = 45
///|
/// Local coloring heuristic over provider-stated numbers (color is a UI
/// decision, never a data estimate), on the remaining axis: exhausted
/// windows read as errors, windows within 20% of exhaustion as warnings.
fn quota_color_role(left_percent : Double) -> String? {
if left_percent <= 0.0 {
Some("error")
} else if left_percent <= 20.0 {
Some("warning")
} else {
None
}
}
///|
/// Register one quota segment on the bus.
fn publish_quota_segment(
bus : @devkit.EventBus,
segment~ : String,
value~ : String,
color~ : String?,
) -> Unit {
match color {
Some(role) =>
@devkit.publish_status_register(
bus,
source="posoco_ext_llm",
segment~,
priority=quota_segment_priority,
value~,
color=role,
)
None =>
@devkit.publish_status_register(
bus,
source="posoco_ext_llm",
segment~,
priority=quota_segment_priority,
value~,
)
}
}
///|
/// Render the cached registry readings as status segments. Segments that
/// were published before but vanished from the current readings are
/// unregistered, so a stale number never lingers on the bar.
fn RouterModelPort::publish_quota_segments(self : RouterModelPort) -> Unit {
let bus = match self.bus {
Some(bus) => bus
None => return
}
let published : Array[String] = []
for reading in self.quota_readings {
if reading.window == "balance" {
match reading.amount {
Some((value, currency)) => {
published.push("balance")
publish_quota_segment(
bus,
segment="balance",
value="\{value} \{currency}",
color=None,
)
}
None => ()
}
} else {
match reading.used_percent {
Some(percent) => {
// Providers declare used; the bar follows the codex CLI remaining
// convention. 100 − used is exact arithmetic on the declared value,
// not an estimate.
let left = 100.0 - percent
published.push(reading.window)
publish_quota_segment(
bus,
segment=reading.window,
value=format_status_percent(left) + "% left",
color=quota_color_role(left),
)
}
None => ()
}
}
}
for segment in self.quota_shown {
if !published.contains(segment) {
@devkit.publish_status_unregister(bus, source="posoco_ext_llm", segment~)
}
}
self.quota_shown = published
}
///|
/// Pull the active provider's registry source when due: a successful pull
/// throttles re-reads for 30 minutes, a failed one backs off for 5. Runs
/// after the chat response; never raises. A provider without a registry
/// source (codex, for example) clears the router's quota segments — the bar
/// facts belong to that provider's own publisher.
async fn RouterModelPort::refresh_quota_if_due(
self : RouterModelPort,
) -> Unit noraise {
let now = @async.now()
if now - self.quota_fetched_at < quota_refresh_interval_ms {
return
}
if now - self.quota_failed_at < quota_failure_backoff_ms {
return
}
match @devkit.quota_registry_lookup(self.current_provider_id()) {
Some(source) => {
let result = source.read() catch { _ => Err("quota pull failed") }
match result {
Ok(readings) => {
self.quota_readings = readings
self.quota_fetched_at = now
self.quota_failed_at = 0L
self.publish_quota_segments()
}
Err(_) => self.quota_failed_at = now
}
}
None => self.clear_quota_segments()
}
}
///|
/// Force an immediate registry pull, skipping the throttle window. The
/// `/model` and `/effort` switches use this so the bar reflects the new
/// provider as soon as the command returns; failures back off like the
/// deferred path, and a provider without a source clears the segments.
async fn RouterModelPort::refresh_quota_now(
self : RouterModelPort,
) -> Unit noraise {
let now = @async.now()
match @devkit.quota_registry_lookup(self.current_provider_id()) {
Some(source) => {
let result = source.read() catch { _ => Err("quota pull failed") }
match result {
Ok(readings) => {
self.quota_readings = readings
self.quota_fetched_at = now
self.quota_failed_at = 0L
self.publish_quota_segments()
}
Err(_) => self.quota_failed_at = now
}
}
None => self.clear_quota_segments()
}
}
///|
/// Unregister every quota segment this router published and reset the pull
/// cache, so the next active provider starts clean.
fn RouterModelPort::clear_quota_segments(self : RouterModelPort) -> Unit {
match self.bus {
Some(bus) =>
for segment in self.quota_shown {
@devkit.publish_status_unregister(
bus,
source="posoco_ext_llm",
segment~,
)
}
None => ()
}
self.quota_shown = []
self.quota_readings = []
self.quota_fetched_at = 0L
self.quota_failed_at = 0L
}