From c3ba3286ff09596a5913622452602e1d7c0f29e3 Mon Sep 17 00:00:00 2001 From: Roman | RyzenAdvanced Date: Tue, 26 May 2026 15:02:02 +0400 Subject: [PATCH] v3.11.0: merge cobra PR, smart-continue, hot-reload, XML extraction - Merge PR #5 from cobra91: concurrency semaphore, auto-continue, SO_REUSEADDR, proxy-stderr.log, stream diagnostics, timeout handler, restart proxy fix - Tool call argument normalizer, smart-continue loop, XML extraction - API key hot-reload with mtime tracking + /admin/ endpoints - GUI hot-reload on endpoint edit with upstream verification - Synthetic tool-results disabled (caused deepseek-v4-pro truncation) - Version bump 3.10.12 -> 3.11.0, rebuild .deb --- CHANGELOG.md | 20 + codex-launcher_3.11.0_all.deb | Bin 0 -> 137652 bytes install.sh | 12 +- src/codex-launcher-gui | 333 +++++++++++++---- src/codex_launcher_lib.py | 22 ++ src/translate-proxy.py | 233 +++++++++--- translate-proxy.py | 671 +++++++++++++++++++++++++++++++--- 7 files changed, 1100 insertions(+), 191 deletions(-) create mode 100644 codex-launcher_3.11.0_all.deb diff --git a/CHANGELOG.md b/CHANGELOG.md index 06357ca..fe1eb0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## v3.11.0 (2026-05-26) + +**Cobra PR Merge + Smart Continuation + API Key Hot-Reload** + +### New Features +- **Concurrency semaphore (max 3)**: limits parallel upstream requests to prevent rate-limiting +- **Auto-continue for truncated text**: detects text ending in `:`, `(`, `;`, `…` or `finish_reason=length`, continues seamlessly +- **SO_REUSEADDR on sticky port**: prevents `TIME_WAIT` from changing port on restart +- **proxy-stderr.log**: persistent log file for proxy errors +- **Stream diagnostics**: logs event count, finish reason, content flag, elapsed time after each stream +- **Timeout/OSError handler**: sends proper `response.failed` SSE event instead of silently dropping connection +- **Restart Proxy button**: now only restarts proxy without killing Codex Desktop +- **Tool call argument normalizer**: fixes capital-A `Arguments` key, strips markdown/JSON code block wrapping from tool call arguments +- **Smart-continue loop (2× retries)**: escalating nudge messages when model returns text-only stop mid-task +- **XML tool call extraction**: parses `name{args}` from model text output, injects as real `function_call` items +- **Auto-continue + smart-continue ordered with skip guard**: prevents both from double-firing on the same response +- **API key hot-reload**: mtime tracking detects config changes, `/admin/reload` endpoint triggers hot-reload, `/admin/verify-key` tests key against upstream +- **GUI hot-reload**: auto-refreshes proxy key on endpoint edit, verifies with upstream — no proxy restart needed +- **Synthetic tool-results disabled**: was causing deepseek-v4-pro truncation on opencode.ai + ## v3.10.12 (2026-05-26) **Sticky Endpoint, Claude Fixes, Guardrail Skip, Anti-Stall** diff --git a/codex-launcher_3.11.0_all.deb b/codex-launcher_3.11.0_all.deb new file mode 100644 index 0000000000000000000000000000000000000000..44561f07b9feb34d9fda54ab1077a9a1e274553f GIT binary patch literal 137652 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�j}m4&!KU) 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 OH2~pK67 /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 3dfd4ac..7fa065b 100755 --- a/src/codex-launcher-gui +++ b/src/codex-launcher-gui @@ -26,6 +26,19 @@ model_catalog_json = "" """ CHANGELOG = [ + ("3.11.0", "2026-05-26", [ + "Merge cobra PR: concurrency semaphore (max 3), auto-continue for truncated text", + "SO_REUSEADDR on sticky port, proxy-stderr.log, stream diagnostics logging", + "Timeout/OSError handler sends response.failed SSE instead of silent drop", + "Restart Proxy button: only restarts proxy without killing Codex Desktop", + "Tool call argument normalizer: fixes Arguments→arguments, strips markdown wrapping", + "Smart-continue loop (2× retries): escalating nudges when model stops text-only mid-task", + "XML tool call extraction: parses patterns from text, injects as real calls", + "Auto-continue + smart-continue ordered with skip guard to avoid double-firing", + "API key hot-reload with mtime tracking + /admin/reload + /admin/verify-key endpoints", + "GUI hot-reload: auto-refreshes proxy key on endpoint edit, verifies with upstream", + "Synthetic tool-results disabled: was causing deepseek-v4-pro truncation on opencode.ai", + ]), ("3.10.4", "2026-05-25", [ "OAuth Secrets editor in GUI — update client ID/secret without editing files", "Secrets stored in ~/.config/codex-launcher/oauth-secrets.json (not in repo)", @@ -361,7 +374,7 @@ PROVIDER_PRESETS = { }, "Google Antigravity (OAuth)": { "backend_type": "gemini-oauth-antigravity", - "base_url": "https://daily-cloudcode-pa.sandbox.googleapis.com", + "base_url": "https://cloudcode-pa.googleapis.com", "oauth_provider": "google-antigravity", "models": [ "Gemini 3.5 Flash (High)", "Gemini 3.5 Flash (Medium)", "Gemini 3.5 Flash (Low)", @@ -1782,6 +1795,64 @@ class AIMonitoringWindow(Gtk.Window): # Main window # ═══════════════════════════════════════════════════════════════════ +def _oauth_discover_project(access_token, token_path, tokens): + project_id = "" + try: + lr = urllib.request.Request( + "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", + data=json.dumps({}).encode(), + headers={"Content-Type": "application/json", + "Authorization": f"Bearer {access_token}", + "User-Agent": "google-api-nodejs-client/9.15.1"}) + lresp = urllib.request.urlopen(lr, timeout=15) + ldata = json.loads(lresp.read()) + p = ldata.get("cloudaicompanionProject", "") + if isinstance(p, dict): + project_id = p.get("id", "") + elif isinstance(p, str): + project_id = p + except Exception: + pass + if not project_id: + return "" + try: + test_url = f"https://cloudcode-pa.googleapis.com/v1internal:listModels?project={project_id}" + test_req = urllib.request.Request(test_url, + headers={"Authorization": f"Bearer {access_token}", + "User-Agent": "google-api-nodejs-client/9.15.1"}) + urllib.request.urlopen(test_req, timeout=10) + except urllib.error.HTTPError as e: + if e.code == 403 and "SERVICE_DISABLED" in (e.read().decode()[:500]): + print(f"[oauth] project {project_id} has API disabled, searching for valid project...", file=sys.stderr) + try: + list_req = urllib.request.Request( + "https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState:ACTIVE", + headers={"Authorization": f"Bearer {access_token}"}) + list_resp = urllib.request.urlopen(list_req, timeout=15) + projects = json.loads(list_resp.read()).get("projects", []) + for proj in projects: + pid = proj.get("projectId", "") + if not pid or pid == project_id: + continue + try: + t2 = urllib.request.Request( + f"https://cloudcode-pa.googleapis.com/v1internal:listModels?project={pid}", + headers={"Authorization": f"Bearer {access_token}", + "User-Agent": "google-api-nodejs-client/9.15.1"}) + urllib.request.urlopen(t2, timeout=10) + project_id = pid + print(f"[oauth] found working project: {pid}", file=sys.stderr) + break + except Exception: + continue + except Exception: + pass + tokens["project_id"] = project_id + with open(token_path, "w") as f: + json.dump(tokens, f, indent=2) + os.chmod(token_path, 0o600) + return project_id + class LauncherWin(Gtk.Window): def __init__(self): super().__init__(title="Codex Launcher") @@ -1798,7 +1869,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.10.7") + lbl = Gtk.Label(label="Codex Launcher v3.10.9") lbl.set_use_markup(True) hdr.pack_start(lbl, False, False, 0) changelog_btn = Gtk.Button(label="Changelog") @@ -2832,63 +2903,163 @@ class LauncherWin(Gtk.Window): _stop_proxy() Gtk.main_quit() - def _google_reoauth(self, provider): - secrets_path = os.path.expanduser("~/.config/codex-launcher/oauth-secrets.json") - try: - with open(secrets_path) as f: - secrets = json.load(f) - except Exception: - secrets = {} + def _google_reoauth(self, provider, parent_dlg=None): + import http.server is_antigravity = provider == "google-antigravity" sec_key = "antigravity" if is_antigravity else "gemini_cli" - sec = secrets.get(sec_key, {}) - client_id = sec.get("client_id", "") - client_secret = sec.get("client_secret", "") - if not client_id or not client_secret: + _sp = os.path.expanduser("~/.config/codex-launcher/oauth-secrets.json") + try: + with open(_sp) as _f: + _secrets_data = json.load(_f) + except Exception: + _secrets_data = {} + sec = _secrets_data.get(sec_key, {}) + CLIENT_ID = sec.get("client_id", "") + CLIENT_SECRET = sec.get("client_secret", "") + if not CLIENT_ID or not CLIENT_SECRET: self._show_error_dialog("Missing OAuth secrets", f"No client_id/client_secret for {sec_key}.\nSet them in OAuth Secrets first.") return token_file = "google-antigravity-oauth-token.json" if is_antigravity else "google-cli-oauth-token.json" token_path = os.path.expanduser(f"~/.cache/codex-proxy/{token_file}") - redirect = "urn:ietf:wg:oauth:2.0:oob" - auth_url = (f"https://accounts.google.com/o/oauth2/v2/auth?client_id={client_id}" - f"&redirect_uri={urllib.parse.quote(redirect)}" - f"&response_type=code&scope={urllib.parse.quote('https://www.googleapis.com/auth/cloud-platform')}" - f"&access_type=offline&prompt=consent") - webbrowser.open(auth_url) - code_dlg = Gtk.Dialog(title=f"Re-OAuth: {'Antigravity' if is_antigravity else 'Gemini CLI'}", parent=self, modal=True) - code_dlg.add_button("Cancel", Gtk.ResponseType.CANCEL) - code_dlg.add_button("Exchange", Gtk.ResponseType.OK) - code_dlg.set_default_size(500, 180) - ca = code_dlg.get_content_area() + provider_kind = "antigravity" if is_antigravity else "cli" + + if is_antigravity: + SCOPES = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + "https://www.googleapis.com/auth/cclog", + "https://www.googleapis.com/auth/experimentsandconfigs", + ] + port = 51121 + redirect_uri = f"http://localhost:{port}/oauth-callback" + callback_path = "/oauth-callback" + else: + SCOPES = [ + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ] + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(("127.0.0.1", 0)) + port = s.getsockname()[1] + redirect_uri = f"http://127.0.0.1:{port}/oauth2callback" + callback_path = "/oauth2callback" + + state = secrets.token_hex(32) + verifier = secrets.token_urlsafe(64) + challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode() + + scope_str = " ".join(SCOPES) + auth_url = ( + f"https://accounts.google.com/o/oauth2/v2/auth?" + f"client_id={CLIENT_ID}" + f"&redirect_uri={urllib.parse.quote(redirect_uri)}" + f"&response_type=code" + f"&scope={urllib.parse.quote(scope_str)}" + f"&access_type=offline" + f"&prompt=select_account%20consent" + f"&state={state}" + f"&code_challenge={challenge}" + f"&code_challenge_method=S256" + ) + + oauth_dlg = Gtk.Dialog(title=f"Re-OAuth: {'Antigravity' if is_antigravity else 'Gemini CLI'}", parent=parent_dlg or self, modal=True) + oauth_dlg.add_button("Cancel", Gtk.ResponseType.CANCEL) + oauth_dlg.set_default_size(520, 200) + ca = oauth_dlg.get_content_area() ca.set_margin_start(12) ca.set_margin_end(12) ca.set_spacing(6) - ca.pack_start(Gtk.Label(label="Browser opened for Google OAuth.\nPaste the authorization code below:", xalign=0), False, False, 0) - code_entry = Gtk.Entry() - code_entry.set_placeholder_text("4/0AX...") - ca.pack_start(code_entry, False, False, 4) + ca.pack_start(Gtk.Label(label=f"Re-authenticating {'Antigravity' if is_antigravity else 'Gemini CLI'}", use_markup=True, xalign=0), False, False, 0) + link_lbl = Gtk.Label(label="Click here to open Google authorization", use_markup=True, xalign=0) + link_lbl.set_markup(f'Click here to open Google authorization') + ca.pack_start(link_lbl, False, False, 4) + status_lbl = Gtk.Label(label="Waiting for browser callback...", xalign=0) + ca.pack_start(status_lbl, False, False, 4) ca.show_all() - if code_dlg.run() == Gtk.ResponseType.OK: - code = code_entry.get_text().strip() - if code: + + code_holder = [None] + error_holder = [None] + + class OAuthHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self2): + qs = urllib.parse.urlparse(self2.path).query + params = urllib.parse.parse_qs(qs) + if "code" in params: + if params.get("state", [None])[0] != state: + self2.send_response(400) + self2.end_headers() + self2.wfile.write(b"CSRF state mismatch") + error_holder[0] = "CSRF state mismatch" + return + code_holder[0] = params["code"][0] + self2.send_response(302) + self2.send_header("Location", "https://developers.google.com/gemini-code-assist/auth_success_gemini") + self2.end_headers() + else: + error_holder[0] = params.get("error", ["unknown"])[0] + self2.send_response(302) + self2.send_header("Location", "https://developers.google.com/gemini-code-assist/auth_failure_gemini") + self2.end_headers() + def log_message(self2, fmt, *args): + pass + + try: + bind_host = "localhost" if is_antigravity else "127.0.0.1" + server = http.server.HTTPServer((bind_host, port), OAuthHandler) + except OSError: + status_lbl.set_text(f"Port {port} in use — close other apps and retry.") + oauth_dlg.run() + oauth_dlg.destroy() + return + + def _wait(): + deadline = time.time() + 120 + while code_holder[0] is None and error_holder[0] is None and time.time() < deadline: + server.handle_request() + server.server_close() + if code_holder[0]: try: - tok_req = urllib.request.Request("https://oauth2.googleapis.com/token", - data=urllib.parse.urlencode({ - "code": code, "client_id": client_id, "client_secret": client_secret, - "redirect_uri": redirect, "grant_type": "authorization_code" - }).encode(), + tok_data = urllib.parse.urlencode({ + "code": code_holder[0], "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, + "redirect_uri": redirect_uri, "grant_type": "authorization_code", + "code_verifier": verifier, + }).encode() + req = urllib.request.Request("https://oauth2.googleapis.com/token", data=tok_data, headers={"Content-Type": "application/x-www-form-urlencoded"}) - tok_resp = urllib.request.urlopen(tok_req, timeout=30) - tok_data = json.loads(tok_resp.read()) - tok_data["_updated"] = time.time() + resp = urllib.request.urlopen(req, timeout=30) + tokens = json.loads(resp.read()) + tokens["client_id"] = CLIENT_ID + tokens["client_secret"] = CLIENT_SECRET + tokens["provider_kind"] = provider_kind + tokens["expires_at"] = time.time() + tokens.get("expires_in", 3600) os.makedirs(os.path.dirname(token_path), exist_ok=True) with open(token_path, "w") as f: - json.dump(tok_data, f, indent=2) - self._log(f"[oauth] Refreshed {provider} token → {token_path}") + json.dump(tokens, f, indent=2) + os.chmod(token_path, 0o600) + project_id = _oauth_discover_project(tokens["access_token"], token_path, tokens) + def _on_success(): + status_lbl.set_text(f"Authorization successful! Project: {project_id or 'none'}") + GLib.timeout_add_seconds(2, lambda: oauth_dlg.destroy()) + return False + GLib.idle_add(_on_success) except Exception as e: - self._show_error_dialog("Token exchange failed", str(e)[:300]) - code_dlg.destroy() + def _on_err(exc=str(e)): + status_lbl.set_text(f"Token exchange failed: {exc[:200]}") + return False + GLib.idle_add(_on_err) + else: + def _on_fail(err=error_holder[0]): + status_lbl.set_text(f"Failed: {err or 'No code received'}") + return False + GLib.idle_add(_on_fail) + + webbrowser.open(auth_url) + threading.Thread(target=_wait, daemon=True).start() + oauth_dlg.run() + oauth_dlg.destroy() def _codebuff_reoauth(self): self._codebuff_oauth_standalone() @@ -3019,7 +3190,7 @@ class LauncherWin(Gtk.Window): hdr_row.pack_start(Gtk.Label(label=f"\n{section_label}", use_markup=True, xalign=0), True, True, 0) reauth_btn = Gtk.Button(label="Re-OAuth") reauth_btn.set_size_request(80, -1) - reauth_btn.connect("clicked", lambda b, p=oauth_prov: self._google_reoauth(p)) + reauth_btn.connect("clicked", lambda b, p=oauth_prov: self._google_reoauth(p, dlg)) hdr_row.pack_end(reauth_btn, False, False, 0) import_btn = Gtk.Button(label="Import JSON") import_btn.set_size_request(100, -1) @@ -3868,32 +4039,8 @@ class EditEndpointDialog(Gtk.Dialog): json.dump(tokens, f, indent=2) os.chmod(token_path, 0o600) _oauth_log(f"Token saved to {token_path}") - project_id = "" - try: - _oauth_log("Discovering project ID via loadCodeAssist...") - lr = urllib.request.Request( - "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", - data=json.dumps({}).encode(), - headers={ - "Content-Type": "application/json", - "Authorization": f"Bearer {tokens['access_token']}", - "User-Agent": "google-api-nodejs-client/9.15.1", - }) - lresp = urllib.request.urlopen(lr, timeout=15) - ldata = json.loads(lresp.read()) - p = ldata.get("cloudaicompanionProject", "") - if isinstance(p, dict): - project_id = p.get("id", "") - elif isinstance(p, str): - project_id = p - _oauth_log(f"Project ID: {project_id or '(none)'}") - if project_id: - tokens["project_id"] = project_id - with open(token_path, "w") as f2: - json.dump(tokens, f2, indent=2) - os.chmod(token_path, 0o600) - except Exception as pe: - _oauth_log(f"loadCodeAssist failed (non-fatal): {pe}") + project_id = _oauth_discover_project(tokens["access_token"], token_path, tokens) + _oauth_log(f"Project ID: {project_id or '(none)'}") if is_antigravity: found_models = [ "gemini-2.5-flash", "gemini-2.5-pro", @@ -3915,7 +4062,7 @@ class EditEndpointDialog(Gtk.Dialog): for mc in probe_candidates: try: pr = urllib.request.Request( - "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent", + "https://cloudcode-pa.googleapis.com/v1internal:generateContent", data=json.dumps({ "project": project_id, "model": mc, @@ -4264,10 +4411,54 @@ class EditEndpointDialog(Gtk.Dialog): data["default"] = name save_endpoints(data) + self._hot_reload_proxy_key(new_ep) self._parent_mgr._rebuild() self._parent_mgr._parent._on_endpoints_updated() self.destroy() + def _hot_reload_proxy_key(self, ep): + try: + ep_name = ep.get("name", "") + proxy_port = None + import glob as _glob + for cfg_file in _glob.glob(str(PROXY_CONFIG_DIR / "proxy-*.json")): + try: + with open(cfg_file) as f: + pcfg = json.load(f) + if ep_name.lower().replace(" ", "-") in cfg_file.lower(): + proxy_port = pcfg.get("port") + pcfg["api_key"] = ep.get("api_key", "") + with open(cfg_file, "w") as f: + json.dump(pcfg, f, indent=2) + break + except Exception: + continue + if proxy_port: + import urllib.request as _ur + try: + url = f"http://127.0.0.1:{proxy_port}/admin/reload" + resp = _ur.urlopen(url, timeout=3) + result = json.loads(resp.read()) + reloaded = result.get("reloaded", False) + preview = result.get("api_key_preview", "?") + self._parent_mgr._parent.log( + f"[hot-reload] key {'updated' if reloaded else 'unchanged'}: {preview}") + if reloaded: + verify_url = f"http://127.0.0.1:{proxy_port}/admin/verify-key" + vresp = _ur.urlopen(verify_url, timeout=10) + vresult = json.loads(vresp.read()) + valid = vresult.get("valid", False) + if valid: + self._parent_mgr._parent.log( + f"[hot-reload] key verified OK ({vresult.get('models', '?')} models)") + else: + self._parent_mgr._parent.log( + f"[hot-reload] WARNING: key verification failed: {vresult.get('error', 'unknown')}") + except Exception: + pass + except Exception: + pass + def _show_error(self, msg): d = Gtk.MessageDialog(self, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, msg) d.run(); d.destroy() diff --git a/src/codex_launcher_lib.py b/src/codex_launcher_lib.py index 2622e69..28e6e92 100644 --- a/src/codex_launcher_lib.py +++ b/src/codex_launcher_lib.py @@ -83,6 +83,19 @@ model_catalog_json = "" """ CHANGELOG = [ + ("3.11.0", "2026-05-26", [ + "Merge cobra PR: concurrency semaphore (max 3), auto-continue for truncated text", + "SO_REUSEADDR on sticky port, proxy-stderr.log, stream diagnostics logging", + "Timeout/OSError handler sends response.failed SSE instead of silent drop", + "Restart Proxy button: only restarts proxy without killing Codex Desktop", + "Tool call argument normalizer: fixes Arguments→arguments, strips markdown wrapping", + "Smart-continue loop (2× retries): escalating nudges when model stops text-only mid-task", + "XML tool call extraction: parses patterns from text, injects as real calls", + "Auto-continue + smart-continue ordered with skip guard to avoid double-firing", + "API key hot-reload with mtime tracking + /admin/reload + /admin/verify-key endpoints", + "GUI hot-reload: auto-refreshes proxy key on endpoint edit, verifies with upstream", + "Synthetic tool-results disabled: was causing deepseek-v4-pro truncation on opencode.ai", + ]), ("3.10.12", "2026-05-26", [ "Sticky endpoint: caches last working endpoint, sequential fallback on failure", "Endpoint order: cloudcode-pa first (matches agy CLI), daily-cloudcode-pa fallback", @@ -1468,6 +1481,7 @@ def _pick_free_port(): try: saved = int(_PROXY_PORT_FILE.read_text().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)) return saved except (ValueError, OSError, FileNotFoundError): @@ -1559,11 +1573,19 @@ def _start_proxy_with_config(pcfg_path, port, logfn): ) _register_pgid_entry("proxy", _proxy_proc.pid) + _proxy_log_path = PROXY_CONFIG_DIR / "proxy-stderr.log" + _proxy_log_file = open(_proxy_log_path, "a", encoding="utf-8") + def _pipe_stderr(): if not _proxy_proc.stderr: return for line in _proxy_proc.stderr: logfn(f"[proxy] {line.rstrip()}") + try: + _proxy_log_file.write(line) + _proxy_log_file.flush() + except Exception: + pass threading.Thread(target=_pipe_stderr, daemon=True).start() diff --git a/src/translate-proxy.py b/src/translate-proxy.py index 4cf20a9..6d7b966 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -323,6 +323,8 @@ _conn_pool_lock = threading.Lock() _conn_pool = {} _STREAM_IDLE_TIMEOUT = 300 +_MAX_CONCURRENT_REQUESTS = 3 +_request_semaphore = threading.Semaphore(_MAX_CONCURRENT_REQUESTS) _CODEBUFF_AUTH_URL = "https://www.codebuff.com" _CODEBUFF_API_URL = "https://www.codebuff.com" @@ -4829,6 +4831,11 @@ class Handler(http.server.BaseHTTPRequestHandler): _last_user_urls.append(url_m.group(0)) save_request_snapshot(request_id, body) _req_t0 = time.time() + wait_start = time.monotonic() + _request_semaphore.acquire() + wait_ms = (time.monotonic() - wait_start) * 1000 + if wait_ms > 100: + print(f"[{_sid}] waited {wait_ms:.0f}ms for upstream slot (concurrency gate)", file=sys.stderr) try: with RequestTracker(request_id) as tracker: if BACKEND == "auto": @@ -4847,6 +4854,8 @@ class Handler(http.server.BaseHTTPRequestHandler): except Exception as _snap_err: update_snapshot_response(request_id, "error", time.time() - _req_t0, _snap_err) raise + finally: + _request_semaphore.release() def _handle_openai_compat(self, body, model, stream, tracker=None): input_data = body.get("input", "") @@ -4859,7 +4868,8 @@ class Handler(http.server.BaseHTTPRequestHandler): body = dict(body) body["input"] = input_data - if (policy.get("synthetic_tool_results") or _provider_cap(model, "synthetic_tool_results", False)) and isinstance(input_data, list): + # synthetic tool-results disabled: causes deepseek-v4-pro truncation on opencode.ai + if False and (policy.get("synthetic_tool_results") or _provider_cap(model, "synthetic_tool_results", False)) and isinstance(input_data, list): input_data, synthesized = synthesize_tool_results_for_chat(input_data) if synthesized: print("[provider-adapter] using synthetic tool-result continuation", file=sys.stderr) @@ -5739,11 +5749,25 @@ class Handler(http.server.BaseHTTPRequestHandler): break collected_events.append(event) _observe_event(event) + print(f"[{self._session_id}] stream ended: events={len(collected_events)} finish={finish_reason} has_content={has_content} elapsed={time.time()-t0:.1f}s", file=sys.stderr) except (ConnectionResetError, BrokenPipeError, ConnectionAbortedError): print("[translate-proxy] client disconnected during stream", file=sys.stderr) _crof_record(model, n_items, False) _log_resp(last_resp_id, "client_disconnect", last_output) return + except (TimeoutError, OSError, urllib.error.URLError) as e: + print(f"[translate-proxy] upstream error during stream: {type(e).__name__}: {e}", file=sys.stderr) + err_resp_id = body.get("request_id") or body.get("id") or uid("resp") + try: + self.wfile.write(emit("response.failed", {"type": "response.failed", + "response": {"id": err_resp_id, "error": {"type": "upstream_error", + "code": "stream_interrupted", "message": str(e)[:200]}}}).encode()) + self.wfile.flush() + except Exception: + pass + _crof_record(model, n_items, False) + _log_resp(last_resp_id, "upstream_error", last_output) + return # Record outcome success = (finish_reason != "length") @@ -5819,43 +5843,160 @@ class Handler(http.server.BaseHTTPRequestHandler): except Exception as e: print(f"[crof-adaptive] retry failed: {e}", file=sys.stderr) + # ── Auto-continue for truncated responses ── (cobra PR) + _ac_did_run = False + if stream and collected_events: + _ac_text = "" + _ac_msg_id = _ac_resp_id = None + for _ev in collected_events: + for _ln in _ev.strip().split("\n"): + if not _ln.startswith("data: "): + continue + try: + _d = json.loads(_ln[6:]) + _t = _d.get("type") + if _t == "response.output_text.done": + _ac_text = _d.get("text", "") + elif _t == "response.output_item.added" and _d.get("item",{}).get("type") == "message": + _ac_msg_id = _d.get("item",{}).get("id") + elif _t == "response.completed": + _ac_resp_id = _d.get("response",{}).get("id") + except Exception: + pass + + _ac_tc = reasoning_out.get("tool_calls", []) + _ac_truncated = False + if not _ac_tc and _ac_text: + _ac_stripped = _ac_text.rstrip() + if finish_reason == "length": + _ac_truncated = True + elif len(_ac_stripped) > 10 and _ac_stripped[-1] in "(:,;…": + _ac_truncated = True + + if _ac_truncated and _ac_text: + print(f"[{self._session_id}] auto-continue: truncated (finish={finish_reason}, ends '{_ac_text.rstrip()[-10:]}')", file=sys.stderr) + _ac_did_run = True + _ac_cut = len(collected_events) + for _i, _ev2 in enumerate(collected_events): + if "response.output_text.done" in _ev2: + _ac_cut = _i + break + collected_events = collected_events[:_ac_cut] + + _ac_accumulated = _ac_text + _ac_max = 3 + for _ac_attempt in range(_ac_max): + try: + _ac_cont_msgs = list(chat_body.get("messages", [])) + _ac_cont_msgs.append({"role": "assistant", "content": _ac_accumulated}) + _ac_cont_msgs.append({"role": "user", "content": "Continue exactly where you left off. Do not repeat anything already written."}) + _ac_cont_body = dict(chat_body) + _ac_cont_body["messages"] = _ac_cont_msgs + _ac_cont_body["stream"] = False + _ac_cont_req = urllib.request.Request(target, data=json.dumps(_ac_cont_body).encode(), headers=fwd) + _ac_cont_resp = json.loads(urllib.request.urlopen(_ac_cont_req, timeout=120).read()) + _ac_choices = _ac_cont_resp.get("choices", []) + if _ac_choices: + _ac_chunk = _ac_choices[0].get("message",{}).get("content","") + if not _ac_chunk: + _ac_chunk = _ac_choices[0].get("delta",{}).get("content","") + _ac_finish = _ac_choices[0].get("finish_reason") + if _ac_chunk: + _ac_accumulated += _ac_chunk + collected_events.append(emit("response.output_text.delta", { + "type": "response.output_text.delta", + "delta": _ac_chunk, "item_id": _ac_msg_id, "content_index": 0})) + if _ac_finish != "length": + break + _ac_text = _ac_accumulated + except Exception as _ac_e: + print(f"[{self._session_id}] auto-continue attempt {_ac_attempt+1} failed: {_ac_e}", file=sys.stderr) + break + + if _ac_msg_id: + collected_events.append(emit("response.output_text.done", { + "type": "response.output_text.done", + "text": _ac_accumulated, "item_id": _ac_msg_id, "content_index": 0})) + collected_events.append(emit("response.content_part.done", { + "type": "response.content_part.done", + "part": {"type": "output_text", "text": _ac_accumulated, "annotations": []}, "item_id": _ac_msg_id})) + collected_events.append(emit("response.output_item.done", { + "type": "response.output_item.done", + "item": {"type": "message", "id": _ac_msg_id, "role": "assistant", "status": "completed", + "content": [{"type": "output_text", "text": _ac_accumulated, "annotations": []}]}})) + if _ac_resp_id: + collected_events.append(emit("response.completed", { + "type": "response.completed", + "response": {"id": _ac_resp_id, "object": "response", "model": model, + "status": "completed", "created": int(time.time()), + "output": [{"type": "message", "id": _ac_msg_id, "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": _ac_accumulated, "annotations": []}]}]}})) + has_content = True + finish_reason = "stop" + print(f"[{self._session_id}] auto-continue done: {len(_ac_text)} -> {len(_ac_accumulated)} chars", file=sys.stderr) + # Smart continuation: loop with escalating nudges when model stops text-only mid-task. - _smart_max = 2 - _smart_attempt = 0 - while _smart_attempt < _smart_max: - _has_tool_calls_in_output = any(o.get("type") == "function_call" for o in (last_output or [])) - if not (finish_reason == "stop" and has_content and not _has_tool_calls_in_output - and isinstance(input_data, list) and len(input_data) >= 3 - and has_function_call_output(input_data)): - break - _smart_attempt += 1 - _nudges = [ - "Continue with the task using tool calls. Do NOT describe what to do — call the appropriate functions.", - "You MUST use tool calls to complete the task. Read files, run commands, and make changes using tools. Do NOT output XML tool calls as text.", - ] - nudge_text = _nudges[min(_smart_attempt - 1, len(_nudges) - 1)] - # Try extracting XML tool calls from text as fallback before nudging - last_text = "" - for o in (last_output or []): - if o.get("type") == "message": - for c in (o.get("content") or []): - if isinstance(c, dict) and c.get("type") == "output_text": - last_text += c.get("text", "") - xml_fc = _extract_xml_tool_calls(last_text) - if xml_fc: - print(f"[{self._session_id}] [smart-continue] extracted {len(xml_fc)} XML tool calls from text, injecting and retrying", file=sys.stderr) - fake_input = list(input_data) - for xfc in xml_fc: - fake_input.append({"type": "function_call", "id": uid("fcx"), "call_id": uid("fcx"), - "name": xfc["name"], "arguments": xfc["args"], "status": "completed"}) - fake_messages = oa_input_to_messages(fake_input) + # Skip if auto-continue already handled the response. + if not _ac_did_run: + _smart_max = 2 + _smart_attempt = 0 + while _smart_attempt < _smart_max: + _has_tool_calls_in_output = any(o.get("type") == "function_call" for o in (last_output or [])) + if not (finish_reason == "stop" and has_content and not _has_tool_calls_in_output + and isinstance(input_data, list) and len(input_data) >= 3 + and has_function_call_output(input_data)): + break + _smart_attempt += 1 + _nudges = [ + "Continue with the task using tool calls. Do NOT describe what to do — call the appropriate functions.", + "You MUST use tool calls to complete the task. Read files, run commands, and make changes using tools. Do NOT output XML tool calls as text.", + ] + nudge_text = _nudges[min(_smart_attempt - 1, len(_nudges) - 1)] + # Try extracting XML tool calls from text as fallback before nudging + last_text = "" + for o in (last_output or []): + if o.get("type") == "message": + for c in (o.get("content") or []): + if isinstance(c, dict) and c.get("type") == "output_text": + last_text += c.get("text", "") + xml_fc = _extract_xml_tool_calls(last_text) + if xml_fc: + print(f"[{self._session_id}] [smart-continue] extracted {len(xml_fc)} XML tool calls from text, injecting and retrying", file=sys.stderr) + fake_input = list(input_data) + for xfc in xml_fc: + fake_input.append({"type": "function_call", "id": uid("fcx"), "call_id": uid("fcx"), + "name": xfc["name"], "arguments": xfc["args"], "status": "completed"}) + fake_messages = oa_input_to_messages(fake_input) + instructions = body.get("instructions", "").strip() + if instructions: + fake_messages.insert(0, {"role": "system", "content": instructions}) + fake_chat_body = self._build_chat_body(model, fake_messages, body, stream) + fake_req = urllib.request.Request(target, data=json.dumps(fake_chat_body).encode(), headers=fwd) + try: + retry_upstream = urllib.request.urlopen(fake_req, timeout=_upstream_timeout(body, True)) + collected_events = [] + last_resp_id = last_output = last_status = None + finish_reason = None + has_content = False + for event in oa_stream_to_sse(retry_upstream, model, body.get("request_id") or body.get("id")): + collected_events.append(event) + _observe_event(event) + input_data = fake_input + continue + except Exception as e: + print(f"[{self._session_id}] [smart-continue] XML injection retry failed: {e}", file=sys.stderr) + break + _nudge_msg = {"role": "user", "content": nudge_text} + nudge_messages = oa_input_to_messages(input_data) + [_nudge_msg] instructions = body.get("instructions", "").strip() if instructions: - fake_messages.insert(0, {"role": "system", "content": instructions}) - fake_chat_body = self._build_chat_body(model, fake_messages, body, stream) - fake_req = urllib.request.Request(target, data=json.dumps(fake_chat_body).encode(), headers=fwd) + nudge_messages.insert(0, {"role": "system", "content": instructions}) + nudge_chat_body = self._build_chat_body(model, nudge_messages, body, stream) + nudge_req = urllib.request.Request(target, data=json.dumps(nudge_chat_body).encode(), headers=fwd) + print(f"[{self._session_id}] [smart-continue] attempt {_smart_attempt}/{_smart_max}: model stopped mid-task, nudging", file=sys.stderr) try: - retry_upstream = urllib.request.urlopen(fake_req, timeout=_upstream_timeout(body, True)) + retry_upstream = urllib.request.urlopen(nudge_req, timeout=_upstream_timeout(body, True)) collected_events = [] last_resp_id = last_output = last_status = None finish_reason = None @@ -5863,31 +6004,9 @@ class Handler(http.server.BaseHTTPRequestHandler): for event in oa_stream_to_sse(retry_upstream, model, body.get("request_id") or body.get("id")): collected_events.append(event) _observe_event(event) - input_data = fake_input - continue except Exception as e: - print(f"[{self._session_id}] [smart-continue] XML injection retry failed: {e}", file=sys.stderr) + print(f"[{self._session_id}] [smart-continue] nudge attempt {_smart_attempt} failed: {e}", file=sys.stderr) break - _nudge_msg = {"role": "user", "content": nudge_text} - nudge_messages = oa_input_to_messages(input_data) + [_nudge_msg] - instructions = body.get("instructions", "").strip() - if instructions: - nudge_messages.insert(0, {"role": "system", "content": instructions}) - nudge_chat_body = self._build_chat_body(model, nudge_messages, body, stream) - nudge_req = urllib.request.Request(target, data=json.dumps(nudge_chat_body).encode(), headers=fwd) - print(f"[{self._session_id}] [smart-continue] attempt {_smart_attempt}/{_smart_max}: model stopped mid-task, nudging", file=sys.stderr) - try: - retry_upstream = urllib.request.urlopen(nudge_req, timeout=_upstream_timeout(body, True)) - collected_events = [] - last_resp_id = last_output = last_status = None - finish_reason = None - has_content = False - for event in oa_stream_to_sse(retry_upstream, model, body.get("request_id") or body.get("id")): - collected_events.append(event) - _observe_event(event) - except Exception as e: - print(f"[{self._session_id}] [smart-continue] nudge attempt {_smart_attempt} failed: {e}", file=sys.stderr) - break self.stream_buffered_events(collected_events) else: diff --git a/translate-proxy.py b/translate-proxy.py index 19d6c44..6d7b966 100755 --- a/translate-proxy.py +++ b/translate-proxy.py @@ -188,6 +188,7 @@ DEFAULT_MODELS = { } def load_config(): + global _CONFIG_PATH, _CONFIG_MTIME p = argparse.ArgumentParser(description="Responses API translation proxy") p.add_argument("--config", help="JSON config file path") p.add_argument("--port", type=int, default=None) @@ -195,16 +196,21 @@ def load_config(): p.add_argument("--target-url", default=None) p.add_argument("--api-key", default=None) p.add_argument("--models-file", default=None, help="JSON file with model list array") - args = p.parse_args() + _args = p.parse_args() cfg = {} - if args.config: - with open(args.config) as f: + if _args.config: + _CONFIG_PATH = os.path.abspath(_args.config) + with open(_args.config) as f: cfg = json.load(f) + try: + _CONFIG_MTIME = os.path.getmtime(_CONFIG_PATH) + except OSError: + pass for ck, ak in [("port", "port"), ("backend_type", "backend"), ("target_url", "target_url"), ("api_key", "api_key")]: - v = getattr(args, ak, None) + v = getattr(_args, ak, None) if v is not None: cfg[ck] = v @@ -226,8 +232,8 @@ def load_config(): cfg.setdefault("api_key", "") models = cfg.get("models", []) - if not models and args.models_file: - with open(args.models_file) as f: + if not models and _args.models_file: + with open(_args.models_file) as f: models = json.load(f) if not models: models = DEFAULT_MODELS.get(cfg["backend_type"], []) @@ -236,6 +242,8 @@ def load_config(): return cfg CONFIG = None +_CONFIG_PATH = None +_CONFIG_MTIME = 0 PORT = 8080 BACKEND = "openai-compat" TARGET_URL = "" @@ -315,6 +323,8 @@ _conn_pool_lock = threading.Lock() _conn_pool = {} _STREAM_IDLE_TIMEOUT = 300 +_MAX_CONCURRENT_REQUESTS = 3 +_request_semaphore = threading.Semaphore(_MAX_CONCURRENT_REQUESTS) _CODEBUFF_AUTH_URL = "https://www.codebuff.com" _CODEBUFF_API_URL = "https://www.codebuff.com" @@ -616,6 +626,51 @@ class APIKeyPool(AccountPool): _cb_pool = CodebuffAccountPool("codebuff") _google_antigravity_pool = GoogleAccountPool("antigravity") _google_cli_pool = GoogleAccountPool("cli") +_antigravity_preferred_endpoint = None +_antigravity_endpoint_lock = threading.Lock() + +def _classify_antigravity_error(status_code, body): + lower = body.lower() + if status_code == 400: + return "bad_request" + if status_code == 401: + if any(x in lower for x in ["invalid_grant", "token revoked", "token_revoked", "invalid_client"]): + return "auth_permanent" + return "auth_transient" + if status_code == 403: + if "validation_required" in lower or "account_disabled" in lower: + return "validation_required" + if "has been disabled" in lower and "violation of terms of service" in lower: + return "account_banned" + if "service_disabled" in lower: + return "service_disabled" + return "forbidden" + if status_code in (429, 503, 529): + if any(x in lower for x in ["model_capacity_exhausted", "capacity_exhausted", "model is currently overloaded", "service temporarily unavailable"]): + return "capacity_exhausted" + if any(x in lower for x in ["quota_exhausted", "resource_exhausted", "daily limit", "quota exceeded", "quotaresetdelay"]): + return "quota_exhausted" + return "rate_limited" + if status_code >= 500: + return "server_error" + return "unknown" + +def _parse_rate_limit_reset(body): + import re as _re + m = _re.search(r'quotaResetDelay[:"\s]+(\d+(?:\.\d+)?)(ms|s)', body, _re.IGNORECASE) + if m: + val = float(m.group(1)) + return val / 1000 if m.group(2) == 'ms' else val + m = _re.search(r'(\d+)h(\d+)m(\d+)s', body, _re.IGNORECASE) + if m: + return int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3)) + m = _re.search(r'Resets in ~(\d+)h(\d+)m', body, _re.IGNORECASE) + if m: + return int(m.group(1)) * 3600 + int(m.group(2)) * 60 + m = _re.search(r'retry[-_]?after[:\s]+(\d+)\s*(?:sec|s\b)', body, _re.IGNORECASE) + if m: + return int(m.group(1)) + return None def _get_codebuff_account(): """Return (token, account_dict) for best available codebuff account.""" @@ -714,14 +769,21 @@ def _gemini_reattach_sigs(contents): # Gemini follow-through guardrail _GEMINI_AGENT_GUARDRAIL = ( - "You are running inside Codex as an autonomous coding agent. " - "When the user asks for a change to existing files, do not merely describe the previous work or summarize. " - "You must inspect the existing files, apply edits with tools, and verify the result. " - "If a file path is known from prior context, reuse it. " - "If unsure, list files first. " - "After tool results, continue until the requested change is actually implemented. " - "Never answer only with a plan such as 'I will start by...' or 'I am going to...'. " - "Always emit the actual tool call in the same response." + "!!! ABSOLUTELY CRITICAL - DO NOT IGNORE THIS UNDER ANY CIRCUMSTANCES !!! " + "YOU ARE RUNNING INSIDE CODEX AS AN AUTONOMOUS CODING AGENT. " + "!!!!!! NEVER EVER CONTINUE, PARAPHRASE, COMPLETE, OR ADD ANYTHING TO THE USER'S INSTRUCTIONS !!!!!! " + "!!!!!! NEVER SAY 'LET\\'S FIRST VIEW' OR 'LET\\'S FIRST FIND' OR SIMILAR PHRASES - EMIT THE ACTUAL TOOL CALL NOW !!!!!! " + "WHEN THE USER ASKS FOR A CHANGE TO EXISTING FILES, YOU MUST " + "1. IMMEDIATELY INSPECT EXISTING FILES USING exec_command OR read_files TOOLS RIGHT NOW, " + "2. THEN APPLY EDITS USING write OR exec_command TOOLS, " + "3. THEN VERIFY THE RESULT. " + "IF A FILE PATH IS KNOWN, REUSE IT IMMEDIATELY. " + "IF UNSURE, LIST FILES FIRST USING exec_command (ls -la). " + "AFTER TOOL RESULTS, CONTINUE UNTIL THE REQUESTED CHANGE IS FULLY IMPLEMENTED AND FILES ARE MODIFIED. " + "NEVER ANSWER ONLY WITH A PLAN LIKE 'I WILL START BY...' OR 'I AM GOING TO...'. " + "NEVER SUMMARIZE THE USER'S REQUEST. NEVER CONTINUE THEIR SENTENCE. " + "ALWAYS, ALWAYS, ALWAYS EMIT THE ACTUAL TOOL CALL IN THE SAME RESPONSE. " + "!!! FAILURE TO FOLLOW THESE INSTRUCTIONS WILL RESULT IN A BROKEN USER EXPERIENCE !!!" ) _LOG_FILE_LOCK = threading.Lock() @@ -771,6 +833,20 @@ def _ensure_antigravity_version(): _antigravity_version_checked = time.time() return _antigravity_version +_antigravity_client_version = "1.110.0" +_antigravity_client_version_checked = 0 + +def _ensure_antigravity_client_version(): + global _antigravity_client_version, _antigravity_client_version_checked + env_ver = os.environ.get("ANTIGRAVITY_CLIENT_VERSION", "").strip() + if env_ver: + return env_ver + if time.time() - _antigravity_client_version_checked < 6 * 3600: + return _antigravity_client_version + _antigravity_client_version = os.environ.get("ANTIGRAVITY_CLIENT_VERSION_FALLBACK", "1.110.0") + _antigravity_client_version_checked = time.time() + return _antigravity_client_version + def _init_runtime(): global CONFIG, PORT, BACKEND, TARGET_URL, API_KEY, OAUTH_PROVIDER, _antigravity_version global MODELS, CC_VERSION, REASONING_ENABLED, REASONING_EFFORT, BGP_ROUTES @@ -801,6 +877,68 @@ def _init_runtime(): _antigravity_version = _ensure_antigravity_version() print(f"[antigravity] version={_antigravity_version}", file=sys.stderr) +def _verify_api_key(key, target_url): + if not key or not target_url: + return {"valid": False, "error": "missing key or url"} + test_url = upstream_target(target_url, "/models") + if not test_url: + return {"valid": False, "error": "invalid target url"} + try: + req = urllib.request.Request(test_url, headers={ + "Authorization": f"Bearer {key}", + "Content-Type": "application/json", + }) + resp = urllib.request.urlopen(req, timeout=10) + body = resp.read().decode() + model_count = 0 + try: + data = json.loads(body) + model_count = len(data.get("data", [])) + except Exception: + pass + return {"valid": True, "status": resp.status, "models": model_count} + except urllib.error.HTTPError as e: + err = e.read().decode()[:200] + return {"valid": False, "status": e.code, "error": err} + except Exception as e: + return {"valid": False, "error": str(e)[:200]} + +_HOT_RELOAD_LOCK = threading.Lock() + +def _hot_reload_api_key(): + global API_KEY, _api_key_pool, _CONFIG_MTIME + if not _CONFIG_PATH: + return False + try: + cur_mtime = os.path.getmtime(_CONFIG_PATH) + except OSError: + return False + if cur_mtime <= _CONFIG_MTIME: + return False + with _HOT_RELOAD_LOCK: + try: + cur_mtime2 = os.path.getmtime(_CONFIG_PATH) + if cur_mtime2 <= _CONFIG_MTIME: + return False + with open(_CONFIG_PATH) as f: + new_cfg = json.load(f) + new_key = (new_cfg.get("api_key") or "").strip() + if not new_key or new_key == API_KEY: + _CONFIG_MTIME = cur_mtime2 + return False + old_preview = API_KEY[:8] + "..." if len(API_KEY) > 8 else "(empty)" + new_preview = new_key[:8] + "..." if len(new_key) > 8 else "(empty)" + API_KEY = new_key + _CONFIG_MTIME = cur_mtime2 + 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) + print(f"[hot-reload] API key pool refreshed: {len(_api_key_pool._accounts)} keys", file=sys.stderr) + print(f"[hot-reload] API key updated: {old_preview} -> {new_preview}", file=sys.stderr) + return True + except Exception as e: + print(f"[hot-reload] error: {e}", file=sys.stderr) + return False + bgp_models = [] for _r in BGP_ROUTES: for _m in _r.get("models", [{"id": _r.get("model", "unknown")}]): @@ -1623,7 +1761,8 @@ _PROVIDER_POLICIES = { "openrouter": {"reasoning_mode": "provider_default", "max_tokens": 32768, "strip_reasoning": True, "tool_output_limit": 6000, "max_input_items": 35, "compaction": "balanced"}, "openadapter": {"reasoning_mode": "off", "max_tokens": 32768, "strip_reasoning": True, - "tool_output_limit": 6000, "max_input_items": 30, "compaction": "balanced"}, + "tool_output_limit": 1000, "max_input_items": 10, "compaction": "aggressive", + "synthetic_tool_results": True}, "cloudcode-pa": {"compaction": "aggressive", "context_size": 1000000, "tool_output_limit": 6000, "max_input_items": 60}, "googleapis": {"compaction": "balanced", "context_size": 1000000, @@ -2054,6 +2193,75 @@ def _inject_stored_reasoning(messages): msg["reasoning_content"] = reasoning return messages +def _normalize_tool_args(raw_args): + if not raw_args or raw_args == "{}": + return raw_args + try: + parsed = json.loads(raw_args) + if isinstance(parsed, dict): + if "Arguments" in parsed and "arguments" not in parsed: + inner = parsed["Arguments"] + if isinstance(inner, str): + inner = inner.strip() + for pfx in ("```json", "```"): + if inner.startswith(pfx): + inner = inner[len(pfx):].strip() + if inner.endswith("```"): + inner = inner[:-3].strip() + try: + inner_parsed = json.loads(inner) + if isinstance(inner_parsed, dict): + return json.dumps(inner_parsed) + except json.JSONDecodeError: + pass + if "cmd" not in parsed and "Arguments" in parsed: + inner = parsed["Arguments"] + if isinstance(inner, str): + inner = inner.strip() + for pfx in ("```json", "```"): + if inner.startswith(pfx): + inner = inner[len(pfx):].strip() + if inner.endswith("```"): + inner = inner[:-3].strip() + try: + inner_parsed = json.loads(inner) + if isinstance(inner_parsed, dict): + return json.dumps(inner_parsed) + except json.JSONDecodeError: + pass + return raw_args + except json.JSONDecodeError: + return raw_args + +_XML_TC_RE = re.compile(r'(\w+)(.*?)', re.DOTALL) +_XML_ARG_VALUE_RE = re.compile(r'\s*') + +def _extract_xml_tool_calls(text): + if not text: + return [] + results = [] + for m in _XML_TC_RE.finditer(text): + name = m.group(1) + rest = _XML_ARG_VALUE_RE.sub("", m.group(2)).strip() + args_str = "{}" + try: + for pfx in ("```json", "```"): + if rest.startswith(pfx): + rest = rest[len(pfx):].strip() + if rest.endswith("```"): + rest = rest[:-3].strip() + if rest.startswith("{"): + json.loads(rest) + args_str = rest + else: + json.loads(rest) + args_str = rest + except Exception: + if rest.startswith("{"): + args_str = rest + results.append({"name": name, "args": args_str, "call_id": f"xml_{len(results)}"}) + return results + def oa_input_to_messages(input_data): msgs = [] tool_name_by_id = {} @@ -2066,11 +2274,13 @@ def oa_input_to_messages(input_data): t = item.get("type") if t == "function_call": tcid = item.get("call_id") or item.get("id") or uid("tc") + raw_args = item.get("arguments", "{}") + normalized_args = _normalize_tool_args(raw_args) pending_tool_calls.append( {"id": tcid, "type": "function", "function": {"name": item.get("name", ""), - "arguments": item.get("arguments", "{}")}}) + "arguments": normalized_args}}) tool_name_by_id[tcid] = item.get("name", "") continue if pending_tool_calls: @@ -4280,9 +4490,10 @@ def _antigravity_is_simple_user(text): return True return False -def _antigravity_normalize_context(input_data): +def _antigravity_normalize_context(input_data, model=""): if not isinstance(input_data, list) or len(input_data) < 2: return input_data + is_claude_model = "claude" in model.lower() latest_user = "" latest_user_idx = -1 @@ -4310,13 +4521,19 @@ def _antigravity_normalize_context(input_data): if os.environ.get("ANTIGRAVITY_AUTO_RESET_POLLUTED_CONTEXT", "1") != "1": auto_reset = False - if is_simple and (auto_reset or n_tool_outputs == 0): + has_compaction_summary = any( + isinstance(it, dict) and it.get("type") == "message" and it.get("role") == "user" + and ("Auto-compacted" in str(it.get("content", "")) or "auto-compacted" in str(it.get("content", "")).lower()) + for it in input_data + ) + + if is_simple and auto_reset and not has_compaction_summary: system_items = [it for it in input_data if isinstance(it, dict) and it.get("type") == "message" and it.get("role") in ("developer", "system")] user_item = input_data[latest_user_idx] result = system_items + [user_item] if system_items else [user_item] print(f"[antigravity-context] raw_items={n_raw} compacted_items={n_raw} final_items={len(result)}", file=sys.stderr) print(f"[antigravity-context] raw_tool_outputs={n_tool_outputs} kept_tool_outputs=0", file=sys.stderr) - print(f"[antigravity-context] simple_latest_user=true auto_reset={auto_reset}", file=sys.stderr) + print(f"[antigravity-context] simple_latest_user=true auto_reset={auto_reset} has_compaction={has_compaction_summary}", file=sys.stderr) return result dev_messages = [] @@ -4340,9 +4557,15 @@ def _antigravity_normalize_context(input_data): latest_words = set(latest_user.strip().lower().split()) has_edit_intent = bool(latest_words.intersection(_ANTIGRAVITY_EDIT_WORDS)) has_ref_intent = bool(latest_words.intersection(_ANTIGRAVITY_REFERENCE_WORDS)) - keep_tools = 2 if (has_edit_intent or has_ref_intent) else 1 + if is_claude_model: + keep_tools = len(tool_outputs) + else: + keep_tools = 2 if (has_edit_intent or has_ref_intent) else 1 - kept_tools = tool_outputs[-keep_tools:] if tool_outputs and (has_edit_intent or has_ref_intent) else [] + if is_claude_model: + kept_tools = tool_outputs + else: + kept_tools = tool_outputs[-keep_tools:] if tool_outputs and (has_edit_intent or has_ref_intent) else [] for idx_t, t_item in enumerate(kept_tools): orig = t_item[1] @@ -4357,6 +4580,22 @@ def _antigravity_normalize_context(input_data): tail_start = max(0, len(recent_items) - 6) recent_tail = recent_items[tail_start:] + deduped_tail = [] + seen_goal_context = False + for idx, msg_item in recent_tail: + content_str = "" + c = msg_item.get("content", "") + if isinstance(c, str): + content_str = c + elif isinstance(c, list): + content_str = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict)) + if "" in content_str: + if seen_goal_context: + continue + seen_goal_context = True + deduped_tail.append((idx, msg_item)) + recent_tail = deduped_tail if deduped_tail else recent_tail + tool_call_ids = set() for _, t_item in kept_tools: cid = t_item.get("call_id", t_item.get("id", "")) @@ -4371,6 +4610,15 @@ def _antigravity_normalize_context(input_data): result = list(dev_messages) + compaction_summaries = [] + for idx, msg_item in recent_items: + if msg_item is input_data[latest_user_idx]: + continue + c = msg_item.get("content", "") + content_str = c if isinstance(c, str) else " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict)) if isinstance(c, list) else "" + if "Auto-compacted" in content_str or "auto-compacted" in content_str.lower(): + compaction_summaries.append(msg_item) + if n_summarized > 0: summary_text = f"[Tool history summary: {n_summarized} older tool outputs omitted. {n_tool_calls} prior function calls were made for file inspection/editing.]" result.append({"type": "message", "role": "user", "content": [{"type": "input_text", "text": summary_text}]}) @@ -4381,6 +4629,9 @@ def _antigravity_normalize_context(input_data): for _, tool_item in kept_tools: result.append(tool_item) + for cs_item in compaction_summaries: + result.append(cs_item) + for _, msg_item in recent_tail: if msg_item is not input_data[latest_user_idx]: result.append(msg_item) @@ -4408,7 +4659,10 @@ def _antigravity_normalize_context(input_data): if total_chars > _ANTIGRAVITY_EMERGENCY_CHARS: print(f"[antigravity-context] EMERGENCY: {total_chars} chars exceeds limit, resetting to minimal", file=sys.stderr) - result = list(dev_messages) + [input_data[latest_user_idx]] + result = list(dev_messages) + if compaction_summaries: + result.extend(compaction_summaries) + result.append(input_data[latest_user_idx]) total_chars = sum(len(json.dumps(it, ensure_ascii=False)) for it in result) while len(result) > _ANTIGRAVITY_MAX_CONTENTS and total_chars > _ANTIGRAVITY_SOFT_CHARS: @@ -4475,12 +4729,23 @@ class Handler(http.server.BaseHTTPRequestHandler): 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), - "uptime_s": round(_uptime, 1), - "memory_mb": round(_mem_mb, 1), - "requests_total": _STATS.get("requests", 0)}) + "target_url": TARGET_URL, + "models": [m.get("id") for m in MODELS], + "bgp_routes": len(BGP_ROUTES), + "uptime_s": round(_uptime, 1), + "memory_mb": round(_mem_mb, 1), + "requests_total": _STATS.get("requests", 0)}) + elif self.path == "/admin/reload": + reloaded = _hot_reload_api_key() + key_preview = API_KEY[:8] + "..." if len(API_KEY) > 8 else "(empty)" + self.send_json(200, {"ok": True, "reloaded": reloaded, + "api_key_preview": key_preview, + "config_path": _CONFIG_PATH or "none"}) + elif self.path == "/admin/verify-key": + result = _verify_api_key(API_KEY, TARGET_URL) + key_preview = API_KEY[:8] + "..." if len(API_KEY) > 8 else "(empty)" + result["api_key_preview"] = key_preview + self.send_json(200, result) else: self.send_error(404) @@ -4502,6 +4767,7 @@ class Handler(http.server.BaseHTTPRequestHandler): _logf = None def _handle(self): + _hot_reload_api_key() try: clen = int(self.headers.get("Content-Length", 0)) body = json.loads(self.rfile.read(clen)) @@ -4565,6 +4831,11 @@ class Handler(http.server.BaseHTTPRequestHandler): _last_user_urls.append(url_m.group(0)) save_request_snapshot(request_id, body) _req_t0 = time.time() + wait_start = time.monotonic() + _request_semaphore.acquire() + wait_ms = (time.monotonic() - wait_start) * 1000 + if wait_ms > 100: + print(f"[{_sid}] waited {wait_ms:.0f}ms for upstream slot (concurrency gate)", file=sys.stderr) try: with RequestTracker(request_id) as tracker: if BACKEND == "auto": @@ -4583,6 +4854,8 @@ class Handler(http.server.BaseHTTPRequestHandler): except Exception as _snap_err: update_snapshot_response(request_id, "error", time.time() - _req_t0, _snap_err) raise + finally: + _request_semaphore.release() def _handle_openai_compat(self, body, model, stream, tracker=None): input_data = body.get("input", "") @@ -4595,7 +4868,8 @@ class Handler(http.server.BaseHTTPRequestHandler): body = dict(body) body["input"] = input_data - if (policy.get("synthetic_tool_results") or _provider_cap(model, "synthetic_tool_results", False)) and isinstance(input_data, list): + # synthetic tool-results disabled: causes deepseek-v4-pro truncation on opencode.ai + if False and (policy.get("synthetic_tool_results") or _provider_cap(model, "synthetic_tool_results", False)) and isinstance(input_data, list): input_data, synthesized = synthesize_tool_results_for_chat(input_data) if synthesized: print("[provider-adapter] using synthetic tool-result continuation", file=sys.stderr) @@ -4603,7 +4877,7 @@ class Handler(http.server.BaseHTTPRequestHandler): body["input"] = input_data compacted = False - if policy.get("compaction") and isinstance(input_data, list): + if 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) @@ -4652,6 +4926,22 @@ class Handler(http.server.BaseHTTPRequestHandler): upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream)) except urllib.error.HTTPError as e: err_body = e.read().decode() + if "context_length_exceeded" in err_body and attempt < max_retries: + print(f"[{self._session_id}] context_length_exceeded (attempt {attempt+1}/{max_retries}), retrying with extreme compaction!", file=sys.stderr) + policy = provider_policy() + if isinstance(input_data, list): + print(f"[{self._session_id}] applying extreme compaction to {len(input_data)} items", file=sys.stderr) + input_data = _crof_compact_for_retry(input_data, model) + body = dict(body) + body["input"] = input_data + messages = oa_input_to_messages(input_data) + messages = _inject_stored_reasoning(messages) + instructions = body.get("instructions", "").strip() + if instructions: + messages.insert(0, {"role": "system", "content": instructions}) + chat_body = self._build_chat_body(model, messages, body, stream) + chat_body_b = json.dumps(chat_body).encode() + continue if e.code in (429, 502, 503) and attempt < max_retries: if e.code == 429 and _api_key_pool: pool_acct = _api_key_pool.get() @@ -4782,7 +5072,7 @@ class Handler(http.server.BaseHTTPRequestHandler): body["input"] = input_data compacted = False - if policy.get("compaction") and isinstance(input_data, list): + if 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) @@ -4793,8 +5083,8 @@ class Handler(http.server.BaseHTTPRequestHandler): body = dict(body) body["input"] = input_data - if OAUTH_PROVIDER == "google-antigravity" and isinstance(input_data, list): - input_data = _antigravity_normalize_context(input_data) + if OAUTH_PROVIDER == "google-antigravity" and isinstance(input_data, list) and "claude" not in model.lower(): + input_data = _antigravity_normalize_context(input_data, model) body = dict(body) body["input"] = input_data @@ -4872,7 +5162,7 @@ class Handler(http.server.BaseHTTPRequestHandler): resp_part["functionResponse"]["id"] = call_id contents.append({"role": "user", "parts": [resp_part]}) - if OAUTH_PROVIDER.startswith("google"): + if OAUTH_PROVIDER.startswith("google") and "claude" not in model.lower(): sanitized = [] last_user_text = None last_role = None @@ -4963,8 +5253,18 @@ class Handler(http.server.BaseHTTPRequestHandler): contents = _gemini_reattach_sigs(contents) if OAUTH_PROVIDER == "google-antigravity": - guardrail_found = any("autonomous coding agent" in json.dumps(c.get("parts", []), ensure_ascii=False) for c in contents[:2]) - if not guardrail_found: + latest_user = "" + if isinstance(input_data, list): + for item in reversed(input_data): + if item.get("type") == "message" and item.get("role") == "user": + c = item.get("content", "") + if isinstance(c, str): + latest_user = c + elif isinstance(c, list): + latest_user = "\n".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict)) + break + is_latest_simple = _antigravity_is_simple_user(latest_user) + if not is_latest_simple: contents.insert(0, {"role": "user", "parts": [{"text": _GEMINI_AGENT_GUARDRAIL}]}) if OAUTH_PROVIDER == "google-antigravity" and isinstance(input_data, list): @@ -4976,11 +5276,10 @@ class Handler(http.server.BaseHTTPRequestHandler): if isinstance(c, str): latest_lower = c.lower() elif isinstance(c, list): latest_lower = " ".join(p.get("text", p.get("input_text", "")) for p in c if isinstance(p, dict)).lower() break - if latest_lower and any(w in latest_lower for w in _EDIT_WORDS) and len(input_data) > 6: + if latest_lower and any(w in latest_lower for w in _EDIT_WORDS): n_tool_calls = sum(1 for it in input_data if isinstance(it, dict) and it.get("type") == "function_call") - if n_tool_calls > 0: - contents.append({"role": "user", "parts": [{"text": "IMPORTANT: The user is requesting a modification to existing files. You MUST use tools (exec_command, write, etc.) to make the changes. Do NOT just describe what to do — actually call the tools to modify the files now."}]}) - print(f"[antigravity] edit-intent detected with {n_tool_calls} prior tool calls; injected tool-use nudge", file=sys.stderr) + contents.append({"role": "user", "parts": [{"text": "!!! ABSOLUTELY NO PLANNING - EMIT THE TOOL CALL NOW !!! IMPORTANT: The user is requesting a modification to existing files. You MUST use tools (exec_command, read_files, write, etc.) to make the changes RIGHT NOW. Do NOT just describe what to do — actually CALL THE TOOLS IN THIS RESPONSE. IMMEDIATELY INSPECT THE FILE OR LIST FILES USING exec_command TOOL CALL."}]}) + print(f"[antigravity] edit-intent detected; injected tool-use nudge", file=sys.stderr) if OAUTH_PROVIDER == "google-antigravity" and isinstance(input_data, list): latest_user = "" @@ -5031,10 +5330,14 @@ class Handler(http.server.BaseHTTPRequestHandler): wrapped["requestType"] = "agent" wrapped["userAgent"] = "antigravity" wrapped["requestId"] = f"agent-{uuid.uuid4().hex[:12]}" + wrapped["request"]["sessionId"] = f"{uuid.uuid4().hex}{int(time.time()*1000)}" _allow_staging = os.environ.get("ALLOW_ANTIGRAVITY_STAGING", "0") == "1" if OAUTH_PROVIDER == "google-antigravity": - _antigravity_endpoints = ["https://cloudcode-pa.googleapis.com"] + _antigravity_endpoints = [ + "https://cloudcode-pa.googleapis.com", + "https://daily-cloudcode-pa.googleapis.com", + ] if _allow_staging: _antigravity_endpoints.extend([ "https://daily-cloudcode-pa.sandbox.googleapis.com", @@ -5052,7 +5355,13 @@ class Handler(http.server.BaseHTTPRequestHandler): } if OAUTH_PROVIDER == "google-antigravity": version = _ensure_antigravity_version() - headers["User-Agent"] = f"antigravity/{version} darwin/arm64" + import platform as _plat + _os_name = _plat.system().lower() + _os_arch = _plat.machine().lower().replace("x86_64", "x64").replace("aarch64", "arm64") + headers["User-Agent"] = f"antigravity/{version} {_os_name}/{_os_arch}" + headers["X-Client-Name"] = "antigravity" + headers["X-Client-Version"] = _ensure_antigravity_client_version() + headers["x-goog-api-client"] = "gl-node/18.18.2 fire/0.8.6 grpc/1.10.x" else: headers["User-Agent"] = "google-api-nodejs-client/9.15.1" headers["X-Goog-Api-Client"] = "gl-node/22.17.0" @@ -5072,14 +5381,33 @@ class Handler(http.server.BaseHTTPRequestHandler): if OAUTH_PROVIDER == "google-antigravity": print(f"[antigravity-endpoint] endpoints={[e.replace('https://','') for e in endpoints]} project={project_id}", file=sys.stderr) - for ep in endpoints: + upstream = None + chosen_ep = None + global _antigravity_preferred_endpoint + + with _antigravity_endpoint_lock: + _pref = _antigravity_preferred_endpoint + + if _pref and _pref in endpoints: + ordered = [_pref] + [e for e in endpoints if e != _pref] + else: + ordered = list(endpoints) + + for ep in ordered: target = f"{ep}/{url_suffix}" req = urllib.request.Request(target, data=body_b, headers=headers) try: upstream = urllib.request.urlopen(req, timeout=_upstream_timeout(body, stream)) + chosen_ep = ep + with _antigravity_endpoint_lock: + _antigravity_preferred_endpoint = ep + if ep != _pref: + print(f"[{self._session_id}] fallback OK: {ep.replace('https://','')}", file=sys.stderr) break except urllib.error.HTTPError as e: err_body = e.read().decode() + err_class = _classify_antigravity_error(e.code, err_body) + print(f"[{self._session_id}] {ep.replace('https://','')} {e.code} class={err_class}", file=sys.stderr) if e.code == 400 and OAUTH_PROVIDER.startswith("google"): try: debug_path = os.path.join(_LOG_DIR, "gemini-last-400-request.json") @@ -5088,23 +5416,38 @@ class Handler(http.server.BaseHTTPRequestHandler): print(f"[{self._session_id}] saved 400 debug request to {debug_path}", file=sys.stderr) except Exception: pass - if e.code == 403 and "SERVICE_DISABLED" in err_body[:500] and ep != endpoints[-1]: - print(f"[{self._session_id}] {ep} SERVICE_DISABLED, trying next endpoint", file=sys.stderr) + return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) + if err_class == "auth_permanent": + return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) + if err_class in ("quota_exhausted", "rate_limited"): + reset_s = _parse_rate_limit_reset(err_body) + if ep == ordered[-1]: + pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool + _, acct = _get_google_account(OAUTH_PROVIDER) + if acct: + cooldown = reset_s if reset_s and reset_s > 10 else 60 + pool.mark_rate_limited(acct, cooldown) + print(f"[{self._session_id}] quota reset in ~{reset_s}s, cooldown={cooldown}s", file=sys.stderr) + return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) + print(f"[{self._session_id}] {ep.replace('https://','')} 429, trying next", file=sys.stderr) + with _antigravity_endpoint_lock: + _antigravity_preferred_endpoint = None continue - if e.code == 429 and ep != endpoints[-1] and _allow_staging: - print(f"[{self._session_id}] {ep} HTTP 429, trying next endpoint", file=sys.stderr) + if err_class in ("service_disabled", "forbidden", "account_banned", "validation_required"): + if ep == ordered[-1]: + return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) continue - if e.code == 429: - pool = _google_antigravity_pool if OAUTH_PROVIDER == "google-antigravity" else _google_cli_pool - _, acct = _get_google_account(OAUTH_PROVIDER) - if acct: - pool.mark_rate_limited(acct, 60) - return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) - except Exception as e: - if ep == endpoints[-1]: - return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) - print(f"[{self._session_id}] {ep} failed: {e}, trying next", file=sys.stderr) + if ep == ordered[-1]: + return self.send_json(e.code, {"error": {"type": "upstream_error", "message": _sanitize_err_body(err_body)}}) continue + except Exception as e: + print(f"[{self._session_id}] {ep.replace('https://','')} conn failed: {e}", file=sys.stderr) + if ep == ordered[-1]: + return self.send_json(502, {"error": {"type": "proxy_error", "message": str(e)}}) + continue + + if upstream is None: + return self.send_json(502, {"error": {"type": "proxy_error", "message": "All endpoints failed"}}) if stream: self._forward_gemini_sse(upstream, model, body, input_data, tracker) @@ -5406,11 +5749,25 @@ class Handler(http.server.BaseHTTPRequestHandler): break collected_events.append(event) _observe_event(event) + print(f"[{self._session_id}] stream ended: events={len(collected_events)} finish={finish_reason} has_content={has_content} elapsed={time.time()-t0:.1f}s", file=sys.stderr) except (ConnectionResetError, BrokenPipeError, ConnectionAbortedError): print("[translate-proxy] client disconnected during stream", file=sys.stderr) _crof_record(model, n_items, False) _log_resp(last_resp_id, "client_disconnect", last_output) return + except (TimeoutError, OSError, urllib.error.URLError) as e: + print(f"[translate-proxy] upstream error during stream: {type(e).__name__}: {e}", file=sys.stderr) + err_resp_id = body.get("request_id") or body.get("id") or uid("resp") + try: + self.wfile.write(emit("response.failed", {"type": "response.failed", + "response": {"id": err_resp_id, "error": {"type": "upstream_error", + "code": "stream_interrupted", "message": str(e)[:200]}}}).encode()) + self.wfile.flush() + except Exception: + pass + _crof_record(model, n_items, False) + _log_resp(last_resp_id, "upstream_error", last_output) + return # Record outcome success = (finish_reason != "length") @@ -5486,6 +5843,171 @@ class Handler(http.server.BaseHTTPRequestHandler): except Exception as e: print(f"[crof-adaptive] retry failed: {e}", file=sys.stderr) + # ── Auto-continue for truncated responses ── (cobra PR) + _ac_did_run = False + if stream and collected_events: + _ac_text = "" + _ac_msg_id = _ac_resp_id = None + for _ev in collected_events: + for _ln in _ev.strip().split("\n"): + if not _ln.startswith("data: "): + continue + try: + _d = json.loads(_ln[6:]) + _t = _d.get("type") + if _t == "response.output_text.done": + _ac_text = _d.get("text", "") + elif _t == "response.output_item.added" and _d.get("item",{}).get("type") == "message": + _ac_msg_id = _d.get("item",{}).get("id") + elif _t == "response.completed": + _ac_resp_id = _d.get("response",{}).get("id") + except Exception: + pass + + _ac_tc = reasoning_out.get("tool_calls", []) + _ac_truncated = False + if not _ac_tc and _ac_text: + _ac_stripped = _ac_text.rstrip() + if finish_reason == "length": + _ac_truncated = True + elif len(_ac_stripped) > 10 and _ac_stripped[-1] in "(:,;…": + _ac_truncated = True + + if _ac_truncated and _ac_text: + print(f"[{self._session_id}] auto-continue: truncated (finish={finish_reason}, ends '{_ac_text.rstrip()[-10:]}')", file=sys.stderr) + _ac_did_run = True + _ac_cut = len(collected_events) + for _i, _ev2 in enumerate(collected_events): + if "response.output_text.done" in _ev2: + _ac_cut = _i + break + collected_events = collected_events[:_ac_cut] + + _ac_accumulated = _ac_text + _ac_max = 3 + for _ac_attempt in range(_ac_max): + try: + _ac_cont_msgs = list(chat_body.get("messages", [])) + _ac_cont_msgs.append({"role": "assistant", "content": _ac_accumulated}) + _ac_cont_msgs.append({"role": "user", "content": "Continue exactly where you left off. Do not repeat anything already written."}) + _ac_cont_body = dict(chat_body) + _ac_cont_body["messages"] = _ac_cont_msgs + _ac_cont_body["stream"] = False + _ac_cont_req = urllib.request.Request(target, data=json.dumps(_ac_cont_body).encode(), headers=fwd) + _ac_cont_resp = json.loads(urllib.request.urlopen(_ac_cont_req, timeout=120).read()) + _ac_choices = _ac_cont_resp.get("choices", []) + if _ac_choices: + _ac_chunk = _ac_choices[0].get("message",{}).get("content","") + if not _ac_chunk: + _ac_chunk = _ac_choices[0].get("delta",{}).get("content","") + _ac_finish = _ac_choices[0].get("finish_reason") + if _ac_chunk: + _ac_accumulated += _ac_chunk + collected_events.append(emit("response.output_text.delta", { + "type": "response.output_text.delta", + "delta": _ac_chunk, "item_id": _ac_msg_id, "content_index": 0})) + if _ac_finish != "length": + break + _ac_text = _ac_accumulated + except Exception as _ac_e: + print(f"[{self._session_id}] auto-continue attempt {_ac_attempt+1} failed: {_ac_e}", file=sys.stderr) + break + + if _ac_msg_id: + collected_events.append(emit("response.output_text.done", { + "type": "response.output_text.done", + "text": _ac_accumulated, "item_id": _ac_msg_id, "content_index": 0})) + collected_events.append(emit("response.content_part.done", { + "type": "response.content_part.done", + "part": {"type": "output_text", "text": _ac_accumulated, "annotations": []}, "item_id": _ac_msg_id})) + collected_events.append(emit("response.output_item.done", { + "type": "response.output_item.done", + "item": {"type": "message", "id": _ac_msg_id, "role": "assistant", "status": "completed", + "content": [{"type": "output_text", "text": _ac_accumulated, "annotations": []}]}})) + if _ac_resp_id: + collected_events.append(emit("response.completed", { + "type": "response.completed", + "response": {"id": _ac_resp_id, "object": "response", "model": model, + "status": "completed", "created": int(time.time()), + "output": [{"type": "message", "id": _ac_msg_id, "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": _ac_accumulated, "annotations": []}]}]}})) + has_content = True + finish_reason = "stop" + print(f"[{self._session_id}] auto-continue done: {len(_ac_text)} -> {len(_ac_accumulated)} chars", file=sys.stderr) + + # Smart continuation: loop with escalating nudges when model stops text-only mid-task. + # Skip if auto-continue already handled the response. + if not _ac_did_run: + _smart_max = 2 + _smart_attempt = 0 + while _smart_attempt < _smart_max: + _has_tool_calls_in_output = any(o.get("type") == "function_call" for o in (last_output or [])) + if not (finish_reason == "stop" and has_content and not _has_tool_calls_in_output + and isinstance(input_data, list) and len(input_data) >= 3 + and has_function_call_output(input_data)): + break + _smart_attempt += 1 + _nudges = [ + "Continue with the task using tool calls. Do NOT describe what to do — call the appropriate functions.", + "You MUST use tool calls to complete the task. Read files, run commands, and make changes using tools. Do NOT output XML tool calls as text.", + ] + nudge_text = _nudges[min(_smart_attempt - 1, len(_nudges) - 1)] + # Try extracting XML tool calls from text as fallback before nudging + last_text = "" + for o in (last_output or []): + if o.get("type") == "message": + for c in (o.get("content") or []): + if isinstance(c, dict) and c.get("type") == "output_text": + last_text += c.get("text", "") + xml_fc = _extract_xml_tool_calls(last_text) + if xml_fc: + print(f"[{self._session_id}] [smart-continue] extracted {len(xml_fc)} XML tool calls from text, injecting and retrying", file=sys.stderr) + fake_input = list(input_data) + for xfc in xml_fc: + fake_input.append({"type": "function_call", "id": uid("fcx"), "call_id": uid("fcx"), + "name": xfc["name"], "arguments": xfc["args"], "status": "completed"}) + fake_messages = oa_input_to_messages(fake_input) + instructions = body.get("instructions", "").strip() + if instructions: + fake_messages.insert(0, {"role": "system", "content": instructions}) + fake_chat_body = self._build_chat_body(model, fake_messages, body, stream) + fake_req = urllib.request.Request(target, data=json.dumps(fake_chat_body).encode(), headers=fwd) + try: + retry_upstream = urllib.request.urlopen(fake_req, timeout=_upstream_timeout(body, True)) + collected_events = [] + last_resp_id = last_output = last_status = None + finish_reason = None + has_content = False + for event in oa_stream_to_sse(retry_upstream, model, body.get("request_id") or body.get("id")): + collected_events.append(event) + _observe_event(event) + input_data = fake_input + continue + except Exception as e: + print(f"[{self._session_id}] [smart-continue] XML injection retry failed: {e}", file=sys.stderr) + break + _nudge_msg = {"role": "user", "content": nudge_text} + nudge_messages = oa_input_to_messages(input_data) + [_nudge_msg] + instructions = body.get("instructions", "").strip() + if instructions: + nudge_messages.insert(0, {"role": "system", "content": instructions}) + nudge_chat_body = self._build_chat_body(model, nudge_messages, body, stream) + nudge_req = urllib.request.Request(target, data=json.dumps(nudge_chat_body).encode(), headers=fwd) + print(f"[{self._session_id}] [smart-continue] attempt {_smart_attempt}/{_smart_max}: model stopped mid-task, nudging", file=sys.stderr) + try: + retry_upstream = urllib.request.urlopen(nudge_req, timeout=_upstream_timeout(body, True)) + collected_events = [] + last_resp_id = last_output = last_status = None + finish_reason = None + has_content = False + for event in oa_stream_to_sse(retry_upstream, model, body.get("request_id") or body.get("id")): + collected_events.append(event) + _observe_event(event) + except Exception as e: + print(f"[{self._session_id}] [smart-continue] nudge attempt {_smart_attempt} failed: {e}", file=sys.stderr) + break + self.stream_buffered_events(collected_events) else: result = oa_resp_to_responses(json.loads(upstream.read()), model) @@ -6392,12 +6914,45 @@ def _handle_shutdown_signal(sig, frame): if 'SERVER' in globals() and SERVER: SERVER.shutdown() +def _anti_stall_cleanup(): + my_pid = os.getpid() + my_port = PORT + killed = [] + try: + import subprocess as _sp + out = _sp.run(["pgrep", "-f", "translate-proxy"], capture_output=True, text=True, timeout=5).stdout.strip() + for pid_str in out.splitlines(): + pid_str = pid_str.strip() + if not pid_str or not pid_str.isdigit(): + continue + pid = int(pid_str) + if pid == my_pid: + continue + try: + os.kill(pid, signal.SIGTERM) + killed.append(pid) + except (ProcessLookupError, PermissionError): + pass + except Exception: + pass + try: + _cache_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "__pycache__") + if os.path.isdir(_cache_dir): + import shutil + shutil.rmtree(_cache_dir, ignore_errors=True) + except Exception: + pass + if killed: + print(f"[anti-stall] killed {len(killed)} stale proxy process(es): {killed}", flush=True) + time.sleep(1) + def main(): global SERVER, _START_TIME _START_TIME = time.time() + _anti_stall_cleanup() _init_runtime() try: - _current_cfg = os.path.basename(args.config) if args.config else "" + _current_cfg = os.path.basename(_CONFIG_PATH) if _CONFIG_PATH else "" for _f in os.listdir(_LOG_DIR): if _f.startswith("proxy-") and _f.endswith(".json") and _f != _current_cfg: os.remove(os.path.join(_LOG_DIR, _f))