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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user