v2.6.0: Usage Dashboard, per-provider tracking, OAuth file picker

- Usage Dashboard: visual cards with success rate bars, token stats, latency
- Per-model breakdown and error tracking per provider
- Proxy records usage-stats.json after every request
- Google OAuth: browse for client_secret.json instead of fixed path
- Auto-copies selected file to ~/.cache/codex-proxy/
This commit is contained in:
Roman
2026-05-20 17:23:14 +04:00
Unverified
parent 0e70fa47f9
commit bd4ccf1635
5 changed files with 301 additions and 8 deletions

View File

@@ -141,6 +141,43 @@ _pool = uuid.uuid4().hex[:8]
_response_store = {}
_MAX_STORED = 50
_stats_path = os.path.join(_LOG_DIR, "usage-stats.json")
_stats_lock = threading.Lock()
def _load_stats():
try:
if os.path.exists(_stats_path):
return json.load(open(_stats_path))
except Exception:
pass
return {"providers": {}, "updated": None}
def _record_usage(provider, model, success, duration_s, tokens_in=0, tokens_out=0, error_type=None):
with _stats_lock:
stats = _load_stats()
p = stats["providers"].setdefault(provider, {
"total_requests": 0, "successes": 0, "failures": 0,
"total_tokens_in": 0, "total_tokens_out": 0,
"total_duration_s": 0.0, "models": {}, "last_used": None, "last_error": None,
})
p["total_requests"] += 1
p["total_tokens_in"] += tokens_in
p["total_tokens_out"] += tokens_out
p["total_duration_s"] += duration_s
p["last_used"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
if success:
p["successes"] += 1
else:
p["failures"] += 1
p["last_error"] = error_type or "unknown"
m = p["models"].setdefault(model, {"requests": 0, "tokens_in": 0, "tokens_out": 0})
m["requests"] += 1
m["tokens_in"] += tokens_in
m["tokens_out"] += tokens_out
stats["updated"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
with open(_stats_path, "w") as f:
json.dump(stats, f, indent=2)
def store_response(resp_id, input_data, output_items):
if not resp_id:
return
@@ -1161,6 +1198,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
def _forward_oa_compat(self, upstream, stream, model, chat_body, body, input_data, fwd, target):
n_items = len(input_data) if isinstance(input_data, list) else 1
t0 = time.time()
provider = TARGET_URL.split("//")[-1].split("/")[0]
if BGP_ROUTES:
provider = "bgp:" + (BGP_ROUTES[0].get("name", "pool") if BGP_ROUTES else "unknown")
if stream:
self.send_response(200)
@@ -1205,6 +1246,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
_log_resp(last_resp_id, last_status, last_output)
if last_resp_id and input_data is not None:
store_response(last_resp_id, input_data, last_output)
_record_usage(provider, model, success, time.time() - t0, error_type="length" if not success else None)
# Auto-retry on finish_reason=length with no content
if finish_reason == "length" and not has_content and isinstance(input_data, list) and len(input_data) > 5:
@@ -1234,6 +1276,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
_log_resp(rid, result.get("status"), result.get("output", []))
if rid and input_data is not None:
store_response(rid, input_data, result.get("output", []))
_record_usage(provider, model, success, time.time() - t0)
def _forward_oa_compat_retry(self, req, model, chat_body, body, input_data):
try: