Files
shusub2/sub2api_quota_tui.py
T

3319 lines
133 KiB
Python

#!/usr/bin/env python3
"""Textual TUI for Sub2API quota and daily account usage."""
from __future__ import annotations
import argparse
import copy
import datetime as dt
import gzip
import importlib.metadata
import io
import json
import math
import os
import re
import subprocess
import sys
import threading
import time
import urllib.error
import urllib.parse
import urllib.request
import zlib
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import Any
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
APP_NAME = "shusub2"
FALLBACK_VERSION = "0.3.5"
DEFAULT_WORKSPACE_URL = "https://price.tailbeb9ad.ts.net/api/ui-data?view=workspace"
DEFAULT_WORKSPACE_URL_CONFIG_FILE = "~/.config/shusub2/workspace-url"
DEFAULT_API_URL = "http://127.0.0.1:18318/api/tui/accounts"
DEFAULT_CONFIG_FILE = "~/.config/shusub2/api-url"
DEFAULT_STATUS_CONFIG_FILE = "~/.config/shusub2/status-url"
DEFAULT_PRICING_URL = "https://price.tailbeb9ad.ts.net/api/ui-data?view=accounts"
DEFAULT_PRICING_URL_CONFIG_FILE = "~/.config/shusub2/pricing-url"
DEFAULT_LOGS_API_URL = "https://sub2apicn.shujk.top/api/v1/admin/usage"
DEFAULT_LOGS_URL_CONFIG_FILE = "~/.config/shusub2/logs-url"
DEFAULT_LOGS_TOKEN_CONFIG_FILE = "~/.config/shusub2/logs-token"
# us.sub2.shujk.top is the DNS-only standard HTTPS entry for server4, avoiding Cloudflare 1010.
DEFAULT_ERRORS_CN_URL = "https://sub2apicn.shujk.top/api/v1/admin/ops/errors"
DEFAULT_ERRORS_US_URL = "https://us.sub2.shujk.top/api/v1/admin/ops/errors"
DEFAULT_ERRORS_CN_URL_CONFIG_FILE = "~/.config/shusub2/errors-cn-url"
DEFAULT_ERRORS_US_URL_CONFIG_FILE = "~/.config/shusub2/errors-us-url"
DEFAULT_VERSION_CHECK_URL = "https://gitea.shujk.top/shujakuin/shusub2/raw/branch/main/pyproject.toml"
DEFAULT_REFRESH_SECONDS = 300
DEFAULT_LOGS_REFRESH_SECONDS = 300
DEFAULT_ERRORS_REFRESH_SECONDS = 300
DEFAULT_LOGS_LIMIT = 100
DEFAULT_ERRORS_LIMIT = 100
DEFAULT_ERRORS_TIME_RANGE = "24h"
DEFAULT_TIMEOUT_SECONDS = 10
DEFAULT_VERSION_CHECK_TIMEOUT_SECONDS = 2
MAX_JSON_RESPONSE_BYTES = 4 * 1024 * 1024
MAX_WORKSPACE_JSON_BYTES = 768 * 1024
MAX_WORKSPACE_ACCOUNTS = 512
MAX_WORKSPACE_MONITORS = 256
MAX_WORKSPACE_SOURCES = 64
MAX_WORKSPACE_INSTANCES = 16
MAX_WORKSPACE_REQUESTS = 100
MAX_WORKSPACE_ERROR_EVENTS = 256
MAX_WORKSPACE_KEYS = 256
USAGE_TIMEZONE = "Asia/Shanghai"
class WorkspaceNotModified(RuntimeError):
"""A conditional workspace GET confirmed the validated snapshot is unchanged."""
def __init__(self, etag: str = "") -> None:
super().__init__("workspace payload not modified")
self.etag = str(etag or "").strip()
class WorkspaceNoRedirectHandler(urllib.request.HTTPRedirectHandler):
"""Keep workspace validators confined to the configured endpoint origin."""
def redirect_request(
self,
request: urllib.request.Request,
response: Any,
code: int,
message: str,
headers: Any,
new_url: str,
) -> None:
return None
class WorkspacePayload(dict[str, Any]):
"""Validated workspace JSON with an opaque HTTP validator kept out of the payload."""
def __init__(self, payload: dict[str, Any], etag: str = "") -> None:
super().__init__(payload)
self.etag = str(etag or "").strip()
MONITOR_OK_STATUSES = {"operational", "ok", "success"}
MONITOR_FAILED_STATUSES = {"error", "failed", "failure"}
MONITOR_STOPWORDS = {"response", "responses", "monitor"}
PRICING_ACCOUNT_SOURCE_RE = re.compile(
r"^[a-z0-9]+-quota(?:only)?-(?P<source>[a-z0-9]+(?:-[a-z0-9]+)*)$",
re.IGNORECASE,
)
ERROR_DISPLAY_LABEL_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9 ._/+\-]{0,95}$")
ERROR_URL_RE = re.compile(r"(?i)\b(?:https?|wss?)://|\bwww\.")
ERROR_EMAIL_RE = re.compile(r"(?i)\b[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}\b")
ERROR_SECRET_RE = re.compile(
r"(?i)\b(?:authorization|bearer|api[_ -]?key|token|password|secret|cookie|session(?:[_ -]?id)?)\b"
)
ERROR_OPAQUE_RE = re.compile(r"(?i)\b(?:sk|rk|pk|eyj)[_-]?[A-Z0-9]{12,}\b")
INSTALL_COMMAND = "uv tool install --force git+https://gitea.shujk.top/shujakuin/shusub2.git"
INSTALL_COMMAND_ARGS = ["uv", "tool", "install", "--force", "git+https://gitea.shujk.top/shujakuin/shusub2.git"]
def env_int(name: str, default: int, *, minimum: int = 1) -> int:
try:
return max(minimum, int(os.environ.get(name, default)))
except Exception:
return default
def configured_url(env_names: tuple[str, ...], config_path: str, default: str = "") -> str:
for name in env_names:
value = os.environ.get(name, "").strip()
if value:
return value
config_file = Path(config_path).expanduser()
try:
for line in config_file.read_text(encoding="utf-8").splitlines():
value = line.strip()
if value and not value.startswith("#"):
return value
except OSError:
pass
return default
def default_workspace_url() -> str:
return configured_url(
("SHUSUB2_WORKSPACE_URL",),
os.environ.get("SHUSUB2_WORKSPACE_URL_FILE", DEFAULT_WORKSPACE_URL_CONFIG_FILE),
DEFAULT_WORKSPACE_URL,
)
def default_api_url() -> str:
return configured_url(
("SHUSUB2_API_URL", "SUB2API_QUOTA_TUI_API_URL"),
os.environ.get("SHUSUB2_API_URL_FILE", DEFAULT_CONFIG_FILE),
DEFAULT_API_URL,
)
def default_status_url() -> str:
return configured_url(
("SHUSUB2_STATUS_URL",),
os.environ.get("SHUSUB2_STATUS_URL_FILE", DEFAULT_STATUS_CONFIG_FILE),
)
def default_pricing_url() -> str:
return configured_url(
("SHUSUB2_PRICING_URL",),
os.environ.get("SHUSUB2_PRICING_URL_FILE", DEFAULT_PRICING_URL_CONFIG_FILE),
DEFAULT_PRICING_URL,
)
def default_logs_url() -> str:
return configured_url(
("SHUSUB2_LOGS_URL",),
os.environ.get("SHUSUB2_LOGS_URL_FILE", DEFAULT_LOGS_URL_CONFIG_FILE),
DEFAULT_LOGS_API_URL,
)
def default_logs_token() -> str:
return configured_url(
("SHUSUB2_LOGS_TOKEN",),
os.environ.get("SHUSUB2_LOGS_TOKEN_FILE", DEFAULT_LOGS_TOKEN_CONFIG_FILE),
)
def workspace_url_config_file_path() -> Path:
return Path(
os.environ.get("SHUSUB2_WORKSPACE_URL_FILE", DEFAULT_WORKSPACE_URL_CONFIG_FILE)
).expanduser()
def config_file_path() -> Path:
return Path(os.environ.get("SHUSUB2_API_URL_FILE", DEFAULT_CONFIG_FILE)).expanduser()
def logs_url_config_file_path() -> Path:
return Path(os.environ.get("SHUSUB2_LOGS_URL_FILE", DEFAULT_LOGS_URL_CONFIG_FILE)).expanduser()
def pricing_url_config_file_path() -> Path:
return Path(
os.environ.get("SHUSUB2_PRICING_URL_FILE", DEFAULT_PRICING_URL_CONFIG_FILE)
).expanduser()
def logs_token_config_file_path() -> Path:
return Path(os.environ.get("SHUSUB2_LOGS_TOKEN_FILE", DEFAULT_LOGS_TOKEN_CONFIG_FILE)).expanduser()
def default_errors_cn_url() -> str:
return configured_url(
("SHUSUB2_ERRORS_CN_URL",),
os.environ.get("SHUSUB2_ERRORS_CN_URL_FILE", DEFAULT_ERRORS_CN_URL_CONFIG_FILE),
DEFAULT_ERRORS_CN_URL,
)
def default_errors_us_url() -> str:
return configured_url(
("SHUSUB2_ERRORS_US_URL",),
os.environ.get("SHUSUB2_ERRORS_US_URL_FILE", DEFAULT_ERRORS_US_URL_CONFIG_FILE),
DEFAULT_ERRORS_US_URL,
)
def errors_cn_url_config_file_path() -> Path:
return Path(os.environ.get("SHUSUB2_ERRORS_CN_URL_FILE", DEFAULT_ERRORS_CN_URL_CONFIG_FILE)).expanduser()
def errors_us_url_config_file_path() -> Path:
return Path(os.environ.get("SHUSUB2_ERRORS_US_URL_FILE", DEFAULT_ERRORS_US_URL_CONFIG_FILE)).expanduser()
def write_config_value(path: Path, value: str) -> Path:
text = str(value or "").strip()
if not text:
raise ValueError("config value is empty")
path.parent.mkdir(parents=True, exist_ok=True)
try:
path.parent.chmod(0o700)
except OSError:
pass
path.write_text(text + "\n", encoding="utf-8")
try:
path.chmod(0o600)
except OSError:
pass
return path
def write_api_url_config(api_url: str) -> Path:
value = str(api_url or "").strip()
if not value:
raise ValueError("api url is empty")
return write_config_value(config_file_path(), value)
def run_install_command() -> int:
try:
return subprocess.run(INSTALL_COMMAND_ARGS, check=False).returncode
except FileNotFoundError:
print("uv command not found; install uv first, then run:", INSTALL_COMMAND, file=sys.stderr)
return 127
def inferred_status_url(api_url: str) -> str:
parsed = urllib.parse.urlparse(str(api_url or "").strip())
if not parsed.scheme or not parsed.netloc:
return ""
path = parsed.path.rstrip("/")
if path.endswith("/api/tui/accounts"):
status_path = path[: -len("/api/tui/accounts")] + "/api/status"
elif path.endswith("/api/accounts"):
status_path = path[: -len("/api/accounts")] + "/api/status"
else:
return ""
return urllib.parse.urlunparse(parsed._replace(path=status_path, params="", query="", fragment=""))
def current_version() -> str:
try:
return importlib.metadata.version(APP_NAME)
except importlib.metadata.PackageNotFoundError:
return FALLBACK_VERSION
except Exception:
return FALLBACK_VERSION
def parse_version(value: Any) -> tuple[int, ...]:
parts = []
for part in re.split(r"[^0-9]+", str(value or "")):
if part:
parts.append(int(part))
return tuple(parts)
def version_is_newer(latest: str, current: str) -> bool:
latest_parts = parse_version(latest)
current_parts = parse_version(current)
width = max(len(latest_parts), len(current_parts), 1)
return latest_parts + (0,) * (width - len(latest_parts)) > current_parts + (0,) * (width - len(current_parts))
def latest_version_from_text(text: str) -> str:
match = re.search(r'(?m)^version\s*=\s*"([^"]+)"', text)
return "" if not match else match.group(1).strip()
def fetch_latest_version(url: str, timeout: int) -> str:
req = urllib.request.Request(url, headers={"Accept": "text/plain"})
with urllib.request.urlopen(req, timeout=timeout) as response:
return latest_version_from_text(response.read(65536).decode("utf-8", errors="replace"))
def version_update_message(latest: str, current: str | None = None) -> str:
current = current or current_version()
if not latest or not version_is_newer(latest, current):
return ""
return f"update available: {APP_NAME} {current} -> {latest}; run `{INSTALL_COMMAND}`"
def check_version_update(url: str, timeout: int, *, disabled: bool = False) -> str:
if disabled or os.environ.get("SHUSUB2_NO_VERSION_CHECK", "").strip().lower() in {"1", "true", "yes", "on"}:
return ""
try:
return version_update_message(fetch_latest_version(url, timeout))
except Exception:
return ""
def as_float(value: Any) -> float:
try:
if value is None or str(value).strip() == "":
return 0.0
return float(value)
except Exception:
return 0.0
def as_int(value: Any) -> int:
try:
if value is None:
return 0
if isinstance(value, bool):
return int(value)
if isinstance(value, int):
return value
if isinstance(value, float):
return int(value) if math.isfinite(value) else 0
text = str(value).strip()
if not text:
return 0
if re.fullmatch(r"[+-]?[0-9]+", text):
return int(text)
number = float(text)
return int(number) if math.isfinite(number) else 0
except (TypeError, ValueError, OverflowError):
return 0
def format_cost(value: Any) -> str:
amount = as_float(value)
if amount == 0:
return "$0"
if abs(amount) < 0.01:
return f"${amount:.6f}".rstrip("0").rstrip(".")
if abs(amount) < 10:
return f"${amount:.3f}".rstrip("0").rstrip(".")
return f"${amount:.2f}".rstrip("0").rstrip(".")
def optional_number(value: Any) -> float | None:
if value is None or str(value).strip() == "":
return None
try:
number = float(value)
except (TypeError, ValueError):
return None
return number if math.isfinite(number) else None
def format_amount(value: Any) -> str:
amount = optional_number(value)
if amount is None:
return "-"
if amount.is_integer():
return f"{int(amount):,}"
return f"{amount:,.6f}".rstrip("0").rstrip(".")
def format_cny(value: Any) -> str:
amount = format_amount(value)
return "-" if amount == "-" else f{amount}"
def format_multiplier(value: Any) -> str:
amount = optional_number(value)
if amount is None:
return "-"
return f"{amount:.8g}x"
def account_rate_value(item: dict[str, Any]) -> float | None:
display = optional_number(item.get("account_rate_multiplier_cny"))
return display if display is not None else optional_number(item.get("account_rate_multiplier"))
def format_count(value: Any) -> str:
number = as_int(value)
if abs(number) >= 1_000_000:
return f"{number / 1_000_000:.1f}M".rstrip("0").rstrip(".")
if abs(number) >= 1_000:
return f"{number / 1_000:.1f}K".rstrip("0").rstrip(".")
return str(number)
def format_percent(value: Any) -> str:
if value is None or str(value).strip() == "":
return "-"
percent = as_float(value)
rounded = round(percent, 1)
if rounded.is_integer():
return f"{int(rounded)}%"
return f"{rounded}%"
def parse_time(value: Any) -> dt.datetime | None:
text = str(value or "").strip()
if not text:
return None
try:
normalized = text[:-1] + "+00:00" if text.endswith("Z") else text
return dt.datetime.fromisoformat(normalized)
except Exception:
return None
def short_time(value: Any) -> str:
parsed = parse_time(value)
if not parsed:
return "-"
return parsed.astimezone().strftime("%m-%d %H:%M")
def relative_age(value: Any, now: dt.datetime | None = None) -> str:
parsed = parse_time(value)
if not parsed:
return "-"
current = now or dt.datetime.now(dt.timezone.utc)
if current.tzinfo is None:
current = current.replace(tzinfo=dt.timezone.utc)
elapsed = (current.astimezone(dt.timezone.utc) - parsed.astimezone(dt.timezone.utc)).total_seconds()
if elapsed < -30:
return f"in {max(1, int((-elapsed) / 60))}m"
seconds = max(0, int(elapsed))
if seconds < 60:
return "now"
minutes = seconds // 60
if minutes < 60:
return f"{minutes}m ago"
hours = minutes // 60
if hours < 24:
return f"{hours}h ago"
return f"{hours // 24}d ago"
def add_refresh_param(api_url: str, refresh: bool) -> str:
if not refresh:
return api_url
parsed = urllib.parse.urlparse(api_url)
query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)
query = [(key, value) for key, value in query if key != "refresh"]
query.append(("refresh", "1"))
return urllib.parse.urlunparse(parsed._replace(query=urllib.parse.urlencode(query)))
def decode_json_response(
response: Any,
error_message: str,
*,
maximum_bytes: int = MAX_JSON_RESPONSE_BYTES,
) -> dict[str, Any]:
limit = max(1, int(maximum_bytes))
raw = response.read(limit + 1)
if len(raw) > limit:
raise RuntimeError(error_message)
content_encoding = str(response.headers.get("Content-Encoding", "")).lower()
if "gzip" in {value.strip() for value in content_encoding.split(",")}:
try:
with gzip.GzipFile(fileobj=io.BytesIO(raw)) as compressed:
raw = compressed.read(limit + 1)
except (OSError, EOFError, zlib.error) as exc:
raise RuntimeError(error_message) from exc
if len(raw) > limit:
raise RuntimeError(error_message)
data = json.loads(raw.decode("utf-8"))
if not isinstance(data, dict):
raise RuntimeError(error_message)
return data
def fetch_payload(
api_url: str,
timeout: int,
*,
refresh: bool = False,
maximum_bytes: int = MAX_JSON_RESPONSE_BYTES,
use_proxy: bool = True,
) -> dict[str, Any]:
req = urllib.request.Request(
add_refresh_param(api_url, refresh),
headers={"Accept": "application/json", "Accept-Encoding": "gzip"},
)
open_request = (
urllib.request.urlopen
if use_proxy
else urllib.request.build_opener(urllib.request.ProxyHandler({})).open
)
with open_request(req, timeout=timeout) as response:
return decode_json_response(
response,
"API did not return a JSON object",
maximum_bytes=maximum_bytes,
)
def fetch_optional_payload(url: str, timeout: int) -> tuple[dict[str, Any], str]:
if not str(url or "").strip():
return {}, ""
try:
return fetch_payload(url, timeout), ""
except Exception as exc:
return {}, str(exc)
def fetch_pricing_payload(pricing_url: str, timeout: int) -> dict[str, Any]:
payload = fetch_payload(pricing_url, timeout)
sources = payload.get("sources")
if payload.get("view") != "accounts" or not isinstance(sources, list):
raise RuntimeError("Pricing Monitor did not return an accounts source projection")
for source in sources:
if (
not isinstance(source, dict)
or not isinstance(source.get("name"), str)
or not source["name"].strip()
or not isinstance(source.get("source_kind"), str)
or not source["source_kind"].strip()
or not isinstance(source.get("health_state"), str)
or not source["health_state"].strip()
or not isinstance(source.get("balance_available"), bool)
or not isinstance(source.get("balance"), dict)
):
raise RuntimeError("Pricing Monitor returned an invalid accounts source projection")
return payload
def fetch_optional_pricing_payload(pricing_url: str, timeout: int) -> tuple[dict[str, Any], str]:
try:
return fetch_pricing_payload(pricing_url, timeout), ""
except Exception:
return {}, "unavailable"
def workspace_values_are_finite(value: Any) -> bool:
pending = [value]
visited = 0
while pending:
current = pending.pop()
visited += 1
if visited > 100_000:
return False
if isinstance(current, float) and not math.isfinite(current):
return False
if isinstance(current, dict):
pending.extend(current.values())
elif isinstance(current, list):
pending.extend(current)
return True
def workspace_traffic_period(traffic: Any) -> dict[str, str] | None:
"""Validate the server-owned local calendar-day Traffic contract."""
if not isinstance(traffic, dict) or traffic.get("period_kind") != "calendar_day":
return None
date_text = str(traffic.get("date") or "").strip()
timezone_text = str(traffic.get("timezone") or "").strip()
if timezone_text != USAGE_TIMEZONE:
return None
started_at = parse_time(traffic.get("started_at"))
ends_at = parse_time(traffic.get("ends_at"))
if not date_text or not timezone_text or started_at is None or ends_at is None:
return None
if started_at.tzinfo is None or ends_at.tzinfo is None:
return None
try:
date = dt.date.fromisoformat(date_text)
zone = ZoneInfo(timezone_text)
except (ValueError, ZoneInfoNotFoundError):
return None
local_start = dt.datetime.combine(date, dt.time.min, tzinfo=zone)
local_end = local_start + dt.timedelta(days=1)
if (
started_at.astimezone(dt.timezone.utc) != local_start.astimezone(dt.timezone.utc)
or ends_at.astimezone(dt.timezone.utc) != local_end.astimezone(dt.timezone.utc)
):
return None
return {
"period_kind": "calendar_day",
"date": date.isoformat(),
"timezone": zone.key,
"started_at": started_at.astimezone(dt.timezone.utc).isoformat(),
"ends_at": ends_at.astimezone(dt.timezone.utc).isoformat(),
}
def workspace_traffic_is_current(payload: dict[str, Any], *, now: dt.datetime | None = None) -> bool:
traffic = payload.get("traffic") if isinstance(payload.get("traffic"), dict) else {}
period = workspace_traffic_period(traffic)
if period is None:
return False
current = now or dt.datetime.now(dt.timezone.utc)
if current.tzinfo is None:
current = current.replace(tzinfo=dt.timezone.utc)
started_at = parse_time(period["started_at"])
ends_at = parse_time(period["ends_at"])
return bool(started_at and ends_at and started_at <= current < ends_at)
def workspace_period_label(traffic: Any) -> str:
period = workspace_traffic_period(traffic)
if period is None:
return "today"
return f"today {period['date']} ({period['timezone']})"
def fetch_workspace_response(
workspace_url: str,
timeout: int,
*,
if_none_match: str = "",
) -> tuple[dict[str, Any], str]:
"""Fetch one workspace representation or signal a bodyless 304 revalidation."""
validator = str(if_none_match or "").strip()
headers = {"Accept": "application/json", "Accept-Encoding": "gzip"}
if validator:
headers["If-None-Match"] = validator
request = urllib.request.Request(workspace_url, headers=headers)
opener = urllib.request.build_opener(
urllib.request.ProxyHandler({}),
WorkspaceNoRedirectHandler(),
)
try:
with opener.open(request, timeout=timeout) as response:
payload = decode_json_response(
response,
"Pricing Monitor workspace did not return a JSON object",
maximum_bytes=MAX_WORKSPACE_JSON_BYTES,
)
etag = str(response.headers.get("ETag") or "").strip()
except urllib.error.HTTPError as exc:
if exc.code == 304:
headers = exc.headers or {}
raise WorkspaceNotModified(str(headers.get("ETag") or validator)) from exc
raise
return payload, etag
def fetch_workspace_payload(
workspace_url: str,
timeout: int,
*,
if_none_match: str = "",
) -> WorkspacePayload:
payload, etag = fetch_workspace_response(
workspace_url,
timeout,
if_none_match=if_none_match,
)
traffic = payload.get("traffic")
if (
payload.get("view") != "workspace"
or not isinstance(payload.get("state"), dict)
or not isinstance(payload.get("components"), dict)
or not isinstance(payload.get("accounts"), dict)
or not isinstance(payload.get("status"), dict)
or not isinstance(payload.get("sources"), list)
or not isinstance(traffic, dict)
or not workspace_values_are_finite(payload)
):
raise RuntimeError("Pricing Monitor returned an invalid workspace projection")
if workspace_traffic_period(traffic) is None:
raise RuntimeError("Pricing Monitor returned an invalid workspace traffic period")
if traffic.get("usage_rollups_full_day") is not True:
raise RuntimeError("Pricing Monitor returned incomplete workspace traffic usage")
channel_monitors = payload["status"].get("channel_monitors")
bounded_fields = (
("accounts", payload["accounts"].get("accounts"), MAX_WORKSPACE_ACCOUNTS),
(
"monitors",
channel_monitors.get("items") if isinstance(channel_monitors, dict) else None,
MAX_WORKSPACE_MONITORS,
),
("instances", traffic.get("instances"), MAX_WORKSPACE_INSTANCES),
("requests", traffic.get("requests"), MAX_WORKSPACE_REQUESTS),
("errors", traffic.get("errors"), MAX_WORKSPACE_ERROR_EVENTS),
("keys", traffic.get("keys"), MAX_WORKSPACE_KEYS),
)
for label, rows, maximum in bounded_fields:
if not isinstance(rows, list) or len(rows) > maximum or any(
not isinstance(row, dict) for row in rows
):
raise RuntimeError(f"Pricing Monitor returned invalid workspace {label}")
for label in ("requests", "keys"):
for row in traffic.get(label) or []:
if str(row.get("instance") or row.get("node") or "").strip() != "server6":
raise RuntimeError(f"Pricing Monitor returned noncanonical workspace {label}")
if len(payload["sources"]) > MAX_WORKSPACE_SOURCES or not all(
isinstance(source, dict) for source in payload["sources"]
):
raise RuntimeError("Pricing Monitor returned invalid workspace sources")
return WorkspacePayload(payload, etag)
def workspace_accounts_payload(payload: dict[str, Any]) -> dict[str, Any]:
value = payload.get("accounts")
return value if isinstance(value, dict) else {}
def workspace_status_payload(payload: dict[str, Any]) -> dict[str, Any]:
value = payload.get("status")
return value if isinstance(value, dict) else {}
def workspace_pricing_payload(payload: dict[str, Any]) -> dict[str, Any]:
return {
"service": payload.get("service"),
"view": "accounts",
"generated_at": payload.get("generated_at"),
"sources": payload.get("sources") if isinstance(payload.get("sources"), list) else [],
}
def workspace_logs_payload(payload: dict[str, Any]) -> dict[str, Any]:
traffic = payload.get("traffic") if isinstance(payload.get("traffic"), dict) else {}
raw_items = traffic.get("requests") if isinstance(traffic.get("requests"), list) else []
items = []
for raw in raw_items:
if not isinstance(raw, dict):
continue
instance = str(raw.get("instance") or raw.get("node") or "").strip()
if instance != "server6":
continue
item = dict(raw)
item["_node"] = instance or "-"
if "total_cost" not in item:
item["total_cost"] = (
raw.get("cost")
if raw.get("cost") is not None
else raw.get("actual_cost")
)
items.append(item)
return {
"data": {"items": items, "total": len(items)},
"generated_at": traffic.get("generated_at") or payload.get("generated_at"),
"workspace": True,
}
def workspace_keys_payload(payload: dict[str, Any]) -> dict[str, Any]:
traffic = payload.get("traffic") if isinstance(payload.get("traffic"), dict) else {}
period = workspace_traffic_period(traffic) or {}
raw_items = traffic.get("keys") if isinstance(traffic.get("keys"), list) else []
items = [
dict(item)
for item in raw_items
if isinstance(item, dict)
and str(item.get("instance") or item.get("node") or "").strip() == "server6"
]
return {
"items": items,
"generated_at": traffic.get("generated_at") or payload.get("generated_at"),
"period_kind": period.get("period_kind", ""),
"date": period.get("date", ""),
"timezone": period.get("timezone", ""),
"usage_rollups_full_day": traffic.get("usage_rollups_full_day") is True,
"workspace": True,
}
def workspace_errors_payload(payload: dict[str, Any]) -> dict[str, Any]:
traffic = payload.get("traffic") if isinstance(payload.get("traffic"), dict) else {}
raw_items = traffic.get("errors") if isinstance(traffic.get("errors"), list) else []
items = []
for index, raw in enumerate(raw_items):
if not isinstance(raw, dict):
continue
items.append(
{
"id": raw.get("id", index + 1),
"_node": raw.get("instance") or raw.get("node") or "-",
"created_at": raw.get("created_at") or raw.get("latest_at"),
"status_code": raw.get("status_code"),
"inbound_status_code": raw.get("inbound_status_code"),
"upstream_status_code": raw.get("upstream_status_code"),
"api_key_id": raw.get("api_key_id"),
"api_key_name": raw.get("api_key_name"),
"account_id": raw.get("account_id"),
"account_name": raw.get("account_name"),
"requested_model": raw.get("model"),
"upstream_model": raw.get("upstream_model"),
"phase": raw.get("error_source"),
"type": raw.get("error_type"),
"error_source": raw.get("error_source"),
"error_type": raw.get("error_type"),
}
)
instances = traffic.get("instances") if isinstance(traffic.get("instances"), list) else []
sources = {}
for raw in instances:
if not isinstance(raw, dict):
continue
node = safe_error_label(raw.get("instance") or raw.get("name"), "-")
sources[node] = {
"ok": raw.get("ok") is not False,
"total": as_int(raw.get("error_total")),
"fetched": sum(1 for item in items if safe_error_label(item.get("_node"), "-") == node),
"error": "unavailable" if raw.get("error") else "",
}
return {
"items": items,
"sources": sources,
"time_range": workspace_period_label(traffic),
"limit": as_int(traffic.get("limit")),
"generated_at": traffic.get("generated_at") or payload.get("generated_at"),
"workspace": True,
}
def workspace_state_summary(payload: dict[str, Any], client_error: str = "") -> str:
if client_error:
return "workspace endpoint unavailable (using last good)"
state = payload.get("state") if isinstance(payload.get("state"), dict) else {}
components = payload.get("components") if isinstance(payload.get("components"), dict) else {}
stale = sorted(
str(name)
for name, value in components.items()
if isinstance(value, dict) and value.get("stale") is True
)
if stale:
return f"workspace partial ({', '.join(stale)} stale)"
if state.get("ok") is True:
return "workspace ok"
return "workspace partial" if state.get("partial") is True else "workspace unknown"
class WorkspaceCache:
def __init__(self, url: str, timeout: int, ttl_seconds: int) -> None:
self.url = str(url or "").strip()
self.timeout = max(1, int(timeout))
self.ttl_seconds = max(1, int(ttl_seconds))
self.payload: dict[str, Any] = {}
self.fetched_at = 0.0
self.last_attempt_at = 0.0
self.retry_seconds = min(30, self.ttl_seconds)
self.error = ""
self.network_fetches = 0
self.etag = ""
self.lock = threading.RLock()
def _has_current_payload(self) -> bool:
return bool(self.payload) and workspace_traffic_is_current(self.payload)
def get(self, *, force: bool = False) -> dict[str, Any]:
with self.lock:
now = time.monotonic()
current_payload = self._has_current_payload()
if not force:
if self.error and self.last_attempt_at and now - self.last_attempt_at < self.retry_seconds:
if current_payload:
return copy.deepcopy(self.payload)
raise RuntimeError(self.error)
if (
not self.error
and current_payload
and now - self.fetched_at < self.ttl_seconds
):
return copy.deepcopy(self.payload)
self.last_attempt_at = now
self.network_fetches += 1
if not current_payload:
self.etag = ""
request_etag = self.etag
try:
if request_etag:
payload = fetch_workspace_payload(
self.url,
self.timeout,
if_none_match=request_etag,
)
else:
payload = fetch_workspace_payload(self.url, self.timeout)
except WorkspaceNotModified as unchanged:
if not self._has_current_payload():
self.etag = ""
self.error = "workspace current-day traffic unavailable"
raise RuntimeError(self.error) from unchanged
self.etag = unchanged.etag or request_etag
self.fetched_at = time.monotonic()
self.error = ""
return copy.deepcopy(self.payload)
except Exception as exc:
self.error = "workspace unavailable"
if self._has_current_payload():
return copy.deepcopy(self.payload)
self.etag = ""
raise RuntimeError(self.error) from exc
if not workspace_traffic_is_current(payload):
self.error = "workspace current-day traffic unavailable"
if self._has_current_payload():
return copy.deepcopy(self.payload)
self.etag = ""
raise RuntimeError(self.error)
self.payload = copy.deepcopy(dict(payload))
self.etag = str(getattr(payload, "etag", "") or "").strip()
self.fetched_at = time.monotonic()
self.error = ""
return copy.deepcopy(self.payload)
def logs_request_url(logs_url: str, limit: int) -> str:
parsed = urllib.parse.urlparse(logs_url)
query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)
query = [(key, value) for key, value in query if key not in {"page", "page_size", "limit"}]
query.extend((("page", "1"), ("page_size", str(max(1, min(int(limit), 1000))))))
return urllib.parse.urlunparse(parsed._replace(query=urllib.parse.urlencode(query)))
def fetch_logs_payload(logs_url: str, token: str, timeout: int, limit: int = DEFAULT_LOGS_LIMIT) -> dict[str, Any]:
headers = {"Accept": "application/json", "Accept-Encoding": "gzip"}
if str(token or "").strip():
headers["x-api-key"] = str(token).strip()
req = urllib.request.Request(logs_request_url(logs_url, limit), headers=headers)
with urllib.request.urlopen(req, timeout=timeout) as response:
return decode_json_response(response, "logs API did not return a JSON object")
def logs_envelope(payload: dict[str, Any]) -> dict[str, Any]:
data = payload.get("data")
if isinstance(data, dict) and isinstance(data.get("items"), list):
return data
return payload
def log_items(payload: dict[str, Any]) -> list[dict[str, Any]]:
items = logs_envelope(payload).get("items")
if not isinstance(items, list):
return []
return [item for item in items if isinstance(item, dict)]
def log_type_label(item: dict[str, Any]) -> str:
text = str(item.get("request_type") or "").strip().lower()
if text and text != "unknown":
return text
if item.get("openai_ws_mode"):
return "ws_v2"
if item.get("stream") is True:
return "stream"
if item.get("stream") is False:
return "sync"
return text or "-"
def log_cache_tokens(item: dict[str, Any]) -> int:
return as_int(item.get("cache_creation_tokens")) + as_int(item.get("cache_read_tokens"))
def log_total_tokens(item: dict[str, Any]) -> int:
return as_int(item.get("input_tokens")) + as_int(item.get("output_tokens")) + log_cache_tokens(item)
def log_tokens_per_second(item: dict[str, Any]) -> float:
output = as_int(item.get("output_tokens"))
duration = as_int(item.get("duration_ms"))
first = max(0, as_int(item.get("first_token_ms")))
decode_ms = duration - first
if output <= 0 or decode_ms <= 0:
return 0.0
return output * 1000.0 / decode_ms
def format_rate(value: Any) -> str:
rate = as_float(value)
if rate <= 0:
return "-"
if rate >= 100:
return f"{rate:.0f}/s"
return f"{rate:.1f}/s"
def format_seconds(value: Any) -> str:
number = as_int(value)
if number <= 0:
return "-"
return f"{number / 1000:.1f}s"
KEY_COLOR_PALETTE = (
"cyan",
"magenta",
"green",
"yellow",
"bright_blue",
"bright_cyan",
"bright_magenta",
"bright_green",
"bright_yellow",
"bright_red",
)
def key_color(name: Any) -> str:
text = str(name or "").strip()
if not text or text == "-":
return ""
return KEY_COLOR_PALETTE[zlib.crc32(text.encode("utf-8")) % len(KEY_COLOR_PALETTE)]
def admin_api_base(logs_url: str) -> str:
parsed = urllib.parse.urlparse(str(logs_url or "").strip())
if not parsed.scheme or not parsed.netloc:
return ""
path = parsed.path.rstrip("/")
if path.endswith("/usage"):
path = path[: -len("/usage")]
if not path.endswith("/admin"):
return ""
return urllib.parse.urlunparse(parsed._replace(path=path, params="", query="", fragment=""))
def fetch_admin_json(url: str, token: str, timeout: int, body: dict[str, Any] | None = None) -> dict[str, Any]:
headers = {"Accept": "application/json", "Accept-Encoding": "gzip"}
if str(token or "").strip():
headers["x-api-key"] = str(token).strip()
data = None
if body is not None:
headers["Content-Type"] = "application/json"
data = json.dumps(body).encode("utf-8")
req = urllib.request.Request(url, data=data, headers=headers)
with urllib.request.urlopen(req, timeout=timeout) as response:
return decode_json_response(response, "admin API did not return a JSON object")
def payload_data(payload: dict[str, Any]) -> dict[str, Any]:
data = payload.get("data")
return data if isinstance(data, dict) else payload
def usage_day_date(now: dt.datetime | None = None) -> str:
current = now or dt.datetime.now(dt.timezone.utc)
if current.tzinfo is None:
current = current.replace(tzinfo=dt.timezone.utc)
return current.astimezone(ZoneInfo(USAGE_TIMEZONE)).date().isoformat()
def fetch_key_usage_payload(logs_url: str, token: str, timeout: int, limit: int = 100) -> dict[str, Any]:
base = admin_api_base(logs_url)
if not base:
raise RuntimeError("cannot derive the admin API base from the logs url")
today = usage_day_date()
query = urllib.parse.urlencode({"start_date": today, "end_date": today, "granularity": "day", "limit": limit})
trend_payload = fetch_admin_json(f"{base}/dashboard/api-keys-trend?{query}", token, timeout)
trend = payload_data(trend_payload).get("trend")
points = [point for point in trend if isinstance(point, dict)] if isinstance(trend, list) else []
ids = sorted({as_int(point.get("api_key_id")) for point in points if as_int(point.get("api_key_id"))})
stats: dict[str, Any] = {}
if ids:
costs_payload = fetch_admin_json(f"{base}/dashboard/api-keys-usage", token, timeout, body={"api_key_ids": ids})
raw_stats = payload_data(costs_payload).get("stats")
if isinstance(raw_stats, dict):
stats = raw_stats
return {"date": today, "trend": points, "stats": stats}
def normalize_key_rows(payload: dict[str, Any], filter_text: str = "") -> list[dict[str, Any]]:
needle = filter_text.strip().lower()
workspace_items = payload.get("items") if isinstance(payload.get("items"), list) else []
if workspace_items:
rows = []
for item in workspace_items:
if not isinstance(item, dict):
continue
key_id = str(item.get("api_key_id") or "").strip()
node = str(item.get("instance") or item.get("node") or "-").strip() or "-"
name = str(item.get("api_key_name") or "").strip() or (f"#{key_id}" if key_id else "-")
if needle and needle not in f"{node} {name}".lower():
continue
rows.append(
{
"id": key_id,
"node": node,
"name": name,
"requests": as_int(item.get("request_count")),
"tokens": as_int(item.get("token_count")),
"cost": as_float(item.get("actual_cost") if item.get("actual_cost") is not None else item.get("cost")),
}
)
rows.sort(key=lambda row: (-row["cost"], -row["tokens"], str(row["node"]), str(row["name"]).lower()))
return rows
stats = payload.get("stats") if isinstance(payload.get("stats"), dict) else {}
merged: dict[Any, dict[str, Any]] = {}
for point in payload.get("trend") or []:
if not isinstance(point, dict):
continue
key_id = as_int(point.get("api_key_id"))
name = str(point.get("key_name") or "").strip() or (f"#{key_id}" if key_id else "-")
if needle and needle not in name.lower():
continue
row = merged.setdefault(key_id or name, {"id": key_id, "node": "-", "name": name, "requests": 0, "tokens": 0, "cost": 0.0})
row["requests"] += as_int(point.get("requests"))
row["tokens"] += as_int(point.get("tokens"))
for row in merged.values():
stat = stats.get(str(row["id"]))
if not isinstance(stat, dict):
stat = stats.get(row["id"])
if isinstance(stat, dict):
row["cost"] = as_float(stat.get("today_actual_cost"))
rows = list(merged.values())
rows.sort(key=lambda row: (-row["cost"], -row["tokens"], str(row["name"]).lower()))
return rows
def usage_period_label(payload: dict[str, Any]) -> str:
if not payload.get("workspace"):
return f"today {payload.get('date') or '-'}"
date = str(payload.get("date") or "").strip()
timezone = str(payload.get("timezone") or "").strip()
return f"today {date}{f' ({timezone})' if timezone else ''}" if date else "today"
def print_keys_once(payload: dict[str, Any]) -> None:
rows = normalize_key_rows(payload)
total_cost = sum(row["cost"] for row in rows)
period = usage_period_label(payload)
print(f"keys {period} | {len(rows)} keys | {format_cost(total_cost)}")
print("node key cost tokens req")
for row in rows:
print(
f"{str(row['node'])[:8]:<9} "
f"{str(row['name'])[:20]:<21} "
f"{format_cost(row['cost']):<10} "
f"{format_count(row['tokens']):<8} "
f"{format_count(row['requests'])}"
)
def nested_name(item: dict[str, Any], object_key: str, id_key: str, name_key: str = "") -> str:
nested = item.get(object_key)
if isinstance(nested, dict):
name = str(nested.get("name") or "").strip()
if name:
return name
if name_key:
name = str(item.get(name_key) or "").strip()
if name:
return name
identifier = as_int(item.get(id_key))
return f"#{identifier}" if identifier else "-"
def normalize_log_rows(payload: dict[str, Any], filter_text: str = "") -> list[dict[str, Any]]:
needle = filter_text.strip().lower()
rows = []
for item in log_items(payload):
node = str(item.get("_node") or item.get("instance") or item.get("node") or "-").strip() or "-"
key_name = nested_name(item, "api_key", "api_key_id", "api_key_name")
account_name = nested_name(item, "account", "account_id", "account_name")
user_name = nested_name(item, "user", "user_id", "user_name")
model = str(item.get("model") or "-")
upstream_model = str(item.get("upstream_model") or "").strip()
effort = str(item.get("reasoning_effort") or "").strip() or "-"
type_label = log_type_label(item)
row = {
"id": as_int(item.get("id")),
"node": node,
"key": key_name,
"account": account_name,
"account_rate_multiplier": optional_number(item.get("account_rate_multiplier")),
"account_rate_multiplier_cny": optional_number(item.get("account_rate_multiplier_cny")),
"account_multiplier": account_rate_value(item),
"user": user_name,
"model": model,
"upstream_model": upstream_model,
"effort": effort,
"type": type_label,
"tokens": log_total_tokens(item),
"input_tokens": as_int(item.get("input_tokens")),
"output_tokens": as_int(item.get("output_tokens")),
"cache_creation_tokens": as_int(item.get("cache_creation_tokens")),
"cache_read_tokens": as_int(item.get("cache_read_tokens")),
"cache_tokens": log_cache_tokens(item),
"cost": as_float(item.get("total_cost")),
"actual_cost": as_float(item.get("actual_cost")),
"duration_ms": as_int(item.get("duration_ms")),
"first_token_ms": as_int(item.get("first_token_ms")),
"tokens_per_second": log_tokens_per_second(item),
"created_at": str(item.get("created_at") or ""),
"time": short_time(item.get("created_at")),
"age": relative_age(item.get("created_at")),
"request_id": str(item.get("request_id") or ""),
"raw": item,
}
if needle:
haystack = " ".join(
(node, key_name, account_name, user_name, model, upstream_model, effort, type_label, row["request_id"])
).lower()
if needle not in haystack:
continue
rows.append(row)
rows.sort(key=lambda row: (row["created_at"], row["id"], row["node"]), reverse=True)
return rows
def logs_summary_line(payload: dict[str, Any], shown: int) -> str:
envelope = logs_envelope(payload)
total = as_int(envelope.get("total"))
page_size = as_int(envelope.get("page_size"))
fetched = short_time(dt.datetime.now(dt.timezone.utc).isoformat())
cost = sum(as_float(item.get("total_cost")) for item in log_items(payload))
tokens = sum(log_total_tokens(item) for item in log_items(payload))
return (
f"{fetched} requests | showing {shown}/{len(log_items(payload))} of latest {page_size or '-'} | "
f"total {format_count(total)} records | page cost {format_cost(cost)} | {format_count(tokens)} tokens"
)
def log_detail_line(row: dict[str, Any]) -> str:
model = row["model"]
if row["upstream_model"] and row["upstream_model"] != row["model"]:
model = f"{row['model']} -> {row['upstream_model']}"
if row["effort"] != "-":
model = f"{model} ({row['effort']})"
detail = (
f"{row['time']} | {row['node']} | key {row['key']} | account {row['account']} | multiplier {format_multiplier(row['account_multiplier'])} | user {row['user']} | {model} | {row['type']} | "
f"tokens in {format_count(row['input_tokens'])} out {format_count(row['output_tokens'])} "
f"cache_w {format_count(row['cache_creation_tokens'])} cache_r {format_count(row['cache_read_tokens'])} | "
f"cost {format_cost(row['cost'])} (actual {format_cost(row['actual_cost'])}) | "
f"first {format_seconds(row['first_token_ms'])} duration {format_seconds(row['duration_ms'])} "
f"{format_rate(row['tokens_per_second'])}"
)
if row["request_id"]:
detail += f" | {row['request_id']}"
return detail
def print_logs_once(payload: dict[str, Any], filter_text: str = "") -> None:
rows = normalize_log_rows(payload, filter_text)
print(logs_summary_line(payload, len(rows)))
print("node key account multiplier model effort type input output cache tokens cost first duration tok/s time age")
for row in rows:
print(
f"{row['node'][:8]:<9} "
f"{row['key'][:20]:<21} "
f"{row['account'][:20]:<21} "
f"{format_multiplier(row['account_multiplier']):<11} "
f"{row['model'][:24]:<25} "
f"{row['effort']:<7} "
f"{row['type']:<7} "
f"{format_count(row['input_tokens']):<8} "
f"{format_count(row['output_tokens']):<8} "
f"{format_count(row['cache_tokens']):<8} "
f"{format_count(row['tokens']):<8} "
f"{format_cost(row['cost']):<10} "
f"{format_seconds(row['first_token_ms']):<8} "
f"{format_seconds(row['duration_ms']):<10} "
f"{format_rate(row['tokens_per_second']):<8} "
f"{row['time']:<11} "
f"{row['age']}"
)
def errors_request_url(errors_url: str, limit: int, time_range: str = DEFAULT_ERRORS_TIME_RANGE) -> str:
parsed = urllib.parse.urlparse(errors_url)
query = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)
query = [(key, value) for key, value in query if key not in {"page", "page_size", "limit", "time_range"}]
page_size = max(1, min(int(limit), 500))
query.extend(
(
("page", "1"),
("page_size", str(page_size)),
("time_range", str(time_range or DEFAULT_ERRORS_TIME_RANGE).strip() or DEFAULT_ERRORS_TIME_RANGE),
)
)
return urllib.parse.urlunparse(parsed._replace(query=urllib.parse.urlencode(query)))
def fetch_errors_payload(
errors_url: str,
token: str,
timeout: int,
limit: int = DEFAULT_ERRORS_LIMIT,
time_range: str = DEFAULT_ERRORS_TIME_RANGE,
) -> dict[str, Any]:
headers = {"Accept": "application/json", "Accept-Encoding": "gzip"}
if str(token or "").strip():
headers["x-api-key"] = str(token).strip()
req = urllib.request.Request(errors_request_url(errors_url, limit, time_range), headers=headers)
with urllib.request.urlopen(req, timeout=timeout) as response:
return decode_json_response(response, "errors API did not return a JSON object")
def error_items(payload: dict[str, Any]) -> list[dict[str, Any]]:
data = payload.get("data")
if isinstance(data, dict) and isinstance(data.get("items"), list):
items = data.get("items")
elif isinstance(payload.get("items"), list):
items = payload.get("items")
else:
items = []
return [item for item in items if isinstance(item, dict)]
def error_total(payload: dict[str, Any]) -> int:
data = payload.get("data")
if isinstance(data, dict) and data.get("total") is not None:
return as_int(data.get("total"))
if payload.get("total") is not None:
return as_int(payload.get("total"))
return len(error_items(payload))
def default_error_sources(cn_url: str, us_url: str) -> list[tuple[str, str]]:
sources: list[tuple[str, str]] = []
if str(cn_url or "").strip():
sources.append(("cn", str(cn_url).strip()))
if str(us_url or "").strip():
sources.append(("us", str(us_url).strip()))
return sources
def fetch_merged_errors(
sources: list[tuple[str, str]],
token: str,
timeout: int,
limit: int = DEFAULT_ERRORS_LIMIT,
time_range: str = DEFAULT_ERRORS_TIME_RANGE,
) -> dict[str, Any]:
source_status: dict[str, dict[str, Any]] = {}
merged_items: list[dict[str, Any]] = []
def _one(node: str, url: str) -> tuple[str, str, dict[str, Any] | None, str]:
try:
return node, url, fetch_errors_payload(url, token, timeout, limit, time_range), ""
except Exception as exc:
return node, url, None, str(exc)
if not sources:
return {"items": [], "sources": {}, "time_range": time_range, "limit": limit}
with ThreadPoolExecutor(max_workers=max(1, len(sources))) as pool:
futures = [pool.submit(_one, node, url) for node, url in sources]
for future in as_completed(futures):
node, url, payload, error = future.result()
if payload is None:
source_status[node] = {"ok": False, "total": 0, "fetched": 0, "error": "unavailable"}
continue
items = error_items(payload)
for item in items:
row = dict(item)
row["_node"] = node
merged_items.append(row)
source_status[node] = {
"ok": True,
"total": error_total(payload),
"fetched": len(items),
"error": "",
}
merged_items.sort(
key=lambda item: (str(item.get("created_at") or ""), as_int(item.get("id")), str(item.get("_node") or "")),
reverse=True,
)
return {
"items": merged_items,
"sources": source_status,
"time_range": time_range,
"limit": limit,
"generated_at": dt.datetime.now(dt.timezone.utc).isoformat(),
}
def safe_error_label(value: Any, fallback: str = "-") -> str:
text = re.sub(r"[\x00-\x1f\x7f]+", " ", str(value or "")).strip()
if (
not text
or not ERROR_DISPLAY_LABEL_RE.fullmatch(text)
or ERROR_URL_RE.search(text)
or ERROR_EMAIL_RE.search(text)
or ERROR_SECRET_RE.search(text)
or ERROR_OPAQUE_RE.search(text)
):
return fallback
return text
def safe_error_timestamp(value: Any) -> str:
parsed = parse_time(value)
if parsed is None:
return ""
return parsed.astimezone(dt.timezone.utc).isoformat(timespec="seconds").replace("+00:00", "Z")
def normalize_error_rows(payload: dict[str, Any], filter_text: str = "") -> list[dict[str, Any]]:
needle = filter_text.strip().lower()
rows = []
for item in payload.get("items") or []:
if not isinstance(item, dict):
continue
key_id = as_int(item.get("api_key_id"))
account_id = as_int(item.get("account_id"))
node = safe_error_label(item.get("_node") or item.get("node"), "-")
key_name = safe_error_label(
item.get("api_key_name"),
f"#{key_id}" if key_id else "-",
)
account_name = safe_error_label(
item.get("account_name"),
f"#{account_id}" if account_id else "-",
)
model = safe_error_label(item.get("requested_model") or item.get("model"), "-")
upstream_model = safe_error_label(item.get("upstream_model"), "")
phase = safe_error_label(item.get("phase") or item.get("error_source"), "-")
error_type = safe_error_label(item.get("type") or item.get("error_type"), "-")
source = safe_error_label(item.get("error_source"), "-")
owner = safe_error_label(item.get("error_owner"), "-")
platform = safe_error_label(item.get("platform"), "-")
group_name = safe_error_label(item.get("group_name"), "-")
created_at = safe_error_timestamp(item.get("created_at"))
row = {
"id": as_int(item.get("id")),
"node": node,
"status_code": as_int(item.get("status_code")),
"inbound_status_code": as_int(item.get("inbound_status_code")),
"upstream_status_code": as_int(item.get("upstream_status_code")),
"key": key_name,
"account": account_name,
"user": "-",
"model": model,
"upstream_model": upstream_model,
"phase": phase,
"type": error_type,
"owner": owner,
"source": source,
"platform": platform,
"group": group_name,
"message": "",
"request_id": "",
"client_request_id": "",
"created_at": created_at,
"time": short_time(created_at),
"age": relative_age(created_at),
"resolved": bool(item.get("resolved")),
}
if needle:
haystack = " ".join(
(
node,
key_name,
account_name,
model,
upstream_model,
phase,
error_type,
owner,
source,
platform,
group_name,
str(row["status_code"]),
)
).lower()
if needle not in haystack:
continue
rows.append(row)
rows.sort(key=lambda row: (row["created_at"], row["id"], row["node"]), reverse=True)
return rows
def errors_summary_line(payload: dict[str, Any], shown: int) -> str:
sources = payload.get("sources") if isinstance(payload.get("sources"), dict) else {}
source_bits = []
for node in sorted(sources.keys()):
info = sources.get(node) if isinstance(sources.get(node), dict) else {}
if info.get("ok"):
source_bits.append(f"{node} {as_int(info.get('fetched'))}/{as_int(info.get('total'))}")
else:
source_bits.append(f"{node} err")
fetched = short_time(payload.get("generated_at") or dt.datetime.now(dt.timezone.utc).isoformat())
time_range = str(payload.get("time_range") or DEFAULT_ERRORS_TIME_RANGE)
source_text = ", ".join(source_bits) if source_bits else "no sources"
return f"{fetched} errors | showing {shown}/{len(payload.get('items') or [])} | {time_range} | {source_text}"
def error_detail_line(row: dict[str, Any]) -> str:
model = row["model"]
if row["upstream_model"] and row["upstream_model"] != row["model"]:
model = f"{row['model']} -> {row['upstream_model']}"
message = row["message"] or "-"
if len(message) > 180:
message = message[:177] + "..."
status_path = [
f"final {row['status_code']}" if row.get("status_code") else "",
f"in {row['inbound_status_code']}" if row.get("inbound_status_code") else "",
f"up {row['upstream_status_code']}" if row.get("upstream_status_code") else "",
]
status_path_text = "/".join(value for value in status_path if value) or "-"
detail = (
f"{row['time']} | {row['node']} | {status_path_text} | key {row['key']} | account {row['account']} | "
f"user {row['user']} | {model} | {row['platform']} | phase {row['phase']} | type {row['type']} | "
f"owner {row['owner']} | source {row['source']} | group {row['group']} | {message}"
)
if row["resolved"]:
detail += " | resolved"
return detail
def print_errors_once(payload: dict[str, Any], filter_text: str = "") -> None:
rows = normalize_error_rows(payload, filter_text)
print(errors_summary_line(payload, len(rows)))
sources = payload.get("sources") if isinstance(payload.get("sources"), dict) else {}
for node in sorted(sources.keys()):
info = sources.get(node) if isinstance(sources.get(node), dict) else {}
if info.get("ok"):
print(f"{node}: ok fetched {as_int(info.get('fetched'))} total {as_int(info.get('total'))}")
else:
print(f"{node}: error {info.get('error') or 'unknown'}")
print("node status key account model phase type owner time age")
for row in rows:
print(
f"{row['node']:<5} "
f"{row['status_code']:<7} "
f"{row['key'][:20]:<21} "
f"{row['account'][:20]:<21} "
f"{row['model'][:24]:<25} "
f"{row['phase'][:9]:<10} "
f"{row['type'][:13]:<14} "
f"{row['owner'][:8]:<9} "
f"{row['time']:<11} "
f"{row['age']}"
)
def kind_label(kind: Any) -> str:
return {
"quota_limited": "quota",
"pending_quota": "pending",
"daily_limited": "daily",
"pay_as_you_go": "usage",
"disabled": "disabled",
}.get(str(kind or ""), str(kind or "unknown"))
def provider_label(account: dict[str, Any]) -> str:
text = str(account.get("provider") or account.get("platform") or "").strip().lower()
if not text:
return "-"
if "anthropic" in text or "claude" in text:
return "anthropic"
if "openai" in text or "chatgpt" in text:
return "openai"
return text
def routing_group_sort_rank(value: Any) -> int:
return {"sfast": 0, "fast": 1, "slow": 2, "id": 3, "": 4, "-": 4}.get(str(value or "").strip().lower(), 4)
def status_kind(value: Any) -> str:
text = str(value or "").strip().lower()
if text in MONITOR_OK_STATUSES:
return "ok"
if text in MONITOR_FAILED_STATUSES:
return "failed"
return "unknown"
def format_latency(value: Any) -> str:
number = as_int(value)
return "-" if number <= 0 else f"{number}ms"
def monitor_items(status_payload: dict[str, Any]) -> list[dict[str, Any]]:
monitors = status_payload.get("channel_monitors") if isinstance(status_payload.get("channel_monitors"), dict) else {}
items = []
for item in monitors.get("items") or []:
if isinstance(item, dict):
items.append(item)
return items
def monitor_summary(status_payload: dict[str, Any], error: str = "") -> str:
if error:
return f"monitors error: {error}"
monitors = status_payload.get("channel_monitors") if isinstance(status_payload.get("channel_monitors"), dict) else {}
if not monitors:
return "monitors -"
enabled = as_int(monitors.get("enabled"))
ok = as_int(monitors.get("latest_ok"))
failed = as_int(monitors.get("latest_failed"))
unknown = as_int(monitors.get("latest_unknown"))
checked = short_time(monitors.get("latest_checked_max"))
return f"monitors {ok} ok / {failed} failed / {unknown} unknown ({enabled} enabled, {checked})"
def text_tokens(value: Any) -> set[str]:
tokens = set()
for token in "".join(ch.lower() if ch.isalnum() else " " for ch in str(value or "")).split():
if token and token not in MONITOR_STOPWORDS and not token.isdigit():
tokens.add(token)
return tokens
def matching_monitor(row: dict[str, Any], status_payload: dict[str, Any]) -> dict[str, Any] | None:
base_url_hash = str(row.get("base_url_hash") or "").strip()
if base_url_hash:
for item in monitor_items(status_payload):
if str(item.get("base_url_hash") or "").strip() == base_url_hash:
return item
account_tokens = text_tokens(row.get("name"))
if not account_tokens:
return None
best: tuple[int, dict[str, Any]] | None = None
for item in monitor_items(status_payload):
monitor_tokens = text_tokens(item.get("name"))
if not monitor_tokens:
continue
overlap = account_tokens & monitor_tokens
if not overlap:
continue
subset_bonus = 10 if account_tokens <= monitor_tokens or monitor_tokens <= account_tokens else 0
score = subset_bonus + len(overlap)
if best is None or score > best[0]:
best = (score, item)
return None if best is None else best[1]
def monitor_detail(row: dict[str, Any], status_payload: dict[str, Any]) -> str:
item = matching_monitor(row, status_payload)
if not item:
return "monitor -"
status = str(item.get("latest_status") or "unknown")
latency = format_latency(item.get("latency_ms"))
checked = short_time(item.get("checked_at"))
message = str(item.get("message") or "").strip()
detail = f"monitor {status} {latency} checked {checked}"
if message:
detail += f" {message}"
return detail
def monitor_availability(row: dict[str, Any], status_payload: dict[str, Any]) -> str:
item = matching_monitor(row, status_payload)
if not item:
return "-"
if item.get("enabled") is False:
return "disabled"
raw_status = str(item.get("latest_status") or "").strip().lower()
if not raw_status:
return "unknown"
kind = status_kind(raw_status)
if kind == "ok":
return "ok"
if kind == "failed":
return "failed"
return raw_status
def window_for(account: dict[str, Any], window_id: str) -> dict[str, Any]:
for window in account.get("windows") or []:
if isinstance(window, dict) and window.get("id") == window_id:
return window
return {}
def window_cell(account: dict[str, Any], window_id: str) -> str:
window = window_for(account, window_id)
if not window or window.get("used_percent") is None:
return "-"
return f"{format_percent(window.get('used_percent'))}/{format_percent(window.get('remaining_percent'))}"
def daily_quota_cell(account: dict[str, Any]) -> str:
limit = account.get("daily_quota_limit")
used = account.get("daily_quota_used")
if limit is None:
return "-"
return f"{used or 0}/{limit}"
def reset_cell(account: dict[str, Any]) -> str:
daily_reset = short_time(account.get("daily_quota_reset_at"))
if daily_reset != "-":
return daily_reset
five_hour = window_for(account, "five-hour")
weekly = window_for(account, "weekly")
for window in (five_hour, weekly):
reset = short_time(window.get("reset"))
if reset != "-":
return reset
return "-"
def normalize_account_rows(
payload: dict[str, Any],
filter_text: str = "",
pricing_payload: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
needle = filter_text.strip().lower()
pricing_sources = pricing_source_lookup(pricing_payload or {})
rows = []
for account in payload.get("accounts") or []:
if not isinstance(account, dict):
continue
pricing_source = pricing_source_for_account(account.get("name"), pricing_sources)
haystack = " ".join(
str(account.get(key) or "")
for key in (
"id",
"name",
"routing_group",
"provider",
"kind",
"status",
"account_type",
"platform",
"plan",
"daily_quota_timezone",
)
).lower()
if pricing_source:
haystack = f"{haystack} {pricing_source.get('name') or ''} {pricing_source.get('status') or ''}".lower()
if needle and needle not in haystack:
continue
routing_group = str(account.get("routing_group") or "-")
rows.append(
{
"id": as_int(account.get("id")),
"name": str(account.get("name") or ""),
"routing_group": routing_group,
"base_url_hash": str(account.get("base_url_hash") or ""),
"pricing_source": str(pricing_source.get("name") or "-") if pricing_source else "-",
"pricing_cny": pricing_source.get("balance_cny") if pricing_source and pricing_source.get("status") == "healthy" else None,
"pricing_state": str(pricing_source.get("status") or "-") if pricing_source else "-",
"pricing_balance": pricing_source.get("balance") if pricing_source else None,
"pricing_unit": str(pricing_source.get("unit") or "-") if pricing_source else "-",
"pricing_updated": str(pricing_source.get("updated") or "-") if pricing_source else "-",
"account_rate_multiplier": optional_number(account.get("account_rate_multiplier")),
"account_rate_multiplier_cny": optional_number(account.get("account_rate_multiplier_cny")),
"account_multiplier": account_rate_value(account),
"provider": provider_label(account),
"kind": str(account.get("kind") or "unknown"),
"kind_label": kind_label(account.get("kind")),
"status": str(account.get("status") or ""),
"account_type": str(account.get("account_type") or ""),
"plan": str(account.get("plan") or ""),
"priority": account.get("priority"),
"today_cost_usd": as_float(account.get("today_cost_usd")),
"today_actual_cost_usd": as_float(account.get("today_actual_cost_usd")),
"today_tokens": as_int(account.get("today_tokens")),
"today_requests": as_int(account.get("today_requests")),
"daily_quota": account.get("daily_quota") if isinstance(account.get("daily_quota"), dict) else {},
"daily_quota_limit": account.get("daily_quota_limit"),
"daily_quota_used": account.get("daily_quota_used"),
"daily_quota_remaining": account.get("daily_quota_remaining"),
"daily_quota_used_percent": account.get("daily_quota_used_percent"),
"daily_quota_reset_at": account.get("daily_quota_reset_at"),
"daily_quota_cell": daily_quota_cell(account),
"quota_used_percent_max": account.get("quota_used_percent_max"),
"five_hour": window_cell(account, "five-hour"),
"weekly": window_cell(account, "weekly"),
"reset": reset_cell(account),
"latest_usage_at": short_time(account.get("latest_usage_at") or account.get("last_used_at")),
"error": str(account.get("error") or ""),
"raw": account,
}
)
rows.sort(
key=lambda item: (
routing_group_sort_rank(item["routing_group"]),
1 if item["kind"] == "disabled" else 0,
-as_float(item["today_cost_usd"]),
-as_int(item["today_tokens"]),
-as_float(item["daily_quota_used_percent"]),
-as_float(item["quota_used_percent_max"]),
str(item["name"]).lower(),
)
)
return rows
def pricing_status(source: dict[str, Any]) -> str:
if str(source.get("last_error") or "").strip():
return "error"
state = str(source.get("health_state") or "").strip().lower()
return state or ("refreshing" if source.get("refreshing") else "unknown")
def normalize_pricing_rows(payload: dict[str, Any], filter_text: str = "") -> list[dict[str, Any]]:
needle = filter_text.strip().lower()
rows: list[dict[str, Any]] = []
for source in payload.get("sources") or []:
if not isinstance(source, dict):
continue
balance = source.get("balance") if isinstance(source.get("balance"), dict) else {}
balance_is_available = source.get("balance_available") is not False
row = {
"name": str(source.get("name") or "-"),
"kind": str(source.get("source_kind") or "-"),
"balance": optional_number(balance.get("available")) if balance_is_available else None,
"balance_cny": optional_number(balance.get("available_cny")) if balance_is_available else None,
"unit": str(balance.get("unit") or "-"),
"status": pricing_status(source),
"updated_at": source.get("last_success_at"),
"updated": short_time(source.get("last_success_at")),
"age": relative_age(source.get("last_success_at")),
"error": str(source.get("last_error") or "").strip(),
"raw": source,
}
haystack = " ".join(
str(row[key] or "") for key in ("name", "kind", "unit", "status", "error")
).lower()
if needle and needle not in haystack:
continue
rows.append(row)
rows.sort(key=lambda row: (row["status"] != "healthy", row["name"].lower()))
return rows
def pricing_source_key(value: Any) -> str:
return re.sub(r"[^a-z0-9]+", "", str(value or "").strip().casefold())
def pricing_source_lookup(payload: dict[str, Any]) -> dict[str, dict[str, Any] | None]:
lookup: dict[str, dict[str, Any] | None] = {}
for source in normalize_pricing_rows(payload):
key = pricing_source_key(source.get("name"))
if not key:
continue
if key in lookup:
lookup[key] = None
else:
lookup[key] = source
return lookup
def pricing_source_for_account(
account_name: Any, pricing_sources: dict[str, dict[str, Any] | None]
) -> dict[str, Any] | None:
match = PRICING_ACCOUNT_SOURCE_RE.fullmatch(str(account_name or "").strip())
if not match:
return None
return pricing_sources.get(pricing_source_key(match.group("source")))
def pricing_summary(payload: dict[str, Any], rows: list[dict[str, Any]] | None = None) -> str:
source_rows = rows if rows is not None else normalize_pricing_rows(payload)
healthy = sum(row["status"] == "healthy" for row in source_rows)
cny_values = [
row["balance_cny"]
for row in source_rows
if row["status"] == "healthy" and row["balance_cny"] is not None
]
total_cny = sum(cny_values) if cny_values else None
total_label = f" | CNY {format_cny(total_cny)}" if total_cny is not None else ""
return f"sources {healthy}/{len(source_rows)} healthy{total_label}"
def pricing_detail_line(row: dict[str, Any]) -> str:
detail = (
f"{row['name']} | {row['kind']} | balance {format_amount(row['balance'])} {row['unit']} | "
f"CNY {format_cny(row['balance_cny'])} | {row['status']} | "
f"updated {row['updated']} ({row['age']})"
)
if row["error"]:
detail += f" | {row['error']}"
return detail
def account_pricing_detail(row: dict[str, Any]) -> str:
source = str(row.get("pricing_source") or "-")
if source == "-":
return "source -"
cny = format_cny(row.get("pricing_cny"))
raw_balance = format_amount(row.get("pricing_balance"))
unit = str(row.get("pricing_unit") or "-")
state = str(row.get("pricing_state") or "-")
updated = str(row.get("pricing_updated") or "-")
return f"source {source} | CNY {cny} | raw {raw_balance} {unit} | {state} | updated {updated}"
def print_pricing_once(payload: dict[str, Any], filter_text: str = "") -> None:
rows = normalize_pricing_rows(payload, filter_text)
print(f"{short_time(payload.get('generated_at'))} | {pricing_summary(payload, rows)}")
print("source kind balance cny unit status updated age")
for row in rows:
print(
f"{row['name']:<30} "
f"{row['kind']:<15} "
f"{format_amount(row['balance']):<19} "
f"{format_cny(row['balance_cny']):<19} "
f"{row['unit']:<9} "
f"{row['status']:<11} "
f"{row['updated']:<12} "
f"{row['age']}"
)
def summary_line(payload: dict[str, Any]) -> str:
totals = payload.get("totals") if isinstance(payload.get("totals"), dict) else {}
generated = short_time(payload.get("generated_local") or payload.get("generated_at"))
cached = "cached" if payload.get("cached") else "live"
return (
f"{generated} {cached} | "
f"{as_int(totals.get('usable_accounts'))}/{as_int(totals.get('total_accounts'))} usable | "
f"today {format_cost(totals.get('today_cost_usd'))} | "
f"{format_count(totals.get('today_tokens'))} tokens | "
f"{format_count(totals.get('today_requests'))} req"
)
def print_once(
payload: dict[str, Any],
filter_text: str = "",
status_payload: dict[str, Any] | None = None,
status_error: str = "",
pricing_payload: dict[str, Any] | None = None,
pricing_error: str = "",
) -> None:
print(summary_line(payload))
if status_payload or status_error:
print(monitor_summary(status_payload or {}, status_error))
if pricing_error:
print(f"sources: {pricing_error}")
elif pricing_payload:
print(pricing_summary(pricing_payload))
print("name provider group source multiplier src cny src state daily today tokens req kind 5h 7d reset status availability")
for row in normalize_account_rows(payload, filter_text, pricing_payload):
print(
f"{row['name'][:30]:<30} "
f"{row['provider']:<9} "
f"{row['routing_group']:<6} "
f"{row['pricing_source']:<16} "
f"{format_multiplier(row['account_multiplier']):<11} "
f"{format_cny(row['pricing_cny']):<13} "
f"{row['pricing_state']:<12} "
f"{row['daily_quota_cell']:<10} "
f"{format_cost(row['today_cost_usd']):<10} "
f"{format_count(row['today_tokens']):<8} "
f"{format_count(row['today_requests']):<5} "
f"{row['kind_label']:<9} "
f"{row['five_hour']:<9} "
f"{row['weekly']:<9} "
f"{row['reset']:<11} "
f"{row['status']:<10} "
f"{monitor_availability(row, status_payload or {})}"
)
def run_textual(
api_url: str,
status_url: str,
pricing_url: str,
logs_url: str,
logs_token: str,
errors_cn_url: str,
errors_us_url: str,
refresh_seconds: int,
logs_refresh_seconds: int,
errors_refresh_seconds: int,
timeout: int,
logs_limit: int,
errors_limit: int,
errors_time_range: str,
version_message: str = "",
start_page: str = "dashboard",
workspace_url: str = "",
legacy_direct: bool = False,
) -> int:
try:
from rich.text import Text
from textual.app import App, ComposeResult
from textual.screen import Screen
from textual.widgets import DataTable, Footer, Header, Input, Static
except ImportError:
print("Textual is required. Run with: uv run --with textual python sub2api_quota_tui.py", file=sys.stderr)
return 2
workspace_cache = WorkspaceCache(
workspace_url or DEFAULT_WORKSPACE_URL,
timeout,
min(refresh_seconds, logs_refresh_seconds, errors_refresh_seconds),
)
def load_accounts(*, force: bool = False) -> dict[str, Any]:
if legacy_direct:
return fetch_payload(api_url, timeout, refresh=force)
return workspace_accounts_payload(workspace_cache.get(force=force))
def load_status() -> tuple[dict[str, Any], str]:
if legacy_direct:
return fetch_optional_payload(status_url, timeout)
payload = workspace_cache.get()
return workspace_status_payload(payload), workspace_cache.error
def load_pricing() -> tuple[dict[str, Any], str]:
if legacy_direct:
return fetch_optional_pricing_payload(pricing_url, timeout)
payload = workspace_cache.get()
return workspace_pricing_payload(payload), workspace_cache.error
def load_logs(*, force: bool = False) -> dict[str, Any]:
if legacy_direct:
return fetch_logs_payload(logs_url, logs_token, timeout, logs_limit)
return workspace_logs_payload(workspace_cache.get(force=force))
def load_keys() -> dict[str, Any]:
if legacy_direct:
return fetch_key_usage_payload(logs_url, logs_token, timeout)
return workspace_keys_payload(workspace_cache.get())
def load_errors(*, force: bool = False) -> dict[str, Any]:
if legacy_direct:
return fetch_merged_errors(
default_error_sources(errors_cn_url, errors_us_url),
logs_token,
timeout,
errors_limit,
errors_time_range,
)
return workspace_errors_payload(workspace_cache.get(force=force))
class DashboardScreen(Screen[None]):
AUTO_FOCUS = "#accounts"
BINDINGS = [
("r", "refresh", "Refresh"),
("/", "focus_filter", "Global filter"),
("a", "focus_accounts", "Accounts"),
("p", "show_pricing", "Sources"),
("k", "focus_keys", "Key usage"),
("l", "focus_logs", "Requests"),
("e", "focus_errors", "Errors"),
]
def __init__(self) -> None:
super().__init__(classes="dashboard")
self.accounts_payload: dict[str, Any] = {}
self.status_payload: dict[str, Any] = {}
self.pricing_payload: dict[str, Any] = {}
self.pricing_error = ""
self.monitor_error = ""
self.accounts_error = ""
self.logs_payload: dict[str, Any] = {}
self.logs_error = ""
self.errors_payload: dict[str, Any] = {}
self.errors_error = ""
self.keys_payload: dict[str, Any] = {}
self.keys_error = ""
self.account_rows: list[dict[str, Any]] = []
self.key_rows: list[dict[str, Any]] = []
self.log_rows: list[dict[str, Any]] = []
self.error_rows: list[dict[str, Any]] = []
self.account_by_key: dict[str, dict[str, Any]] = {}
self.key_by_key: dict[str, dict[str, Any]] = {}
self.log_by_key: dict[str, dict[str, Any]] = {}
self.error_by_key: dict[str, dict[str, Any]] = {}
self.active_table = "accounts"
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield Static("", id="summary")
yield Input(placeholder="filter all tables", id="filter")
yield DataTable(id="accounts")
yield DataTable(id="keys")
yield DataTable(id="logs")
yield DataTable(id="errors")
yield Static("", id="detail")
yield Static("", id="status")
yield Footer()
@staticmethod
def configure_table(table: DataTable, columns: tuple[str, ...]) -> None:
table.cursor_type = "row"
table.zebra_stripes = True
# Content-sized columns preserve complete values on wide terminals;
# narrow terminals use DataTable's horizontal scrolling instead.
for label in columns:
table.add_column(label)
def on_mount(self) -> None:
self.app.sub_title = "Dashboard"
self.configure_table(
self.query_one("#accounts", DataTable),
("ACCOUNT", "Group", "Source", "Multiplier", "Src CNY", "Src state", "Today", "Daily", "5h", "7d", "Avail"),
)
self.configure_table(
self.query_one("#keys", DataTable),
("Node", "KEY", "Day total" if not legacy_direct else "Today", "Tokens", "Req"),
)
self.configure_table(
self.query_one("#logs", DataTable),
(
"Node",
"REQUEST KEY",
"Account",
"Multiplier",
"Model",
"First",
"Duration",
"Tok/s",
"Input",
"Output",
"Cache",
"Tokens",
"Cost",
"Time",
"Age",
),
)
self.configure_table(
self.query_one("#errors", DataTable),
("Node", "Status", "Key", "Account", "Model", "Time", "Age"),
)
self.refresh_all(refresh=legacy_direct)
self.set_interval(refresh_seconds, self.refresh_accounts)
self.set_interval(refresh_seconds, self.refresh_pricing)
self.set_interval(logs_refresh_seconds, self.refresh_logs)
self.set_interval(errors_refresh_seconds, self.refresh_errors)
self.focus_table("accounts")
def action_refresh(self) -> None:
self.refresh_all(refresh=True)
def action_focus_filter(self) -> None:
self.query_one("#filter", Input).focus()
def action_focus_accounts(self) -> None:
self.focus_table("accounts")
def action_show_pricing(self) -> None:
self.app.switch_screen(PricingScreen())
def action_focus_keys(self) -> None:
table = self.query_one("#keys", DataTable)
if table.display:
self.focus_table("keys")
def action_focus_logs(self) -> None:
self.focus_table("logs")
def action_focus_errors(self) -> None:
self.focus_table("errors")
def focus_table(self, table_id: str) -> None:
self.active_table = table_id
self.query_one(f"#{table_id}", DataTable).focus()
self.render_active_detail()
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "filter":
self.render_tables()
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "filter":
self.focus_table(self.active_table)
def refresh_all(self, refresh: bool = False) -> None:
mode = "direct sources" if legacy_direct else "aggregated workspace"
self.query_one("#status", Static).update(f"refreshing {mode}...")
self.refresh_accounts(refresh=refresh)
self.refresh_pricing()
self.refresh_logs()
self.refresh_errors()
def refresh_accounts(self, refresh: bool = False) -> None:
self.accounts_error = ""
try:
self.accounts_payload = load_accounts(force=refresh)
self.status_payload, self.monitor_error = load_status()
except Exception as exc:
self.accounts_error = str(exc)
self.render_accounts()
self.render_meta()
def refresh_pricing(self) -> None:
self.pricing_error = ""
try:
self.pricing_payload, self.pricing_error = load_pricing()
except Exception as exc:
self.pricing_payload = {}
self.pricing_error = str(exc)
self.render_accounts()
self.render_meta()
def refresh_logs(self) -> None:
self.logs_error = ""
if legacy_direct and not str(logs_token or "").strip():
self.logs_payload = {}
self.logs_error = "admin token not configured"
else:
try:
self.logs_payload = load_logs()
except Exception as exc:
self.logs_error = str(exc)
self.render_logs()
self.refresh_keys()
self.render_meta()
def refresh_keys(self) -> None:
self.keys_error = ""
if legacy_direct and not str(logs_token or "").strip():
self.keys_payload = {}
self.keys_error = "admin token not configured"
else:
try:
self.keys_payload = load_keys()
except Exception as exc:
self.keys_payload = {}
self.keys_error = str(exc)
self.render_keys()
def refresh_errors(self) -> None:
self.errors_error = ""
if legacy_direct and not str(logs_token or "").strip():
self.errors_payload = {}
self.errors_error = "admin token not configured"
else:
try:
self.errors_payload = load_errors()
except Exception as exc:
self.errors_error = str(exc)
self.render_errors()
self.render_meta()
def render_tables(self) -> None:
self.render_accounts()
self.render_keys()
self.render_logs()
self.render_errors()
self.render_meta()
self.render_active_detail()
def render_accounts(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.account_rows = normalize_account_rows(
self.accounts_payload, filter_text, self.pricing_payload
)
table = self.query_one("#accounts", DataTable)
table.clear()
self.account_by_key = {}
for index, row in enumerate(self.account_rows):
key = f"account-{row['id']}-{index}"
self.account_by_key[key] = row
table.add_row(
row["name"],
row["routing_group"],
row["pricing_source"],
format_multiplier(row["account_multiplier"]),
format_cny(row["pricing_cny"]),
row["pricing_state"],
format_cost(row["today_cost_usd"]),
row["daily_quota_cell"],
row["five_hour"],
row["weekly"],
monitor_availability(row, self.status_payload),
key=key,
)
def render_keys(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.key_rows = normalize_key_rows(self.keys_payload, filter_text)
table = self.query_one("#keys", DataTable)
table.clear()
self.key_by_key = {}
for index, row in enumerate(self.key_rows):
key = f"key-{row['id']}-{index}"
self.key_by_key[key] = row
color = key_color(row["name"])
table.add_row(
row["node"],
Text(str(row["name"]), style=color) if color else str(row["name"]),
format_cost(row["cost"]),
format_count(row["tokens"]),
format_count(row["requests"]),
key=key,
)
table.display = bool(self.key_rows) and self.app.size.height >= 20
def render_logs(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.log_rows = normalize_log_rows(self.logs_payload, filter_text)
table = self.query_one("#logs", DataTable)
table.clear()
self.log_by_key = {}
for index, row in enumerate(self.log_rows):
key = f"log-{row['id']}-{index}"
self.log_by_key[key] = row
color = key_color(row["key"])
table.add_row(
row["node"],
Text(row["key"], style=color) if color else row["key"],
row["account"],
format_multiplier(row["account_multiplier"]),
row["model"],
format_seconds(row["first_token_ms"]),
format_seconds(row["duration_ms"]),
format_rate(row["tokens_per_second"]),
format_count(row["input_tokens"]),
format_count(row["output_tokens"]),
format_count(row["cache_tokens"]),
format_count(row["tokens"]),
format_cost(row["cost"]),
row["time"],
row["age"],
key=key,
)
def render_errors(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.error_rows = normalize_error_rows(self.errors_payload, filter_text)
table = self.query_one("#errors", DataTable)
table.clear()
self.error_by_key = {}
for index, row in enumerate(self.error_rows):
key = f"error-{row['node']}-{row['id']}-{index}"
self.error_by_key[key] = row
table.add_row(
row["node"],
str(row["status_code"]),
row["key"],
row["account"],
row["model"],
row["time"],
row["age"],
key=key,
)
def errors_total(self) -> int:
sources = self.errors_payload.get("sources") if isinstance(self.errors_payload.get("sources"), dict) else {}
totals = [
as_int(info.get("total"))
for info in sources.values()
if isinstance(info, dict) and info.get("ok")
]
return sum(totals) if totals else len(self.errors_payload.get("items") or [])
def render_meta(self) -> None:
totals = self.accounts_payload.get("totals") if isinstance(self.accounts_payload.get("totals"), dict) else {}
account_total = as_int(totals.get("total_accounts")) or len(self.accounts_payload.get("accounts") or [])
usable = as_int(totals.get("usable_accounts"))
logs_total = as_int(logs_envelope(self.logs_payload).get("total"))
if not logs_total:
logs_total = len(log_items(self.logs_payload))
key_summary = "keys -"
if self.key_rows:
top_key = self.key_rows[0]
key_summary = f"keys {top_key['name']} {format_cost(top_key['cost'])}"
summary = Text()
summary.append(
f"accounts {len(self.account_rows)}/{account_total} ({usable} usable, {format_cost(totals.get('today_cost_usd'))})",
style="green",
)
summary.append(" | ", style="dim")
if self.pricing_error:
summary.append("sources unavailable", style="bright_blue")
else:
summary.append(pricing_summary(self.pricing_payload), style="bright_blue")
summary.append(" | ", style="dim")
key_scope = "keys day total" if not legacy_direct else "keys"
request_scope = "request sample" if not legacy_direct else "requests"
error_scope = "error sample" if not legacy_direct else "errors"
summary.append(f"{key_scope} {len(self.key_rows)} ({key_summary.removeprefix('keys ')})", style="yellow")
summary.append(" | ", style="dim")
summary.append(f"{request_scope} {len(self.log_rows)}/{logs_total}", style="magenta")
summary.append(" | ", style="dim")
summary.append(f"{error_scope} {len(self.error_rows)}/{self.errors_total()}", style="red")
self.query_one("#summary", Static).update(summary)
status_bits = [version_message]
if not legacy_direct:
try:
status_bits.append(workspace_state_summary(workspace_cache.get(), workspace_cache.error))
except Exception:
status_bits.append("workspace unavailable")
if self.accounts_error:
status_bits.append(f"accounts: {self.accounts_error}")
else:
status_bits.extend(
(
monitor_summary(self.status_payload, self.monitor_error),
f"source {self.accounts_payload.get('source_name') or '-'}",
)
)
if self.pricing_error:
status_bits.append(f"sources: {self.pricing_error}")
elif self.pricing_payload:
endpoint = pricing_url if legacy_direct else workspace_cache.url
status_bits.append(pricing_summary(self.pricing_payload) + f" | {endpoint}")
shared_admin_error = self.logs_error and self.logs_error == self.errors_error
if shared_admin_error:
status_bits.append(f"requests/errors: {self.logs_error}")
else:
if self.logs_error:
status_bits.append(f"requests: {self.logs_error}")
if self.errors_error:
status_bits.append(f"errors: {self.errors_error}")
if self.keys_error and self.keys_error != self.logs_error:
status_bits.append(f"keys: {self.keys_error}")
if not self.errors_error and self.errors_payload:
sources = self.errors_payload.get("sources") if isinstance(self.errors_payload.get("sources"), dict) else {}
source_bits = []
for node in sorted(sources):
info = sources.get(node) if isinstance(sources.get(node), dict) else {}
if info:
source_bits.append(f"{node} {'ok' if info.get('ok') else 'err'}")
if source_bits:
status_bits.append("errors " + "/".join(source_bits))
self.query_one("#status", Static).update(" | ".join(bit for bit in status_bits if bit) or "ready")
def render_active_detail(self) -> None:
rows_by_table = {
"accounts": self.account_rows,
"keys": self.key_rows,
"logs": self.log_rows,
"errors": self.error_rows,
}
rows = rows_by_table[self.active_table]
if not rows:
self.query_one("#detail", Static).update(f"no {self.active_table}")
return
self.render_detail(self.active_table, rows[0])
def render_detail(self, table_id: str, row: dict[str, Any]) -> None:
if table_id == "accounts":
detail = (
f"{row['name']} | {row['provider']} | group {row['routing_group']} | {row['kind_label']} | "
f"multiplier {format_multiplier(row['account_multiplier'])} | "
f"daily {row['daily_quota_cell']} | "
f"5h {row['five_hour']} | "
f"7d {row['weekly']} | "
f"today {format_cost(row['today_cost_usd'])}, "
f"{format_count(row['today_tokens'])} tokens, {format_count(row['today_requests'])} req | "
f"{account_pricing_detail(row)} | "
f"{monitor_detail(row, self.status_payload)}"
)
error = str(row.get("error") or "").strip()
if error:
detail += f" | {error}"
elif table_id == "keys":
period_label = usage_period_label(self.keys_payload)
if self.keys_payload.get("usage_rollups_full_day") is True:
period_label += " full-day"
detail = (
f"{row['node']} | {row['name']} | {period_label} {format_cost(row['cost'])} | "
f"{format_count(row['tokens'])} tokens | {format_count(row['requests'])} req"
)
elif table_id == "logs":
detail = log_detail_line(row)
else:
detail = error_detail_line(row)
self.query_one("#detail", Static).update(detail)
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
table_id = str(event.control.id or "")
if table_id not in {"accounts", "keys", "logs", "errors"}:
return
if event.control.has_focus:
self.active_table = table_id
if table_id != self.active_table:
return
row_maps = {
"accounts": self.account_by_key,
"keys": self.key_by_key,
"logs": self.log_by_key,
"errors": self.error_by_key,
}
row = row_maps[table_id].get(str(event.row_key.value))
if row:
self.render_detail(table_id, row)
class AccountsScreen(Screen[None]):
AUTO_FOCUS = "#accounts"
BINDINGS = [
("r", "refresh", "Refresh"),
("/", "focus_filter", "Filter"),
("d", "show_dashboard", "Dashboard"),
("p", "show_pricing", "Sources"),
("l", "show_logs", "Requests"),
("e", "show_errors", "Errors"),
]
def __init__(self) -> None:
super().__init__()
self.payload: dict[str, Any] = {}
self.status_payload: dict[str, Any] = {}
self.pricing_payload: dict[str, Any] = {}
self.pricing_error = ""
self.status_error = ""
self.keys_payload: dict[str, Any] = {}
self.keys_error = ""
self.rows: list[dict[str, Any]] = []
self.row_by_key: dict[str, dict[str, Any]] = {}
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield Static("", id="summary")
yield Input(placeholder="filter", id="filter")
yield DataTable(id="accounts")
yield DataTable(id="keys")
yield Static("", id="detail")
yield Static("", id="status")
yield Footer()
def on_mount(self) -> None:
self.app.sub_title = "Accounts"
table = self.query_one("#accounts", DataTable)
table.cursor_type = "row"
table.zebra_stripes = True
table.add_columns("Name", "Provider", "Group", "Source", "Multiplier", "Src CNY", "Src state", "Daily", "Today", "Tokens", "Req", "Kind", "5h", "7d", "Reset", "Status", "Availability")
keys_table = self.query_one("#keys", DataTable)
keys_table.cursor_type = "row"
keys_table.zebra_stripes = True
keys_table.add_columns("Node", "Key", "Day total" if not legacy_direct else "Today", "Tokens", "Req")
self.refresh_data(refresh=legacy_direct)
self.set_interval(refresh_seconds, self.refresh_data)
def action_refresh(self) -> None:
self.refresh_data(refresh=True)
def action_focus_filter(self) -> None:
self.query_one("#filter", Input).focus()
def action_show_dashboard(self) -> None:
self.app.switch_screen(DashboardScreen())
def action_show_pricing(self) -> None:
self.app.switch_screen(PricingScreen())
def action_show_logs(self) -> None:
self.app.switch_screen(LogsScreen())
def action_show_errors(self) -> None:
self.app.switch_screen(ErrorsScreen())
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "filter":
self.render_payload()
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "filter":
self.query_one("#accounts", DataTable).focus()
def refresh_data(self, refresh: bool = False) -> None:
status = self.query_one("#status", Static)
status.update("refreshing...")
try:
self.payload = load_accounts(force=refresh)
self.status_payload, self.status_error = load_status()
self.pricing_payload, self.pricing_error = load_pricing()
self.refresh_keys()
self.render_payload()
self.render_keys()
status_bits = [version_message, monitor_summary(self.status_payload, self.status_error), f"source {self.payload.get('source_name') or '-'}"]
if not legacy_direct:
status_bits.insert(1, workspace_state_summary(workspace_cache.get(), workspace_cache.error))
if self.pricing_error:
status_bits.append(f"sources: {self.pricing_error}")
else:
status_bits.append(pricing_summary(self.pricing_payload))
if self.keys_error:
status_bits.append(f"keys: {self.keys_error}")
endpoint = api_url if legacy_direct else workspace_cache.url
status.update(" | ".join(bit for bit in status_bits if bit) + f" | {endpoint}")
except Exception as exc:
status.update(f"error: {exc}")
def refresh_keys(self) -> None:
self.keys_error = ""
self.keys_payload = {}
if legacy_direct and not str(logs_token or "").strip():
self.keys_error = "logs token not configured"
return
try:
self.keys_payload = load_keys()
except Exception as exc:
self.keys_error = str(exc)
def render_keys(self) -> None:
table = self.query_one("#keys", DataTable)
table.clear()
rows = normalize_key_rows(self.keys_payload)
table.display = bool(rows)
for row in rows:
color = key_color(row["name"])
table.add_row(
row["node"],
Text(str(row["name"]), style=color) if color else str(row["name"]),
format_cost(row["cost"]),
format_count(row["tokens"]),
format_count(row["requests"]),
)
def render_payload(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.rows = normalize_account_rows(self.payload, filter_text, self.pricing_payload)
table = self.query_one("#accounts", DataTable)
table.clear()
self.row_by_key = {}
for row in self.rows:
key = str(row["id"])
self.row_by_key[key] = row
table.add_row(
row["name"],
row["provider"],
row["routing_group"],
row["pricing_source"],
format_multiplier(row["account_multiplier"]),
format_cny(row["pricing_cny"]),
row["pricing_state"],
row["daily_quota_cell"],
format_cost(row["today_cost_usd"]),
format_count(row["today_tokens"]),
format_count(row["today_requests"]),
row["kind_label"],
row["five_hour"],
row["weekly"],
row["reset"],
row["status"],
monitor_availability(row, self.status_payload),
key=key,
)
self.query_one("#summary", Static).update(summary_line(self.payload))
if self.rows:
self.render_detail(self.rows[0])
else:
self.query_one("#detail", Static).update("no accounts")
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
key = str(event.row_key.value)
row = self.row_by_key.get(key)
if row:
self.render_detail(row)
def render_detail(self, row: dict[str, Any]) -> None:
raw = row.get("raw") if isinstance(row.get("raw"), dict) else {}
detail = (
f"{row['name']} | {row['provider']} | group {row['routing_group']} | {row['kind_label']} | {row['account_type']} | "
f"multiplier {format_multiplier(row['account_multiplier'])} | "
f"daily {row['daily_quota_cell']} ({format_percent(row['daily_quota_used_percent'])} used, "
f"{row['daily_quota_remaining'] if row['daily_quota_remaining'] is not None else '-'} left) | "
f"5h {row['five_hour']} | 7d {row['weekly']} | "
f"{account_pricing_detail(row)} | "
f"today {format_cost(row['today_cost_usd'])}, {format_count(row['today_tokens'])} tokens, "
f"{format_count(row['today_requests'])} req | latest {row['latest_usage_at']} | "
f"priority {row['priority'] if row['priority'] is not None else '-'} | "
f"{monitor_detail(row, self.status_payload)}"
)
error = str(raw.get("error") or "").strip()
if error:
detail += f" | {error}"
self.query_one("#detail", Static).update(detail)
class PricingScreen(Screen[None]):
AUTO_FOCUS = "#pricing"
BINDINGS = [
("r", "refresh", "Refresh"),
("/", "focus_filter", "Filter"),
("d", "show_dashboard", "Dashboard"),
("a", "show_accounts", "Accounts"),
("l", "show_logs", "Requests"),
("e", "show_errors", "Errors"),
]
def __init__(self) -> None:
super().__init__()
self.payload: dict[str, Any] = {}
self.rows: list[dict[str, Any]] = []
self.row_by_key: dict[str, dict[str, Any]] = {}
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield Static("", id="summary")
yield Input(placeholder="filter sources", id="filter")
yield DataTable(id="pricing")
yield Static("", id="detail")
yield Static("", id="status")
yield Footer()
def on_mount(self) -> None:
self.app.sub_title = "Sources"
table = self.query_one("#pricing", DataTable)
table.cursor_type = "row"
table.zebra_stripes = True
table.add_columns("Source", "Kind", "Balance", "CNY", "Unit", "Status", "Updated", "Age")
self.refresh_data()
self.set_interval(refresh_seconds, self.refresh_data)
def action_refresh(self) -> None:
self.refresh_data(force=True)
def action_focus_filter(self) -> None:
self.query_one("#filter", Input).focus()
def action_show_dashboard(self) -> None:
self.app.switch_screen(DashboardScreen())
def action_show_accounts(self) -> None:
self.app.switch_screen(AccountsScreen())
def action_show_logs(self) -> None:
self.app.switch_screen(LogsScreen())
def action_show_errors(self) -> None:
self.app.switch_screen(ErrorsScreen())
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "filter":
self.render_payload()
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "filter":
self.query_one("#pricing", DataTable).focus()
def refresh_data(self, force: bool = False) -> None:
status = self.query_one("#status", Static)
status.update("refreshing source balances from workspace...")
try:
if legacy_direct:
self.payload = fetch_pricing_payload(pricing_url, timeout)
else:
self.payload = workspace_pricing_payload(workspace_cache.get(force=force))
self.render_payload()
endpoint = pricing_url if legacy_direct else workspace_cache.url
status_bits = [version_message, pricing_summary(self.payload), endpoint]
if not legacy_direct:
status_bits.insert(1, workspace_state_summary(workspace_cache.get(), workspace_cache.error))
status.update(" | ".join(bit for bit in status_bits if bit))
except Exception:
self.payload = {}
self.rows = []
self.row_by_key = {}
self.query_one("#pricing", DataTable).clear()
self.query_one("#summary", Static).update("sources unavailable")
self.query_one("#detail", Static).update("no source balances")
status.update("sources unavailable")
def render_payload(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.rows = normalize_pricing_rows(self.payload, filter_text)
table = self.query_one("#pricing", DataTable)
table.clear()
self.row_by_key = {}
for index, row in enumerate(self.rows):
key = f"pricing-{row['name']}-{index}"
self.row_by_key[key] = row
table.add_row(
row["name"],
row["kind"],
format_amount(row["balance"]),
format_cny(row["balance_cny"]),
row["unit"],
row["status"],
row["updated"],
row["age"],
key=key,
)
self.query_one("#summary", Static).update(pricing_summary(self.payload, self.rows))
if self.rows:
self.query_one("#detail", Static).update(pricing_detail_line(self.rows[0]))
else:
self.query_one("#detail", Static).update("no upstream balances")
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
row = self.row_by_key.get(str(event.row_key.value))
if row:
self.query_one("#detail", Static).update(pricing_detail_line(row))
class LogsScreen(Screen[None]):
AUTO_FOCUS = "#logs"
BINDINGS = [
("r", "refresh", "Refresh"),
("/", "focus_filter", "Filter"),
("d", "show_dashboard", "Dashboard"),
("a", "show_accounts", "Accounts"),
("p", "show_pricing", "Sources"),
("e", "show_errors", "Errors"),
]
def __init__(self) -> None:
super().__init__()
self.payload: dict[str, Any] = {}
self.rows: list[dict[str, Any]] = []
self.row_by_key: dict[str, dict[str, Any]] = {}
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield Static("", id="summary")
yield Input(placeholder="filter", id="filter")
yield DataTable(id="logs")
yield Static("", id="detail")
yield Static("", id="status")
yield Footer()
def on_mount(self) -> None:
self.app.sub_title = "Requests"
table = self.query_one("#logs", DataTable)
table.cursor_type = "row"
table.zebra_stripes = True
table.add_columns(
"Node",
"Key",
"Account",
"Multiplier",
"Model",
"Effort",
"Type",
"Input",
"Output",
"Cache",
"Tokens",
"Cost",
"First",
"Duration",
"Tok/s",
"Time",
"Age",
)
self.refresh_data()
self.set_interval(logs_refresh_seconds, self.refresh_data)
def action_refresh(self) -> None:
self.refresh_data(force=True)
def action_focus_filter(self) -> None:
self.query_one("#filter", Input).focus()
def action_show_dashboard(self) -> None:
self.app.switch_screen(DashboardScreen())
def action_show_accounts(self) -> None:
self.app.switch_screen(AccountsScreen())
def action_show_pricing(self) -> None:
self.app.switch_screen(PricingScreen())
def action_show_errors(self) -> None:
self.app.switch_screen(ErrorsScreen())
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "filter":
self.render_payload()
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "filter":
self.query_one("#logs", DataTable).focus()
def refresh_data(self, force: bool = False) -> None:
status = self.query_one("#status", Static)
if legacy_direct and not str(logs_token or "").strip():
status.update(logs_token_hint())
return
status.update("refreshing requests...")
try:
self.payload = load_logs(force=force)
self.render_payload()
endpoint = logs_url if legacy_direct else workspace_cache.url
status_bits = [version_message, f"latest {logs_limit} requests | {endpoint}"]
if not legacy_direct:
status_bits.insert(1, workspace_state_summary(workspace_cache.get(), workspace_cache.error))
status.update(" | ".join(bit for bit in status_bits if bit))
except Exception as exc:
status.update(f"requests error: {exc}")
def render_payload(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.rows = normalize_log_rows(self.payload, filter_text)
table = self.query_one("#logs", DataTable)
table.clear()
self.row_by_key = {}
for index, row in enumerate(self.rows):
key = f"{row['id']}-{index}"
self.row_by_key[key] = row
color = key_color(row["key"])
table.add_row(
row["node"],
Text(row["key"], style=color) if color else row["key"],
row["account"],
format_multiplier(row["account_multiplier"]),
row["model"],
row["effort"],
row["type"],
format_count(row["input_tokens"]),
format_count(row["output_tokens"]),
format_count(row["cache_tokens"]),
format_count(row["tokens"]),
format_cost(row["cost"]),
format_seconds(row["first_token_ms"]),
format_seconds(row["duration_ms"]),
format_rate(row["tokens_per_second"]),
row["time"],
row["age"],
key=key,
)
self.query_one("#summary", Static).update(logs_summary_line(self.payload, len(self.rows)))
if self.rows:
self.query_one("#detail", Static).update(log_detail_line(self.rows[0]))
else:
self.query_one("#detail", Static).update("no request logs")
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
key = str(event.row_key.value)
row = self.row_by_key.get(key)
if row:
self.query_one("#detail", Static).update(log_detail_line(row))
class ErrorsScreen(Screen[None]):
AUTO_FOCUS = "#errors"
BINDINGS = [
("r", "refresh", "Refresh"),
("/", "focus_filter", "Filter"),
("d", "show_dashboard", "Dashboard"),
("a", "show_accounts", "Accounts"),
("p", "show_pricing", "Sources"),
("l", "show_logs", "Requests"),
]
def __init__(self) -> None:
super().__init__()
self.payload: dict[str, Any] = {}
self.rows: list[dict[str, Any]] = []
self.row_by_key: dict[str, dict[str, Any]] = {}
def compose(self) -> ComposeResult:
yield Header(show_clock=True)
yield Static("", id="summary")
yield Input(placeholder="filter", id="filter")
yield DataTable(id="errors")
yield Static("", id="detail")
yield Static("", id="status")
yield Footer()
def on_mount(self) -> None:
self.app.sub_title = "Errors"
table = self.query_one("#errors", DataTable)
table.cursor_type = "row"
table.zebra_stripes = True
table.add_columns("Node", "Status", "Key", "Account", "Model", "Phase", "Type", "Owner", "Time", "Age")
self.refresh_data()
self.set_interval(errors_refresh_seconds, self.refresh_data)
def action_refresh(self) -> None:
self.refresh_data(force=True)
def action_focus_filter(self) -> None:
self.query_one("#filter", Input).focus()
def action_show_dashboard(self) -> None:
self.app.switch_screen(DashboardScreen())
def action_show_accounts(self) -> None:
self.app.switch_screen(AccountsScreen())
def action_show_pricing(self) -> None:
self.app.switch_screen(PricingScreen())
def action_show_logs(self) -> None:
self.app.switch_screen(LogsScreen())
def on_input_changed(self, event: Input.Changed) -> None:
if event.input.id == "filter":
self.render_payload()
def on_input_submitted(self, event: Input.Submitted) -> None:
if event.input.id == "filter":
self.query_one("#errors", DataTable).focus()
def refresh_data(self, force: bool = False) -> None:
status = self.query_one("#status", Static)
if legacy_direct and not str(logs_token or "").strip():
status.update(logs_token_hint())
return
status.update("refreshing errors...")
try:
self.payload = load_errors(force=force)
self.render_payload()
sources = self.payload.get("sources") if isinstance(self.payload.get("sources"), dict) else {}
bits = []
for node in sorted(sources):
info = sources.get(node) if isinstance(sources.get(node), dict) else {}
if not info:
continue
if info.get("ok"):
bits.append(f"{node} ok")
else:
bits.append(f"{node} {info.get('error') or 'error'}")
status_bits = [version_message, f"every {errors_refresh_seconds}s", " | ".join(bits)]
if not legacy_direct:
status_bits.insert(1, workspace_state_summary(workspace_cache.get(), workspace_cache.error))
status.update(" | ".join(bit for bit in status_bits if bit))
except Exception as exc:
status.update(f"errors error: {exc}")
def render_payload(self) -> None:
filter_text = self.query_one("#filter", Input).value
self.rows = normalize_error_rows(self.payload, filter_text)
table = self.query_one("#errors", DataTable)
table.clear()
self.row_by_key = {}
for index, row in enumerate(self.rows):
key = f"{row['node']}-{row['id']}-{index}"
self.row_by_key[key] = row
table.add_row(
row["node"],
str(row["status_code"]),
row["key"],
row["account"],
row["model"],
row["phase"],
row["type"],
row["owner"],
row["time"],
row["age"],
key=key,
)
self.query_one("#summary", Static).update(errors_summary_line(self.payload, len(self.rows)))
if self.rows:
self.query_one("#detail", Static).update(error_detail_line(self.rows[0]))
else:
self.query_one("#detail", Static).update("no error logs")
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
key = str(event.row_key.value)
row = self.row_by_key.get(key)
if row:
self.query_one("#detail", Static).update(error_detail_line(row))
class Sub2APIQuotaApp(App[None]):
TITLE = "shusub2"
SUB_TITLE = "Dashboard"
CSS = """
#summary { height: 1; padding: 0 1; color: $foreground; }
#filter { height: 1; border: none; padding: 0 1; }
#filter:focus { border: none; }
#accounts { height: 1fr; min-height: 3; }
#pricing { height: 1fr; min-height: 3; }
#keys { height: auto; max-height: 6; }
.dashboard #accounts { background: $success 5%; }
.dashboard #accounts > .datatable--header { background: $success 35%; color: $foreground; }
.dashboard #keys { height: 1fr; min-height: 0; max-height: 100%; background: $warning 5%; }
.dashboard #keys > .datatable--header { background: $warning 35%; color: $foreground; }
#logs { height: 1fr; min-height: 3; }
.dashboard #logs { background: $secondary 5%; }
.dashboard #logs > .datatable--header { background: $secondary 35%; color: $foreground; }
#errors { height: 1fr; min-height: 3; }
.dashboard #errors { background: $error 5%; }
.dashboard #errors > .datatable--header { background: $error 35%; color: $foreground; }
#detail { height: 2; padding: 0 1; background: $surface-lighten-1; }
#status { height: 1; padding: 0 1; color: $text-muted; }
"""
BINDINGS = [
("q", "quit", "Quit"),
]
def on_mount(self) -> None:
if start_page == "accounts":
self.push_screen(AccountsScreen())
elif start_page == "pricing":
self.push_screen(PricingScreen())
elif start_page == "logs":
self.push_screen(LogsScreen())
elif start_page == "errors":
self.push_screen(ErrorsScreen())
else:
self.push_screen(DashboardScreen())
Sub2APIQuotaApp().run()
return 0
def logs_token_hint() -> str:
return (
"logs token not configured: set SHUSUB2_LOGS_TOKEN or write the Sub2API admin API key to "
f"{logs_token_config_file_path()}"
)
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Sub2API aggregated operations TUI")
parser.add_argument("--workspace-url", default=default_workspace_url(), help="Pricing Monitor view=workspace aggregate URL")
parser.add_argument("--legacy-direct", action="store_true", help="use the former multi-endpoint Accounts/Status/Admin topology for diagnostics")
parser.add_argument("--api-url", default=default_api_url())
parser.add_argument("--status-url", default=default_status_url(), help="optional sub2api-status /api/status URL for channel monitor health")
parser.add_argument("--pricing-url", default=default_pricing_url(), help="Pricing Monitor /api/ui-data?view=accounts URL for upstream source balances")
parser.add_argument("--logs-url", default=default_logs_url(), help="Sub2API admin usage logs URL (default: sub2apicn /api/v1/admin/usage)")
parser.add_argument("--logs-token", default=None, help="Sub2API admin API key used only with --legacy-direct")
parser.add_argument("--errors-cn-url", default=default_errors_cn_url(), help="cn Sub2API admin ops errors URL (default: sub2apicn /api/v1/admin/ops/errors)")
parser.add_argument(
"--errors-us-url",
default=default_errors_us_url(),
help="us Sub2API admin ops errors URL (default: DNS-only us.sub2 /api/v1/admin/ops/errors; CF sub2apius often blocks non-browser clients)",
)
parser.add_argument("--save-config", action="store_true", help="persist workspace and legacy endpoint options to ~/.config/shusub2/ before running")
parser.add_argument("--install", action="store_true", help="persist config, install shusub2 as a uv tool, then exit")
parser.add_argument("--version-check-url", default=os.environ.get("SHUSUB2_VERSION_CHECK_URL", DEFAULT_VERSION_CHECK_URL))
parser.add_argument(
"--version-check-timeout",
type=int,
default=env_int("SHUSUB2_VERSION_CHECK_TIMEOUT", DEFAULT_VERSION_CHECK_TIMEOUT_SECONDS),
)
parser.add_argument("--no-version-check", action="store_true", default=os.environ.get("SHUSUB2_NO_VERSION_CHECK", "").strip().lower() in {"1", "true", "yes", "on"})
parser.add_argument(
"--refresh-seconds",
type=int,
default=env_int("SUB2API_QUOTA_TUI_REFRESH_SECONDS", DEFAULT_REFRESH_SECONDS),
)
parser.add_argument(
"--logs-refresh-seconds",
type=int,
default=env_int("SHUSUB2_LOGS_REFRESH_SECONDS", DEFAULT_LOGS_REFRESH_SECONDS),
)
parser.add_argument(
"--errors-refresh-seconds",
type=int,
default=env_int("SHUSUB2_ERRORS_REFRESH_SECONDS", DEFAULT_ERRORS_REFRESH_SECONDS),
)
parser.add_argument(
"--logs-limit",
type=int,
default=env_int("SHUSUB2_LOGS_LIMIT", DEFAULT_LOGS_LIMIT),
)
parser.add_argument(
"--errors-limit",
type=int,
default=env_int("SHUSUB2_ERRORS_LIMIT", DEFAULT_ERRORS_LIMIT),
)
parser.add_argument(
"--errors-time-range",
default=os.environ.get("SHUSUB2_ERRORS_TIME_RANGE", DEFAULT_ERRORS_TIME_RANGE),
help="legacy-direct ops errors time_range query (5m/30m/1h/6h/24h/7d/30d, default 24h)",
)
parser.add_argument("--timeout", type=int, default=env_int("SUB2API_QUOTA_TUI_TIMEOUT", DEFAULT_TIMEOUT_SECONDS))
parser.add_argument("--once", action="store_true", help="print one snapshot and exit")
parser.add_argument("--accounts", action="store_true", help="start on the Accounts page (with --once: print Accounts and Key usage)")
parser.add_argument("--pricing", "--sources", dest="pricing", action="store_true", help="start on the Sources page (legacy alias: --pricing)")
parser.add_argument("--logs", "--requests", dest="logs", action="store_true", help="start on the Requests page (legacy alias: --logs)")
parser.add_argument("--errors", action="store_true", help="start on the dedicated merged cn+us errors page (with --once: print an errors snapshot)")
parser.add_argument("--filter", default="", help="initial filter for --once output")
return parser
def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
workspace_url = str(args.workspace_url or "").strip()
legacy_direct = bool(args.legacy_direct or not workspace_url)
logs_token = str(args.logs_token or "").strip()
if legacy_direct and args.logs_token is None:
logs_token = default_logs_token()
status_url = (
str(args.status_url or "").strip() or inferred_status_url(args.api_url)
) if legacy_direct else ""
logs_limit = max(1, min(args.logs_limit, 1000))
errors_limit = max(1, min(args.errors_limit, 500))
errors_time_range = str(args.errors_time_range or DEFAULT_ERRORS_TIME_RANGE).strip() or DEFAULT_ERRORS_TIME_RANGE
selected_pages = sum(bool(value) for value in (args.accounts, args.pricing, args.logs, args.errors))
if selected_pages > 1:
print("choose only one of --accounts, --sources/--pricing, --requests/--logs, or --errors", file=sys.stderr)
return 2
if args.save_config or args.install:
if workspace_url:
workspace_path = write_config_value(workspace_url_config_file_path(), workspace_url)
print(f"saved workspace url to {workspace_path}")
config_path = write_api_url_config(args.api_url)
print(f"saved api url to {config_path}")
if str(args.pricing_url or "").strip():
pricing_url_path = write_config_value(pricing_url_config_file_path(), args.pricing_url)
print(f"saved pricing url to {pricing_url_path}")
if str(args.logs_url or "").strip():
logs_url_path = write_config_value(logs_url_config_file_path(), args.logs_url)
print(f"saved logs url to {logs_url_path}")
if logs_token:
logs_token_path = write_config_value(logs_token_config_file_path(), logs_token)
print(f"saved logs token to {logs_token_path}")
if str(args.errors_cn_url or "").strip():
errors_cn_path = write_config_value(errors_cn_url_config_file_path(), args.errors_cn_url)
print(f"saved errors cn url to {errors_cn_path}")
if str(args.errors_us_url or "").strip():
errors_us_path = write_config_value(errors_us_url_config_file_path(), args.errors_us_url)
print(f"saved errors us url to {errors_us_path}")
if args.install:
print("installing shusub2 with uv tool...")
return run_install_command()
version_message = check_version_update(
args.version_check_url,
max(1, args.version_check_timeout),
disabled=bool(args.no_version_check),
)
if args.once:
if version_message:
print(version_message)
if not legacy_direct:
try:
workspace = fetch_workspace_payload(workspace_url, args.timeout)
except Exception:
print("workspace unavailable", file=sys.stderr)
return 1
accounts_payload = workspace_accounts_payload(workspace)
status_payload = workspace_status_payload(workspace)
pricing_payload = workspace_pricing_payload(workspace)
logs_payload = workspace_logs_payload(workspace)
keys_payload = workspace_keys_payload(workspace)
errors_payload = workspace_errors_payload(workspace)
if args.pricing:
print_pricing_once(pricing_payload, args.filter)
return 0
if args.logs:
print_logs_once(logs_payload, args.filter)
return 0
if args.errors:
print_errors_once(errors_payload, args.filter)
return 0
print_once(
accounts_payload,
args.filter,
status_payload,
"",
pricing_payload,
"",
)
print()
print_keys_once(keys_payload)
return 0
if args.pricing:
try:
print_pricing_once(
fetch_pricing_payload(args.pricing_url, args.timeout), args.filter
)
except Exception:
print("sources unavailable", file=sys.stderr)
return 1
return 0
if args.logs:
if not logs_token:
print(logs_token_hint(), file=sys.stderr)
return 2
print_logs_once(fetch_logs_payload(args.logs_url, logs_token, args.timeout, logs_limit), args.filter)
return 0
if args.errors:
if not logs_token:
print(logs_token_hint(), file=sys.stderr)
return 2
print_errors_once(
fetch_merged_errors(
default_error_sources(args.errors_cn_url, args.errors_us_url),
logs_token,
args.timeout,
errors_limit,
errors_time_range,
),
args.filter,
)
return 0
status_payload, status_error = fetch_optional_payload(status_url, args.timeout)
pricing_payload, pricing_error = fetch_optional_pricing_payload(args.pricing_url, args.timeout)
print_once(
fetch_payload(args.api_url, args.timeout, refresh=True),
args.filter,
status_payload,
status_error,
pricing_payload,
pricing_error,
)
if logs_token:
print()
try:
print_keys_once(fetch_key_usage_payload(args.logs_url, logs_token, args.timeout))
except Exception as exc:
print(f"keys error: {exc}", file=sys.stderr)
return 0
start_page = "dashboard"
if args.accounts:
start_page = "accounts"
elif args.pricing:
start_page = "pricing"
elif args.logs:
start_page = "logs"
elif args.errors:
start_page = "errors"
return run_textual(
args.api_url,
status_url,
args.pricing_url,
args.logs_url,
logs_token,
args.errors_cn_url,
args.errors_us_url,
max(1, args.refresh_seconds),
max(1, args.logs_refresh_seconds),
max(1, args.errors_refresh_seconds),
max(1, args.timeout),
logs_limit,
errors_limit,
errors_time_range,
version_message,
start_page=start_page,
workspace_url=workspace_url,
legacy_direct=legacy_direct,
)
if __name__ == "__main__":
raise SystemExit(main())