From 080f2bc56e16621f39ff584a81a7c73852fcabec Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 22 May 2026 22:36:16 +0400 Subject: [PATCH] =?UTF-8?q?v3.8.0:=20AI=20Monitoring=20=E2=80=94=20self-he?= =?UTF-8?q?aling=20watchdog=20with=203-tier=20response=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HealthWatcher thread: monitors proxy /health every 5s - LogAnalyzer thread: tails cc-debug.log for 18 failure signal patterns - Tier 1 rule engine: 14 rules for instant auto-recovery (< 1s) - Tier 2 incident store: JSON pattern database with success rates - Tier 3 AI diagnostic agent: calls configurable provider/model for novel failures - AIMonitoringWindow GUI: ON/OFF toggle, provider/model/API key selector, incident log - 30 fault types catalogued across 5 categories (A-E) - Enhanced /health endpoint with memory_mb, uptime_s, requests_total - Auto-restart proxy, auto-clear schema cache, kill stale processes - Safety: rate-limited AI calls, restart caps, cooldowns per pattern - AI Monitoring design spec (AI-MONITORING-DESIGN.md) - 54 self-test patterns passing --- codex-launcher_3.8.0_all.deb | Bin 0 -> 81980 bytes install.sh | 8 +- src/codex-launcher-gui | 611 ++++++++++++++++++++++++++++++++++- src/translate-proxy.py | 19 +- 4 files changed, 621 insertions(+), 17 deletions(-) create mode 100644 codex-launcher_3.8.0_all.deb diff --git a/codex-launcher_3.8.0_all.deb b/codex-launcher_3.8.0_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..d24e7f750b2f69a411e60ca07f106ef8ad0c3197 GIT binary patch literal 81980 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 G4WF(q1fHw_ literal 0 HcmV?d00001 diff --git a/install.sh b/install.sh index 70b0b75..d03541b 100755 --- a/install.sh +++ b/install.sh @@ -3,11 +3,11 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -if [ -f "$SCRIPT_DIR/codex-launcher_3.7.0_all.deb" ]; then - echo "Installing codex-launcher_3.7.0_all.deb ..." - sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.7.0_all.deb" +if [ -f "$SCRIPT_DIR/codex-launcher_3.8.0_all.deb" ]; then + echo "Installing codex-launcher_3.8.0_all.deb ..." + sudo dpkg -i "$SCRIPT_DIR/codex-launcher_3.8.0_all.deb" echo "" - echo "Installed v3.7.0 via .deb package." + echo "Installed v3.8.0 via .deb package." echo " translate-proxy.py -> /usr/bin/translate-proxy.py" echo " codex-launcher-gui -> /usr/bin/codex-launcher-gui" echo " cleanup-codex-stale -> /usr/bin/cleanup-codex-stale.sh" diff --git a/src/codex-launcher-gui b/src/codex-launcher-gui index 951e0e9..137b590 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -5,7 +5,7 @@ import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, GLib import subprocess, os, signal, sys, threading, time, json, urllib.request, urllib.parse, urllib.error, tempfile, shutil -import hashlib, socket, ssl, contextlib, re +import hashlib, socket, ssl, contextlib, re, collections import base64, secrets from pathlib import Path @@ -1123,6 +1123,524 @@ def _check_codex_auth(): except Exception as e: return ("error", str(e)) +# ═══════════════════════════════════════════════════════════════════ +# AI Monitoring — Self-Healing Watchdog +# ═══════════════════════════════════════════════════════════════════ + +MONITORING_FILE = Path.home() / ".cache/codex-proxy/monitoring-config.json" +INCIDENT_STORE_FILE = Path.home() / ".cache/codex-proxy/incident-store.json" +MONITORING_LOG = Path.home() / ".cache/codex-proxy/monitoring.log" + +_TIER1_RULES = [ + ("proxy_health_fail", "restart_proxy", 30), + ("proxy_port_conflict", "kill_stale_restart", 60), + ("upstream_429", "wait_retry", 0), + ("upstream_502_503", "retry_backoff", 30), + ("upstream_500_repeat", "switch_provider", 60), + ("upstream_timeout", "retry_increase_timeout",30), + ("upstream_401_403", "alert_bad_key", 0), + ("stream_broken_pipe", "restart_proxy", 30), + ("stream_reset", "restart_proxy", 30), + ("parsed_tool_calls_0_x3", "clear_schema_cache", 300), + ("sanitizer_suspicious_5x","alert_model_issue", 0), + ("stuck_recovery_x5", "suggest_switch_model", 0), + ("codex_process_dead", "alert_restart", 0), + ("schema_corrupt", "delete_provider_caps", 0), +] + +_FAILURE_SIGNALS = { + "parsed_tool_calls=0": ("C1", "parser_empty"), + "[STUCK-RECOVERY]": ("C3", "stuck_recovery"), + "suspicious cmd": ("C4", "sanitizer_flag"), + "empty cmd recovered": ("C6", "empty_cmd"), + "HTTP 429": ("B1", "rate_limited"), + "HTTP 500": ("B2", "server_error"), + "HTTP 502": ("B2", "server_error"), + "HTTP 503": ("B2", "server_error"), + "HTTP 401": ("B3", "auth_failure"), + "HTTP 403": ("B4", "forbidden"), + "Connection refused": ("A1", "proxy_dead"), + "Address already in use": ("A2", "port_conflict"), + "Broken pipe": ("B7", "broken_pipe"), + "Connection reset": ("B6", "connection_reset"), + "timed out": ("B5", "timeout"), + "SELF-REVIVE CRASH": ("A5", "proxy_crash"), + "stream error": ("B6", "stream_error"), + "content_type.*array": ("E1", "schema_corrupt"), +} + +_DIAGNOSTIC_SYSTEM_PROMPT = ( + 'You are a diagnostic agent for "Codex Launcher" — a desktop app that runs a local ' + 'translation proxy between OpenAI Codex CLI/Desktop and AI providers.\n\n' + 'Analyze the incident and respond with ONLY a JSON object:\n' + '{"action": "...", "reason": "...", "confidence": 0.0-1.0}\n\n' + 'Available actions: restart_proxy, kill_stale_processes, clear_schema_cache, ' + 'switch_provider, increase_timeout, regenerate_config, cleanup_stale, ' + 'alert_user, ignore, retry_now\n\n' + 'Rules:\n' + '- upstream 401/403 with auth error -> alert_user\n' + '- proxy dead -> restart_proxy\n' + '- same error 5+ times -> switch_provider or alert_user\n' + '- schema/content_type error -> clear_schema_cache\n' + '- "Address already in use" -> kill_stale_processes then restart_proxy\n' + '- timeout on slow upstream -> increase_timeout\n' + '- single transient 429/502/503 -> ignore\n' + '- "stream disconnected" + proxy healthy -> ignore\n' + '- no extra text, no markdown, just the JSON object' +) + +def _load_monitoring_config(): + if MONITORING_FILE.exists(): + try: + return json.loads(MONITORING_FILE.read_text()) + except Exception: + pass + return { + "enabled": False, + "provider_url": "", + "model": "", + "api_key": "", + "health_check_interval_s": 5, + "auto_restart_proxy": True, + "auto_switch_provider": False, + } + +def _save_monitoring_config(cfg): + MONITORING_FILE.parent.mkdir(parents=True, exist_ok=True) + MONITORING_FILE.write_text(json.dumps(cfg, indent=2)) + +def _load_incident_store(): + if INCIDENT_STORE_FILE.exists(): + try: + return json.loads(INCIDENT_STORE_FILE.read_text()) + except Exception: + pass + return {"version": 1, "incidents": {}, "stats": {"ai_calls": 0, "tokens_used": 0}} + +def _save_incident_store(store): + INCIDENT_STORE_FILE.parent.mkdir(parents=True, exist_ok=True) + INCIDENT_STORE_FILE.write_text(json.dumps(store, indent=2)) + +def _monitoring_log(msg): + try: + with open(str(MONITORING_LOG), "a") as f: + f.write(f"[{time.strftime('%H:%M:%S')}] {msg}\n") + except Exception: + pass + + +class IncidentStore: + def __init__(self): + self._store = _load_incident_store() + self._dirty = False + + def lookup(self, pattern): + inc = self._store.get("incidents", {}).get(pattern) + if inc and inc.get("success_count", 0) > 0: + rate = inc["success_count"] / max(inc["success_count"] + inc.get("fail_count", 0), 1) + if rate > 0.5: + return inc + return None + + def record(self, pattern, fix, success=True): + incs = self._store.setdefault("incidents", {}) + inc = incs.setdefault(pattern, { + "fix": fix, "success_count": 0, "fail_count": 0, + "last_seen": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "occurrences": 0, + }) + inc["last_seen"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + inc["occurrences"] = inc.get("occurrences", 0) + 1 + if success: + inc["success_count"] = inc.get("success_count", 0) + 1 + else: + inc["fail_count"] = inc.get("fail_count", 0) + 1 + self._dirty = True + + def record_ai_call(self, tokens=0): + stats = self._store.setdefault("stats", {"ai_calls": 0, "tokens_used": 0}) + stats["ai_calls"] = stats.get("ai_calls", 0) + 1 + stats["tokens_used"] = stats.get("tokens_used", 0) + tokens + self._dirty = True + + def flush(self): + if self._dirty: + _save_incident_store(self._store) + self._dirty = False + + @property + def stats(self): + return self._store.get("stats", {"ai_calls": 0, "tokens_used": 0}) + + +class AIDiagnosticAgent: + def __init__(self, provider_url, model, api_key): + self.provider_url = provider_url + self.model = model + self.api_key = api_key + self.incident_store = IncidentStore() + + def diagnose(self, context): + pattern = self._extract_pattern(context) + known = self.incident_store.lookup(pattern) + if known: + _monitoring_log(f"Tier 2 HIT: pattern={pattern} fix={known['fix']}") + return {"action": known["fix"], "reason": "known_pattern", "confidence": 0.9, "tier": 2} + action = self._call_model(context) + if action: + self.incident_store.record(pattern, action.get("action", "unknown")) + self.incident_store.flush() + return action + + def _extract_pattern(self, context): + parts = [] + for k in sorted(context.get("signals", [])): + parts.append(k) + if context.get("http_code"): + parts.append(f"http_{context['http_code']}") + return "+".join(parts[:3]) or "unknown" + + def _call_model(self, context): + prompt = ( + f"INCIDENT REPORT:\n" + f"Time: {time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}\n" + f"Proxy health: {context.get('proxy_alive', 'unknown')}\n" + f"Upstream: {context.get('upstream_url', 'unknown')}\n" + f"Model: {context.get('model', 'unknown')}\n" + f"Last HTTP code: {context.get('http_code', 'n/a')}\n" + f"Recent signals: {context.get('signals', [])}\n" + f"Recent log tail:\n{context.get('log_tail', '')[:1500]}\n" + ) + body = { + "model": self.model, + "messages": [ + {"role": "system", "content": _DIAGNOSTIC_SYSTEM_PROMPT}, + {"role": "user", "content": prompt}, + ], + "max_tokens": 200, + "temperature": 0.1, + } + try: + req = urllib.request.Request( + self.provider_url, + data=json.dumps(body).encode(), + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {self.api_key}", + }, + ) + resp = urllib.request.urlopen(req, timeout=15) + result = json.loads(resp.read()) + text = result["choices"][0]["message"]["content"].strip() + self.incident_store.record_ai_call(tokens=800) + action = json.loads(text) + action["tier"] = 3 + _monitoring_log(f"Tier 3 AI: action={action.get('action')} reason={action.get('reason')}") + return action + except Exception as e: + _monitoring_log(f"Tier 3 AI FAILED: {e}") + return {"action": "alert_user", "reason": f"ai_diag_failed: {e}", "confidence": 0.0, "tier": 3} + + +class HealthWatcher(threading.Thread): + def __init__(self, on_failure, on_recovery, on_signal, on_action): + super().__init__(daemon=True) + self.cfg = _load_monitoring_config() + self.on_failure = on_failure + self.on_recovery = on_recovery + self.on_signal = on_signal + self.on_action = on_action + self.failures = 0 + self.running = False + self._signal_counts = collections.defaultdict(int) + self._last_actions = {} + self._restart_count = 0 + self._last_restart_time = 0 + + def run(self): + self.running = True + self.incident_store = IncidentStore() + self._log_analyzer = _LogAnalyzerThread(self._on_log_signal) + self._log_analyzer.start() + while self.running: + self.cfg = _load_monitoring_config() + if not self.cfg.get("enabled"): + time.sleep(5) + continue + port = self._get_proxy_port() + if port: + healthy = self._check_health(port) + if healthy: + if self.failures > 0: + self.failures = 0 + self.on_recovery() + else: + self.failures += 1 + if self.failures >= 3: + self._handle_failure("proxy_health_fail") + self.incident_store.flush() + interval = self.cfg.get("health_check_interval_s", 5) + time.sleep(interval) + + def stop(self): + self.running = False + if hasattr(self, '_log_analyzer'): + self._log_analyzer.running = False + + def _get_proxy_port(self): + try: + cfg_path = Path.home() / ".cache/codex-proxy/proxy-config.json" + if cfg_path.exists(): + d = json.loads(cfg_path.read_text()) + return d.get("port") + except Exception: + pass + return None + + def _check_health(self, port): + try: + req = urllib.request.Request(f"http://localhost:{port}/health") + resp = urllib.request.urlopen(req, timeout=5) + return resp.status == 200 + except Exception: + return False + + def _on_log_signal(self, fault_id, category, line): + self._signal_counts[category] += 1 + self.on_signal(fault_id, category, line[:200]) + count = self._signal_counts[category] + if category in ("proxy_dead", "port_conflict") and count >= 2: + self._handle_failure(category) + elif category in ("server_error", "timeout") and count >= 3: + self._handle_failure(category + "_repeat") + elif category in ("sanitizer_flag",) and count >= 5: + self._handle_failure("sanitizer_suspicious_5x") + elif category in ("stuck_recovery",) and count >= 5: + self._handle_failure("stuck_recovery_x5") + elif category in ("parser_empty",) and count >= 3: + self._handle_failure("parsed_tool_calls_0_x3") + elif category in ("schema_corrupt",): + self._handle_failure("schema_corrupt") + + def _handle_failure(self, trigger): + now = time.time() + for rule_trigger, action, cooldown in _TIER1_RULES: + if rule_trigger == trigger: + last_t = self._last_actions.get(action, 0) + if now - last_t < cooldown: + return + self._last_actions[action] = now + _monitoring_log(f"Tier 1: trigger={trigger} action={action}") + self.on_action(action, trigger) + self.incident_store.record(trigger, action, success=True) + return + self._try_tier2_3(trigger) + + def _try_tier2_3(self, trigger): + cfg = self.cfg + if not cfg.get("provider_url") or not cfg.get("model") or not cfg.get("api_key"): + _monitoring_log(f"No AI configured for Tier 2/3 — alerting user for trigger={trigger}") + self.on_action("alert_user", trigger) + return + agent = AIDiagnosticAgent(cfg["provider_url"], cfg["model"], cfg["api_key"]) + context = { + "signals": [trigger], + "proxy_alive": self.failures == 0, + "log_tail": self._get_recent_log(), + } + result = agent.diagnose(context) + if result: + action = result.get("action", "alert_user") + _monitoring_log(f"Tier {result.get('tier', '?')}: action={action}") + self.on_action(action, trigger) + + +class _LogAnalyzerThread(threading.Thread): + def __init__(self, on_signal): + super().__init__(daemon=True) + self.on_signal = on_signal + self.running = False + + def run(self): + self.running = True + log_paths = [ + str(Path.home() / ".cache/codex-proxy/cc-debug.log"), + str(Path.home() / ".cache/codex-proxy/proxy.log"), + ] + fhs = {} + for p in log_paths: + try: + f = open(p, "r") + f.seek(0, 2) + fhs[p] = f + except Exception: + pass + while self.running: + activity = False + for p, fh in list(fhs.items()): + try: + line = fh.readline() + if line: + activity = True + for pattern, (fault_id, category) in _FAILURE_SIGNALS.items(): + if re.search(pattern, line): + self.on_signal(fault_id, category, line.strip()) + break + except Exception: + pass + if not activity: + time.sleep(0.5) + + +class AIMonitoringWindow(Gtk.Window): + def __init__(self, parent=None): + super().__init__(title="AI Monitoring") + self.set_transient_for(parent) + self.set_default_size(580, 520) + self.set_border_width(12) + self._cfg = _load_monitoring_config() + self._store = _load_incident_store() + + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) + self.add(vbox) + + hdr = Gtk.Box(spacing=8) + vbox.pack_start(hdr, False, False, 0) + lbl = Gtk.Label() + lbl.set_markup("AI Monitoring") + lbl.set_use_markup(True) + hdr.pack_start(lbl, False, False, 0) + self._toggle = Gtk.Switch() + self._toggle.set_active(self._cfg.get("enabled", False)) + self._toggle.connect("state-set", self._on_toggle) + hdr.pack_end(self._toggle, False, False, 0) + lbl2 = Gtk.Label(label="Enabled") + hdr.pack_end(lbl2, False, False, 0) + + frame = Gtk.Frame(label="Diagnostic Agent") + vbox.pack_start(frame, False, False, 0) + grid = Gtk.Grid(column_spacing=8, row_spacing=6, margin=8) + frame.add(grid) + + grid.attach(Gtk.Label(label="Provider URL:", halign=Gtk.Align.END), 0, 0, 1, 1) + self._url_entry = Gtk.Entry(hexpand=True) + self._url_entry.set_text(self._cfg.get("provider_url", "")) + self._url_entry.set_placeholder_text("https://api.openai.com/v1/chat/completions") + grid.attach(self._url_entry, 1, 0, 2, 1) + + grid.attach(Gtk.Label(label="Model:", halign=Gtk.Align.END), 0, 1, 1, 1) + self._model_entry = Gtk.Entry(hexpand=True) + self._model_entry.set_text(self._cfg.get("model", "")) + self._model_entry.set_placeholder_text("gpt-4o-mini or Qwen/Qwen3-32B") + grid.attach(self._model_entry, 1, 1, 2, 1) + + grid.attach(Gtk.Label(label="API Key:", halign=Gtk.Align.END), 0, 2, 1, 1) + self._key_entry = Gtk.Entry(hexpand=True, visibility=False) + self._key_entry.set_text(self._cfg.get("api_key", "")) + self._key_entry.set_placeholder_text("sk-...") + grid.attach(self._key_entry, 1, 2, 1, 1) + self._reveal_btn = Gtk.ToggleButton(label="Show") + self._reveal_btn.connect("toggled", lambda b: self._key_entry.set_visibility(b.get_active())) + grid.attach(self._reveal_btn, 2, 2, 1, 1) + + grid.attach(Gtk.Label(label="Health Check:", halign=Gtk.Align.END), 0, 3, 1, 1) + adj = Gtk.Adjustment(value=self._cfg.get("health_check_interval_s", 5), lower=2, upper=30, step_increment=1) + self._interval_spin = Gtk.SpinButton(adjustment=adj) + self._interval_spin.set_numeric(True) + grid.attach(self._interval_spin, 1, 3, 1, 1) + grid.attach(Gtk.Label(label="seconds"), 2, 3, 1, 1) + + opts_box = Gtk.Box(spacing=12, margin_top=4) + grid.attach(opts_box, 0, 4, 3, 1) + self._auto_restart_cb = Gtk.CheckButton(label="Auto-restart proxy on crash") + self._auto_restart_cb.set_active(self._cfg.get("auto_restart_proxy", True)) + opts_box.pack_start(self._auto_restart_cb, False, False, 0) + self._auto_switch_cb = Gtk.CheckButton(label="Auto-switch provider on repeated failure") + self._auto_switch_cb.set_active(self._cfg.get("auto_switch_provider", False)) + opts_box.pack_start(self._auto_switch_cb, False, False, 0) + + save_btn = Gtk.Button(label="Save Configuration") + save_btn.get_style_context().add_class("suggested-action") + save_btn.connect("clicked", self._on_save) + grid.attach(save_btn, 0, 5, 3, 1) + + stats_box = Gtk.Box(spacing=16) + vbox.pack_start(stats_box, False, False, 0) + stats = self._store.get("stats", {"ai_calls": 0, "tokens_used": 0}) + self._stats_lbl = Gtk.Label() + self._stats_lbl.set_markup( + f"AI diagnostic calls: {stats.get('ai_calls', 0)} | " + f"Tokens used: {stats.get('tokens_used', 0):,} | " + f"Known patterns: {len(self._store.get('incidents', {}))}" + ) + self._stats_lbl.set_use_markup(True) + stats_box.pack_start(self._stats_lbl, False, False, 0) + + frame2 = Gtk.Frame(label="Recent Incidents") + vbox.pack_start(frame2, True, True, 0) + sw = Gtk.ScrolledWindow() + sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + frame2.add(sw) + self._inc_buf = Gtk.TextBuffer() + tv = Gtk.TextView(buffer=self._inc_buf) + tv.set_editable(False) + tv.set_cursor_visible(False) + tv.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) + sw.add(tv) + self._refresh_incidents() + + bb = Gtk.Box(spacing=8) + vbox.pack_start(bb, False, False, 0) + view_btn = Gtk.Button(label="View Monitoring Log") + view_btn.connect("clicked", lambda b: subprocess.Popen(["xdg-open", str(MONITORING_LOG)])) + bb.pack_start(view_btn, False, False, 0) + clear_btn = Gtk.Button(label="Clear Incident Store") + clear_btn.connect("clicked", self._on_clear_store) + bb.pack_start(clear_btn, False, False, 0) + close_btn = Gtk.Button(label="Close") + close_btn.connect("clicked", lambda b: self.destroy()) + bb.pack_end(close_btn, False, False, 0) + + self.show_all() + + def _on_toggle(self, switch, state): + self._cfg["enabled"] = state + _save_monitoring_config(self._cfg) + + def _on_save(self, btn): + self._cfg["provider_url"] = self._url_entry.get_text().strip() + self._cfg["model"] = self._model_entry.get_text().strip() + self._cfg["api_key"] = self._key_entry.get_text().strip() + self._cfg["health_check_interval_s"] = int(self._interval_spin.get_value()) + self._cfg["auto_restart_proxy"] = self._auto_restart_cb.get_active() + self._cfg["auto_switch_provider"] = self._auto_switch_cb.get_active() + _save_monitoring_config(self._cfg) + self._inc_buf.set_text("Configuration saved.\n") + + def _on_clear_store(self, btn): + _save_incident_store({"version": 1, "incidents": {}, "stats": {"ai_calls": 0, "tokens_used": 0}}) + self._store = {"version": 1, "incidents": {}, "stats": {"ai_calls": 0, "tokens_used": 0}} + self._refresh_incidents() + + def _refresh_incidents(self): + lines = [] + for pattern, inc in sorted(self._store.get("incidents", {}).items(), + key=lambda x: x[1].get("last_seen", ""), reverse=True): + sc = inc.get("success_count", 0) + fc = inc.get("fail_count", 0) + rate = sc / max(sc + fc, 1) + bar = "+" * min(int(rate * 10), 10) + "-" * (10 - min(int(rate * 10), 10)) + lines.append( + f"[{inc.get('last_seen', '?')[:16]}] {pattern}\n" + f" fix={inc.get('fix', '?')} success_rate={rate:.0%} [{bar}] " + f"seen={inc.get('occurrences', 0)}x\n" + ) + if not lines: + lines.append("No incidents recorded yet.\n") + lines.append("\nEnable AI Monitoring and use Codex to populate the store.\n") + self._inc_buf.set_text("\n".join(lines)) + + # ═══════════════════════════════════════════════════════════════════ # Main window # ═══════════════════════════════════════════════════════════════════ @@ -1143,7 +1661,7 @@ class LauncherWin(Gtk.Window): # header row hdr = Gtk.Box(spacing=8) vbox.pack_start(hdr, False, False, 0) - lbl = Gtk.Label(label="Codex Launcher v3.7.0") + lbl = Gtk.Label(label="Codex Launcher v3.8.0") lbl.set_use_markup(True) hdr.pack_start(lbl, False, False, 0) changelog_btn = Gtk.Button(label="Changelog") @@ -1161,6 +1679,9 @@ class LauncherWin(Gtk.Window): bgp_btn = Gtk.Button(label="AI BGP") bgp_btn.connect("clicked", lambda b: self._open_bgp()) hdr.pack_end(bgp_btn, False, False, 0) + mon_btn = Gtk.Button(label="AI Monitor") + mon_btn.connect("clicked", lambda b: self._open_monitoring()) + hdr.pack_end(mon_btn, False, False, 0) mgr_btn = Gtk.Button(label="Manage Endpoints") mgr_btn.connect("clicked", lambda b: self._open_mgr()) hdr.pack_end(mgr_btn, False, False, 0) @@ -1310,6 +1831,7 @@ class LauncherWin(Gtk.Window): self.show_all() self._rebuild_combo() self._log_dependency_status() + self._start_watcher() # ── helpers ────────────────────────────────────────────────── @@ -1456,13 +1978,84 @@ class LauncherWin(Gtk.Window): d.run(); d.destroy() def _open_bgp(self): - try: - self._bgp_window = BGPPoolMgr(self) - self._bgp_window.connect("destroy", lambda *_: setattr(self, "_bgp_window", None)) - except Exception as e: - import traceback; traceback.print_exc() - d = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, f"Error: {e}") - d.run(); d.destroy() + try: + self._bgp_window = BGPPoolMgr(self) + self._bgp_window.connect("destroy", lambda *_: setattr(self, "_bgp_window", None)) + except Exception as e: + import traceback; traceback.print_exc() + d = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, f"Error: {e}") + d.run(); d.destroy() + + def _open_monitoring(self): + try: + self._monitoring_window = AIMonitoringWindow(self) + self._monitoring_window.connect("destroy", lambda *_: setattr(self, "_monitoring_window", None)) + except Exception as e: + import traceback; traceback.print_exc() + d = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, f"Error: {e}") + d.run(); d.destroy() + + def _start_watcher(self): + cfg = _load_monitoring_config() + if not cfg.get("enabled"): + return + self._watcher = HealthWatcher( + on_failure=self._on_watcher_failure, + on_recovery=self._on_watcher_recovery, + on_signal=self._on_watcher_signal, + on_action=self._on_watcher_action, + ) + self._watcher.start() + self.log("AI Monitoring: watchdog started") + + def _on_watcher_failure(self, count): + GLib.idle_add(self.log, f"[AI Monitor] Proxy unresponsive (failures={count})") + + def _on_watcher_recovery(self): + GLib.idle_add(self.log, "[AI Monitor] Proxy recovered") + + def _on_watcher_signal(self, fault_id, category, line): + pass + + def _on_watcher_action(self, action, trigger): + cfg = _load_monitoring_config() + if action == "restart_proxy" and cfg.get("auto_restart_proxy"): + GLib.idle_add(self.log, f"[AI Monitor] Auto-restarting proxy (trigger: {trigger})") + GLib.idle_add(self._restart_proxy_from_watcher) + elif action == "clear_schema_cache": + try: + cap_file = Path.home() / ".cache/codex-proxy/provider-caps.json" + if cap_file.exists(): + cap_file.unlink() + GLib.idle_add(self.log, "[AI Monitor] Cleared corrupt schema cache") + except Exception as e: + GLib.idle_add(self.log, f"[AI Monitor] Failed to clear cache: {e}") + elif action == "delete_provider_caps": + try: + cap_file = Path.home() / ".cache/codex-proxy/provider-caps.json" + if cap_file.exists(): + cap_file.unlink() + GLib.idle_add(self.log, "[AI Monitor] Deleted corrupted provider-caps.json") + except Exception as e: + GLib.idle_add(self.log, f"[AI Monitor] Failed: {e}") + elif action == "kill_stale_restart": + GLib.idle_add(self.log, f"[AI Monitor] Killing stale processes + restarting (trigger: {trigger})") + self._kill() + GLib.idle_add(self._restart_proxy_from_watcher) + else: + GLib.idle_add(self.log, f"[AI Monitor] Alert: {action} (trigger: {trigger})") + + def _restart_proxy_from_watcher(self): + try: + ep_name = load_endpoints().get("default") + if not ep_name: + return + for ep in load_endpoints().get("endpoints", []): + if ep.get("name") == ep_name: + self._start_proxy(ep) + break + except Exception as e: + self.log(f"[AI Monitor] Proxy restart failed: {e}") def _open_usage(self): try: diff --git a/src/translate-proxy.py b/src/translate-proxy.py index 8946e15..335af93 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -3410,10 +3410,20 @@ class Handler(http.server.BaseHTTPRequestHandler): if self.path in ("/v1/models", "/models"): self.send_json(200, {"object": "list", "data": MODELS}) elif self.path in ("/health", "/v1/health"): + import resource as _res + _mem_mb = 0 + try: + _mem_mb = _res.getrusage(_res.RUSAGE_SELF).ru_maxrss / 1024 + except Exception: + pass + _uptime = time.time() - _START_TIME if '_START_TIME' in dir() else 0 self.send_json(200, {"ok": True, "backend": BACKEND, "target_url": TARGET_URL, "models": [m.get("id") for m in MODELS], - "bgp_routes": len(BGP_ROUTES)}) + "bgp_routes": len(BGP_ROUTES), + "uptime_s": round(_uptime, 1), + "memory_mb": round(_mem_mb, 1), + "requests_total": _STATS.get("requests", 0)}) else: self.send_error(404) @@ -4750,10 +4760,11 @@ def _handle_shutdown_signal(sig, frame): _SHUTDOWN_REQUESTED = True print(f"[SELF-REVIVE] Signal {sig} received, shutting down cleanly", flush=True) if 'SERVER' in globals() and SERVER: - SERVER.shutdown() - + SERVER.shutdown() + def main(): - global SERVER + global SERVER, _START_TIME + _START_TIME = time.time() _init_runtime() signal.signal(signal.SIGTERM, _handle_shutdown_signal) signal.signal(signal.SIGINT, _handle_shutdown_signal)