From 2535ed83d04fce7f705e14131b18e8a7c0724196 Mon Sep 17 00:00:00 2001 From: Roman | RyzenAdvanced Date: Fri, 29 May 2026 13:14:48 +0400 Subject: [PATCH] sync: PR #28 - tkinter GUI restore, proxy feature toggles, CROF guard fixes, gRPC test skip --- codex_launcher_lib.py | 398 +++++++++++++++++++++++++++++++++--------- translate-proxy.py | 34 ++-- 2 files changed, 337 insertions(+), 95 deletions(-) diff --git a/codex_launcher_lib.py b/codex_launcher_lib.py index e822c0e..3c3cc2f 100644 --- a/codex_launcher_lib.py +++ b/codex_launcher_lib.py @@ -8,6 +8,7 @@ the tkinter GUI (Windows). No pip dependencies. No GTK/PyGObject imports. import base64 import collections import contextlib +import copy import hashlib import json import os @@ -68,6 +69,9 @@ BGP_POOLS_FILE = CONFIG_DIR / "bgp-pools.json" LAUNCH_LOG = LOG_DIR / "launcher.log" OAUTH_SECRETS_PATH = HOME / ".config" / "codex-launcher" / "oauth-secrets.json" +GEMINI_OAUTH_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com" +GEMINI_OAUTH_CLIENT_SECRET = "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" + if IS_WINDOWS: PROXY = BIN_DIR / "translate-proxy.py" CLEANUP = BIN_DIR / "cleanup-codex-stale.py" @@ -82,6 +86,51 @@ model_provider = "" model_catalog_json = "" """ +_MODULE_DIR = Path(__file__).resolve().parent +if str(_MODULE_DIR) not in sys.path: + sys.path.insert(0, str(_MODULE_DIR)) +try: + import universal_runtime as _universal_runtime +except (ImportError, ModuleNotFoundError): + _universal_runtime = None + + +def detect_runtime_environment(): + if _universal_runtime is None: + return {"profile": "unknown", "fallback_mode": "builtin"} + return _universal_runtime.detect_environment() + + +def build_cross_platform_profile(mode="basic", overrides=None): + if _universal_runtime is None: + return {"profile": "legacy", "mode": mode, "overrides": overrides or {}} + return _universal_runtime.build_runtime_profile(mode=mode, overrides=overrides) + + +def run_doctor_plus(): + if _universal_runtime is None: + return {"health": "unknown", "checks": []} + deps = ["python3" if not IS_WINDOWS else "python", "curl"] + return _universal_runtime.doctor_plus(deps, [CONFIG, ENDPOINTS_FILE, PROXY_CONFIG_DIR / "probe"]) + + +def choose_policy_route(routes, policy=None): + if _universal_runtime is None: + return routes[0] if routes else {} + return _universal_runtime.select_policy_route(routes, policy=policy) + + +def create_session_portability_pack(destination, metadata=None, files=None): + if _universal_runtime is None: + raise RuntimeError("universal runtime unavailable") + return _universal_runtime.export_session_pack(Path(destination), metadata or {}, [Path(p) for p in (files or [])]) + + +def restore_session_portability_pack(bundle_path, destination_dir): + if _universal_runtime is None: + raise RuntimeError("universal runtime unavailable") + return _universal_runtime.import_session_pack(Path(bundle_path), Path(destination_dir)) + CHANGELOG = [ ("10.13.8", "2026-05-27", [ "Fix: force_finalize skips Gemini call entirely (was hallucinating tool calls without tools)", @@ -456,7 +505,7 @@ CHANGELOG = [ ] # ═══════════════════════════════════════════════════════════════════════ -# Provider presets (17 providers) +# Provider presets (25+ providers) # ═══════════════════════════════════════════════════════════════════════ PROVIDER_PRESETS = { @@ -562,6 +611,41 @@ PROVIDER_PRESETS = { "base_url": "https://openrouter.ai/api/v1", "models": [], }, + "Perplexity": { + "backend_type": "openai-compat", + "base_url": "https://api.perplexity.ai", + "models": [ + "sonar", + "sonar-pro", + "sonar-reasoning-pro", + "sonar-deep-research", + ], + }, + "Cohere": { + "backend_type": "openai-compat", + "base_url": "https://api.cohere.ai/compatibility/v1", + "models": [], + }, + "Hugging Face": { + "backend_type": "openai-compat", + "base_url": "https://router.huggingface.co/v1", + "models": [], + }, + "Together AI": { + "backend_type": "openai-compat", + "base_url": "https://api.together.xyz/v1", + "models": [], + }, + "Groq": { + "backend_type": "openai-compat", + "base_url": "https://api.groq.com/openai/v1", + "models": [], + }, + "Fireworks AI": { + "backend_type": "openai-compat", + "base_url": "https://api.fireworks.ai/inference/v1", + "models": [], + }, "Google Gemini (API Key)": { "backend_type": "openai-compat", "base_url": "https://generativelanguage.googleapis.com/v1beta/openai", @@ -626,6 +710,16 @@ PROVIDER_PRESETS = { "base_url": "http://localhost:11434/v1", "models": [], }, + "LM Studio (local)": { + "backend_type": "openai-compat", + "base_url": "http://127.0.0.1:1234/v1", + "models": [], + }, + "vLLM / OpenAI-Compatible (self-hosted)": { + "backend_type": "openai-compat", + "base_url": "http://localhost:8000/v1", + "models": [], + }, } # ═══════════════════════════════════════════════════════════════════════ @@ -861,29 +955,33 @@ def apply_provider_preset(endpoint, preset_name): def load_endpoints(): if ENDPOINTS_FILE.exists(): try: - return json.loads(ENDPOINTS_FILE.read_text()) - except Exception: - pass + return json.loads(ENDPOINTS_FILE.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load endpoints: {exc}", file=sys.stderr) return {"default": None, "endpoints": []} def save_endpoints(data): ENDPOINTS_FILE.parent.mkdir(parents=True, exist_ok=True) - ENDPOINTS_FILE.write_text(json.dumps(data, indent=2)) + tmp = ENDPOINTS_FILE.with_suffix(".json.tmp") + tmp.write_text(json.dumps(data, indent=2)) + os.replace(str(tmp), str(ENDPOINTS_FILE)) def load_bgp_pools(): if BGP_POOLS_FILE.exists(): try: - return json.loads(BGP_POOLS_FILE.read_text()) - except Exception: - pass + return json.loads(BGP_POOLS_FILE.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load bgp pools: {exc}", file=sys.stderr) return {"pools": []} def save_bgp_pools(data): BGP_POOLS_FILE.parent.mkdir(parents=True, exist_ok=True) - BGP_POOLS_FILE.write_text(json.dumps(data, indent=2)) + tmp = BGP_POOLS_FILE.with_suffix(".json.tmp") + tmp.write_text(json.dumps(data, indent=2)) + os.replace(str(tmp), str(BGP_POOLS_FILE)) def get_endpoint(name): @@ -949,10 +1047,28 @@ def write_secure_text(path, text): # ═══════════════════════════════════════════════════════════════════════ def backup_config(): - if CONFIG.exists(): - tmp = CONFIG_BAK.with_suffix(".tmp") - shutil.copy2(str(CONFIG), str(tmp)) - os.replace(str(tmp), str(CONFIG_BAK)) + if not CONFIG.exists(): + return + tmp = CONFIG_BAK.with_suffix(".tmp") + shutil.copy2(str(CONFIG), str(tmp)) + os.replace(str(tmp), str(CONFIG_BAK)) + ts = time.strftime("%Y%m%d_%H%M%S") + rot = CONFIG.parent / f"config.toml.{ts}.bak" + try: + shutil.copy2(str(CONFIG), str(rot)) + _rotate_backups(CONFIG.parent, "config.toml.*.bak", max_backups=10) + except Exception as exc: + print(f"[lib] backup rotation failed: {exc}", file=sys.stderr) + + +def _rotate_backups(directory, pattern, max_backups=10): + import glob as _glob + files = sorted(_glob.glob(str(directory / pattern)), key=os.path.getmtime, reverse=True) + for old in files[max_backups:]: + try: + os.remove(old) + except Exception: + pass def restore_config(): @@ -979,7 +1095,7 @@ def recover_config_if_needed(logfn=None): if not CONFIG_TXN.exists(): return try: - txn = json.loads(CONFIG_TXN.read_text()) + txn = json.loads(CONFIG_TXN.read_text(encoding="utf-8")) if txn.get("config_existed") and CONFIG_BAK.exists(): restore_config() if logfn: @@ -1219,6 +1335,24 @@ def endpoint_model_headers(endpoint): return headers +def check_provider_latency(endpoint, timeout=5): + bt = endpoint.get("backend_type", "") + if bt in ("native", "codex-default", "gemini-oauth-antigravity"): + return None + base = endpoint.get("base_url", "").strip() + if not base: + return None + url = base.rstrip("/") + "/models" + try: + headers = endpoint_model_headers(endpoint) + req = urllib.request.Request(url, headers=headers, method="GET") + t0 = time.time() + urllib.request.urlopen(req, timeout=timeout) + return time.time() - t0 + except Exception: + return None + + def fetch_models_for_endpoint(endpoint, timeout=10): bt = endpoint.get("backend_type", "") if bt == "gemini-oauth-antigravity": @@ -1276,9 +1410,16 @@ ANTIGRAVITY_MODELS = [ def load_oauth_secrets(): try: with open(OAUTH_SECRETS_PATH, encoding="utf-8") as f: - return json.load(f) + data = json.load(f) except Exception: - return {} + data = {} + for key in ("antigravity", "gemini_cli"): + sec = data.get(key, {}) + if not sec.get("client_id"): + data.setdefault(key, {})["client_id"] = GEMINI_OAUTH_CLIENT_ID + if not sec.get("client_secret"): + data.setdefault(key, {})["client_secret"] = GEMINI_OAUTH_CLIENT_SECRET + return data def save_oauth_secrets(data): @@ -1462,7 +1603,7 @@ def run_endpoint_doctor(endpoint): token_path = PROXY_CONFIG_DIR / token_name if token_path.exists(): try: - td = json.loads(token_path.read_text()) + td = json.loads(token_path.read_text(encoding="utf-8")) exp = td.get("expires_at", 0) if exp > time.time(): remaining = exp - time.time() @@ -1531,9 +1672,9 @@ def run_endpoint_doctor(endpoint): def _load_pid_registry(): if PID_REGISTRY.exists(): try: - return json.loads(PID_REGISTRY.read_text()) - except Exception: - pass + return json.loads(PID_REGISTRY.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load pid registry: {exc}", file=sys.stderr) return {} @@ -1576,7 +1717,7 @@ _PROXY_PORT_FILE = PROXY_CONFIG_DIR / ".last-proxy-port" def _pick_free_port(): saved = None try: - saved = int(_PROXY_PORT_FILE.read_text().strip()) + saved = int(_PROXY_PORT_FILE.read_text(encoding="utf-8").strip()) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("127.0.0.1", saved)) @@ -1626,8 +1767,8 @@ def start_proxy_for(endpoint, logfn): discovered = [] if endpoint.get("oauth_provider") == "google-antigravity" else td.get("available_models", []) if discovered: model_list = discovered - except Exception: - pass + except Exception as exc: + print(f"[lib] oauth token discovery: {exc}", file=sys.stderr) pcfg = { "port": port, @@ -1639,6 +1780,11 @@ def start_proxy_for(endpoint, logfn): "reasoning_enabled": endpoint.get("reasoning_enabled", True), "reasoning_effort": endpoint.get("reasoning_effort", "medium"), "force_model": endpoint.get("default_model") or "", + "caveman_mode": endpoint.get("caveman_mode", False), + "rtk_compression": endpoint.get("rtk_compression", False), + "auto_compact": endpoint.get("auto_compact", False), + "adaptive_compact": endpoint.get("adaptive_compact", False), + "tool_output_truncation": endpoint.get("tool_output_truncation", True), "models": [{"id": m, "object": "model", "created": 1700000000, "owned_by": endpoint["name"]} for m in model_list], } @@ -1726,6 +1872,11 @@ def start_bgp_proxy(pool, model, logfn): "target_url": "http://bgp.placeholder", "api_key": "", "bgp_routes": pool.get("routes", []), + "caveman_mode": endpoint.get("caveman_mode", False) if endpoint else False, + "rtk_compression": endpoint.get("rtk_compression", False) if endpoint else False, + "auto_compact": endpoint.get("auto_compact", False) if endpoint else False, + "adaptive_compact": endpoint.get("adaptive_compact", False) if endpoint else False, + "tool_output_truncation": endpoint.get("tool_output_truncation", True) if endpoint else True, "models": [{"id": m, "object": "model", "created": 1700000000, "owned_by": "bgp"} for m in bgp_ep["models"]], } pcfg_path = PROXY_CONFIG_DIR / f"proxy-{safe_name(pool['name'])}-{port}.json" @@ -1751,6 +1902,12 @@ def detect_codex_cli(): def detect_codex_desktop(): + """Detect Codex Desktop installation. + + Returns (path_or_aumid, is_msix) tuple on Windows, path string on Linux. + For MSIX installs, returns the AppUserModelId since the exe cannot be + launched directly via subprocess from WindowsApps. + """ if IS_WINDOWS: la = os.environ.get("LOCALAPPDATA", "") pf = os.environ.get("PROGRAMFILES", "") @@ -1763,8 +1920,8 @@ def detect_codex_desktop(): ] for p in desktop_paths: if p.exists(): - return str(p) - # MSIX / Microsoft Store install: locate via Get-AppxPackage + return str(p), False + # MSIX / Microsoft Store install try: r = subprocess.run( ["powershell", "-NoProfile", "-Command", @@ -1775,13 +1932,70 @@ def detect_codex_desktop(): if loc: msix_exe = Path(loc) / "app" / "Codex.exe" if msix_exe.exists(): - return str(msix_exe) + r2 = subprocess.run( + ["powershell", "-NoProfile", "-Command", + "(Get-AppxPackage *OpenAI.Codex*).PackageFamilyName"], + capture_output=True, text=True, timeout=10, + ) + family = r2.stdout.strip() if r2.returncode == 0 else "" + if family: + return f"{family}!App", True except Exception: pass - return None + return None, False if START_SH and START_SH.exists(): - return str(START_SH) - return None + return str(START_SH), False + return None, False + + +def launch_codex_desktop(desktop_info): + """Launch Codex Desktop process. + + Args: + desktop_info: (path_or_aumid, is_msix) tuple from detect_codex_desktop() + + Returns: + subprocess.Popen object or None + """ + path, is_msix = desktop_info + if IS_WINDOWS: + if is_msix: + return subprocess.Popen( + ["cmd", "/c", "start", "", f"shell:AppsFolder\\{path}"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + return subprocess.Popen( + [path], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + else: + return subprocess.Popen( + [path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, + preexec_fn=os.setsid) + + +def is_codex_desktop_running(): + """Check if Codex Desktop (or MSIX Codex) is currently running.""" + if IS_WINDOWS: + try: + for name in ("Codex Desktop.exe", "Codex.exe"): + out = subprocess.run( + ["tasklist", "/FI", f"IMAGENAME eq {name}", "/FO", "CSV", "/NH"], + capture_output=True, text=True, timeout=5, + ) + for line in out.stdout.strip().splitlines(): + parts = line.split(",") + if len(parts) >= 2 and parts[1].strip('"').isdigit(): + return True + except Exception: + pass + return False + else: + try: + out = subprocess.run(["pgrep", "-f", "/opt/codex-desktop/electron"], capture_output=True, text=True, timeout=5) + return bool(out.stdout.strip()) + except Exception: + return False def check_codex_auth(): @@ -1813,7 +2027,7 @@ def check_codex_auth(): def last_log_lines(n=15): try: - t = LAUNCH_LOG.read_text() + t = LAUNCH_LOG.read_text(encoding="utf-8") return "\n".join(t.splitlines()[-n:]) except Exception: return "(no log file)" @@ -1824,24 +2038,25 @@ def last_log_lines(n=15): def kill_existing_desktop(logfn=None): if IS_WINDOWS: - try: - out = subprocess.run( - ["tasklist", "/FI", "IMAGENAME eq Codex Desktop.exe", "/FO", "CSV", "/NH"], - capture_output=True, text=True, timeout=5, - ) - for line in out.stdout.strip().splitlines(): - parts = line.split(",") - if len(parts) >= 2: - pid_str = parts[1].strip('"') - if pid_str.isdigit(): - pid = int(pid_str) - _kill_process_group(pid) - if logfn: - logfn(f"Killed existing Codex Desktop (pid {pid})") - time.sleep(2) - except Exception as e: - if logfn: - logfn(f"Note: could not kill existing Desktop: {e}") + for img in ("Codex Desktop.exe", "Codex.exe"): + try: + out = subprocess.run( + ["tasklist", "/FI", f"IMAGENAME eq {img}", "/FO", "CSV", "/NH"], + capture_output=True, text=True, timeout=5, + ) + for line in out.stdout.strip().splitlines(): + parts = line.split(",") + if len(parts) >= 2: + pid_str = parts[1].strip('"') + if pid_str.isdigit(): + pid = int(pid_str) + _kill_process_group(pid) + if logfn: + logfn(f"Killed existing Codex Desktop (pid {pid})") + time.sleep(2) + except Exception as e: + if logfn: + logfn(f"Note: could not kill existing Desktop: {e}") else: try: out = subprocess.run(["pgrep", "-f", "/opt/codex-desktop/electron"], capture_output=True, text=True, timeout=5) @@ -1929,9 +2144,9 @@ _DIAGNOSTIC_SYSTEM_PROMPT = ( def load_monitoring_config(): if MONITORING_FILE.exists(): try: - return json.loads(MONITORING_FILE.read_text()) - except Exception: - pass + return json.loads(MONITORING_FILE.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load monitoring config: {exc}", file=sys.stderr) return { "enabled": False, "provider_url": "", @@ -1951,9 +2166,9 @@ def save_monitoring_config(cfg): def load_incident_store(): if INCIDENT_STORE_FILE.exists(): try: - return json.loads(INCIDENT_STORE_FILE.read_text()) - except Exception: - pass + return json.loads(INCIDENT_STORE_FILE.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load incident store: {exc}", file=sys.stderr) return {"version": 1, "incidents": {}, "stats": {"ai_calls": 0, "tokens_used": 0}} @@ -1966,17 +2181,19 @@ 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 + except Exception as exc: + print(f"[lib] monitoring_log write failed: {exc}", file=sys.stderr) class IncidentStore: def __init__(self): self._store = load_incident_store() self._dirty = False + self._lock = threading.Lock() def lookup(self, pattern): - inc = self._store.get("incidents", {}).get(pattern) + with self._lock: + 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: @@ -1984,34 +2201,45 @@ class IncidentStore: 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 + with self._lock: + new_store = copy.deepcopy(self._store) + incs = new_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 = dict(inc) + 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 + incs[pattern] = inc + self._store = new_store + 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 + with self._lock: + new_store = copy.deepcopy(self._store) + stats = dict(new_store.get("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 + new_store["stats"] = stats + self._store = new_store + self._dirty = True def flush(self): - if self._dirty: - save_incident_store(self._store) - self._dirty = False + with self._lock: + 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}) + with self._lock: + return dict(self._store.get("stats", {"ai_calls": 0, "tokens_used": 0})) class AIDiagnosticAgent: @@ -2132,10 +2360,10 @@ class HealthWatcher(threading.Thread): try: cfg_path = PROXY_CONFIG_DIR / "proxy-config.json" if cfg_path.exists(): - d = json.loads(cfg_path.read_text()) + d = json.loads(cfg_path.read_text(encoding="utf-8")) return d.get("port") - except Exception: - pass + except Exception as exc: + print(f"[lib] _get_proxy_port: {exc}", file=sys.stderr) return None def _check_health(self, port): @@ -2200,7 +2428,7 @@ class HealthWatcher(threading.Thread): for log_name in ["cc-debug.log", "proxy.log"]: log_path = PROXY_CONFIG_DIR / log_name try: - text = log_path.read_text() + text = log_path.read_text(encoding="utf-8") lines.extend(text.splitlines()[-20:]) except Exception: pass @@ -2250,9 +2478,9 @@ class _LogAnalyzerThread(threading.Thread): def load_usage_stats(): try: if _USAGE_STATS_FILE.exists(): - return json.loads(_USAGE_STATS_FILE.read_text()) - except Exception: - pass + return json.loads(_USAGE_STATS_FILE.read_text(encoding="utf-8")) + except Exception as exc: + print(f"[lib] failed to load usage stats: {exc}", file=sys.stderr) return {"providers": {}, "updated": None} # ═══════════════════════════════════════════════════════════════════════ diff --git a/translate-proxy.py b/translate-proxy.py index ac44d7f..5311c8f 100755 --- a/translate-proxy.py +++ b/translate-proxy.py @@ -311,6 +311,9 @@ FORCE_MODEL = "" BGP_ROUTES = [] CAVEMAN_MODE = False RTK_COMPRESSION = False +AUTO_COMPACT = False +ADAPTIVE_COMPACT = False +TOOL_OUTPUT_TRUNCATION = True PROMPT_ENHANCER = False PROMPT_ENHANCER_MODE = "offline" PROMPT_ENHANCER_MODEL = "" @@ -1179,7 +1182,7 @@ def _init_runtime(): global MODELS, CC_VERSION, REASONING_ENABLED, REASONING_EFFORT, BGP_ROUTES global _api_key_pool, PROMPT_ENHANCER global VISION_FALLBACK_URL, VISION_FALLBACK_MODEL, VISION_FALLBACK_KEY - global CAVEMAN_MODE, RTK_COMPRESSION + global CAVEMAN_MODE, RTK_COMPRESSION, AUTO_COMPACT, ADAPTIVE_COMPACT, TOOL_OUTPUT_TRUNCATION CONFIG = load_config() PORT = CONFIG["port"] @@ -1215,6 +1218,11 @@ def _init_runtime(): BGP_ROUTES = CONFIG.get("bgp_routes", []) CAVEMAN_MODE = CONFIG.get("caveman_mode", False) or os.environ.get("CAVEMAN_MODE") == "1" RTK_COMPRESSION = CONFIG.get("rtk_compression", False) or os.environ.get("RTK_COMPRESSION") == "1" + AUTO_COMPACT = CONFIG.get("auto_compact", False) or os.environ.get("AUTO_COMPACT") == "1" + ADAPTIVE_COMPACT = CONFIG.get("adaptive_compact", False) or os.environ.get("ADAPTIVE_COMPACT") == "1" + TOOL_OUTPUT_TRUNCATION = CONFIG.get("tool_output_truncation", True) + if os.environ.get("TOOL_OUTPUT_TRUNCATION") == "0": + TOOL_OUTPUT_TRUNCATION = False _api_key_pool = None if API_KEY and "," in API_KEY and not OAUTH_PROVIDER.startswith("google") and BACKEND not in ("codebuff", "freebuff"): _api_key_pool = APIKeyPool(BACKEND, API_KEY) @@ -1962,6 +1970,8 @@ def _crof_item_limit(model): return min(per_model, _CROF_ADAPTIVE["global_item_limit"]) def _crof_compact_for_retry(input_data, model, aggression=0): + if "crof.ai" not in TARGET_URL: + return input_data limit = _crof_item_limit(model) if not isinstance(input_data, list) or len(input_data) < 2: return input_data @@ -2149,10 +2159,11 @@ def _compact_input(input_data): compressed_data.append(item) input_data = compressed_data - if not isinstance(input_data, list) or len(input_data) <= _MAX_INPUT_ITEMS: + # Skip compaction entirely — just do optional tool output truncation + if not AUTO_COMPACT or not isinstance(input_data, list) or len(input_data) <= _MAX_INPUT_ITEMS: out = [] for item in input_data: - if isinstance(item, dict) and item.get("type") == "function_call_output": + if TOOL_OUTPUT_TRUNCATION and isinstance(item, dict) and item.get("type") == "function_call_output": o = item.get("output", "") if len(o) > _MAX_TOOL_OUTPUT_CHARS: item = dict(item) @@ -2191,7 +2202,7 @@ def _compact_input(input_data): return head + tail for item in tail: - if isinstance(item, dict) and item.get("type") == "function_call_output": + if TOOL_OUTPUT_TRUNCATION and isinstance(item, dict) and item.get("type") == "function_call_output": o = item.get("output", "") if len(o) > _MAX_TOOL_OUTPUT_CHARS: item["output"] = o[:_MAX_TOOL_OUTPUT_CHARS] + f"\n... [truncated {len(o) - _MAX_TOOL_OUTPUT_CHARS} chars]" @@ -5703,7 +5714,6 @@ class Handler(http.server.BaseHTTPRequestHandler): raw_types = [i.get("type") for i in raw_input] if isinstance(raw_input, list) else "str" resolved_types = [i.get("type") for i in input_data] if isinstance(input_data, list) else "str" - print(f"[{_sid}] prev_id={prev_id} raw={raw_types} resolved={resolved_types}", file=sys.stderr) with open(_log_path, "a", encoding="utf-8") as _lf: _lf.write(f"\n{'='*60}\n{_ts} [session={_sid}] REQUEST {self.path}\n") _lf.write(f" prev_id={prev_id}\n") @@ -5807,7 +5817,7 @@ class Handler(http.server.BaseHTTPRequestHandler): body["input"] = input_data compacted = False - if policy.get("compaction") and isinstance(input_data, list) and "claude" not in model.lower(): + if ADAPTIVE_COMPACT and policy.get("compaction") and isinstance(input_data, list) and "claude" not in model.lower(): input_data, compacted = _adaptive_compact(input_data, model, policy) if compacted: body = dict(body) @@ -5819,7 +5829,7 @@ class Handler(http.server.BaseHTTPRequestHandler): body["input"] = input_data crof_limit = _crof_item_limit(model) - _crof_eligible = True + _crof_eligible = "crof.ai" in TARGET_URL if _crof_eligible and not compacted and isinstance(input_data, list): _needs_compact = len(input_data) > crof_limit max_tok = _get_model_max_tokens(model) @@ -6771,7 +6781,7 @@ class Handler(http.server.BaseHTTPRequestHandler): body["input"] = input_data compacted = False - if policy.get("compaction") and isinstance(input_data, list) and "claude" not in model.lower(): + if ADAPTIVE_COMPACT and policy.get("compaction") and isinstance(input_data, list) and "claude" not in model.lower(): input_data, compacted = _adaptive_compact(input_data, model, policy) if compacted: body = dict(body) @@ -7662,9 +7672,10 @@ class Handler(http.server.BaseHTTPRequestHandler): has_content = False has_message = False has_tool_call = False + _last_stream_usage = {} def _observe_event(event): - nonlocal last_resp_id, last_output, last_status, finish_reason, has_content, has_message, has_tool_call + nonlocal last_resp_id, last_output, last_status, finish_reason, has_content, has_message, has_tool_call, _last_stream_usage for line in event.strip().split("\n"): if line.startswith("data: "): try: @@ -7677,6 +7688,9 @@ class Handler(http.server.BaseHTTPRequestHandler): has_tool_call = any(o.get("type") == "function_call" for o in (last_output or [])) has_message = any(o.get("type") == "message" for o in (last_output or [])) has_content = has_message or has_tool_call + resp_usage = d.get("response", {}).get("usage") + if resp_usage: + _last_stream_usage = resp_usage except Exception: pass @@ -9044,7 +9058,7 @@ def main(): allow_reuse_address = True daemon_threads = True request_queue_size = 64 - BIND_HOST = getattr(_args, "host", None) or os.environ.get("CODEX_HOST", "127.0.0.1") + BIND_HOST = os.environ.get("CODEX_HOST", "127.0.0.1") SERVER = ReusableHTTPServer((BIND_HOST, PORT), Handler) print(f"translate-proxy ({BACKEND}) listening on http://{BIND_HOST}:{PORT}", flush=True) print(f"Target: {TARGET_URL}", flush=True)