From 4a723479a840cbb9073fcd609128ffb459f473cc Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 24 May 2026 19:59:22 +0000 Subject: [PATCH] v3.9.3: sync src/ --- src/translate-proxy.py | 137 +++++++++++++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 18 deletions(-) diff --git a/src/translate-proxy.py b/src/translate-proxy.py index e94094a..8142a6a 100755 --- a/src/translate-proxy.py +++ b/src/translate-proxy.py @@ -603,6 +603,63 @@ def _refresh_google_token(token_data, token_path): print(f"[oauth] refresh failed: {e}", file=sys.stderr) return token_data.get("access_token", "") +# ═══════════════════════════════════════════════════════════════════ +# Gemini 3 thought signature preservation +# ═══════════════════════════════════════════════════════════════════ + +_gemini_sig_store = {} +_gemini_sig_lock = threading.Lock() + +def _gemini_store_sig(key, signature): + if not key or not signature: + return + with _gemini_sig_lock: + _gemini_sig_store[key] = {"sig": signature, "ts": time.time()} + +def _gemini_get_sig(key): + with _gemini_sig_lock: + item = _gemini_sig_store.get(key) + return item["sig"] if item else None + +def _extract_gemini_sig(part): + if not isinstance(part, dict): + return None + return part.get("thoughtSignature") or part.get("thought_signature") or part.get("signature") + +def _gemini_reattach_sigs(contents): + for content in contents: + for part in content.get("parts", []): + if not isinstance(part, dict): + continue + if "thoughtSignature" in part: + continue + if "functionCall" in part: + fc = part["functionCall"] + cid = fc.get("id") or fc.get("name") + if cid: + sig = _gemini_get_sig(f"fc:{cid}") + if sig: + part["thoughtSignature"] = sig + if "text" in part and content.get("role") == "model": + turn_key = content.get("_proxy_turn_key") + if turn_key: + sig = _gemini_get_sig(f"turn:{turn_key}") + if sig: + part["thoughtSignature"] = sig + return 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." +) + _LOG_FILE = None _LOG_FILE_LOCK = threading.Lock() @@ -4180,44 +4237,47 @@ class Handler(http.server.BaseHTTPRequestHandler): def _handle_gemini_oauth(self, body, model, stream, tracker=None): input_data = body.get("input", "") policy = provider_policy() + original_model = model + + _GEMINI_KEEP_RECENT = 6 + _GEMINI_OLD_LIMIT = 3000 + _GEMINI_RECENT_LIMIT = 20000 if isinstance(input_data, list) and len(input_data) > 8: n_tool_outputs = sum(1 for it in input_data if isinstance(it, dict) and it.get("type") == "function_call_output") if n_tool_outputs > 2: + tool_indexes = [i for i, it in enumerate(input_data) if isinstance(it, dict) and it.get("type") == "function_call_output"] + recent_set = set(tool_indexes[-_GEMINI_KEEP_RECENT:]) compacted_data = [] - last_fc_idx = None - for i in range(len(input_data) - 1, -1, -1): - if isinstance(input_data[i], dict) and input_data[i].get("type") == "function_call": - last_fc_idx = i - break - keep_from = last_fc_idx if last_fc_idx is not None else len(input_data) for i, item in enumerate(input_data): if isinstance(item, dict) and item.get("type") == "function_call_output": o = item.get("output", "") - if i < keep_from and len(o) > 1500: + limit = _GEMINI_RECENT_LIMIT if i in recent_set else _GEMINI_OLD_LIMIT + if len(o) > limit: item = dict(item) - summary = o[:600] + f"\n... [compacted {len(o) - 600} chars - earlier tool result]" - item["output"] = summary + item["output"] = o[:limit] + f"\n... [proxy compacted: kept {limit} of {len(o)} chars]" compacted_data.append(item) input_data = compacted_data body = dict(body) body["input"] = input_data - print(f"[gemini-compact] {n_tool_outputs} tool outputs, compacted earlier ones to 600 chars", file=sys.stderr) + print(f"[gemini-compact] {n_tool_outputs} tool outputs, recent={_GEMINI_RECENT_LIMIT} old={_GEMINI_OLD_LIMIT}", file=sys.stderr) if OAUTH_PROVIDER == "google-antigravity": alias_map = { "antigravity-gemini-3-flash": "gemini-3-flash", - "antigravity-gemini-3-pro": "gemini-3-pro-low", - "antigravity-gemini-3.1-pro": "gemini-3.1-pro-low", - "gemini-3-flash-preview": "gemini-3-flash", - "gemini-3-pro-preview": "gemini-3-pro-low", - "gemini-3.1-pro-preview": "gemini-3.1-pro-low", - "gemini-3-pro": "gemini-3-pro-low", - "gemini-3.1-pro": "gemini-3.1-pro-low", + "antigravity-gemini-3-pro": "gemini-3-pro", + "antigravity-gemini-3.1-pro": "gemini-3.1-pro", + "gemini-3-flash-preview": "gemini-3-flash-preview", + "gemini-3-pro-preview": "gemini-3-pro-preview", + "gemini-3.1-pro-preview": "gemini-3.1-pro-preview", + "gemini-3-pro": "gemini-3-pro", + "gemini-3.1-pro": "gemini-3.1-pro", "antigravity-claude-sonnet-4-6": "claude-sonnet-4-6", "antigravity-claude-opus-4-6-thinking": "claude-opus-4-6-thinking", } model = alias_map.get(model, model) + if model != original_model: + print(f"[antigravity] model mapped user={original_model} upstream={model}", file=sys.stderr) pair_errors = validate_tool_pairs(input_data) if pair_errors: @@ -4285,7 +4345,11 @@ class Handler(http.server.BaseHTTPRequestHandler): args = json.loads(args) except Exception: args = {} - contents.append({"role": "model", "parts": [{"functionCall": {"name": fname, "args": args, "id": call_id}, "thoughtSignature": "skip_thought_signature_validator"}]}) + fc_part = {"functionCall": {"name": fname, "args": args, "id": call_id}} + stored_sig = _gemini_get_sig(f"fc:{call_id}") + if stored_sig: + fc_part["thoughtSignature"] = stored_sig + contents.append({"role": "model", "parts": [fc_part]}) elif t == "function_call_output": call_id = item.get("call_id", item.get("id", "")) output = item.get("output", "") @@ -4367,6 +4431,30 @@ class Handler(http.server.BaseHTTPRequestHandler): if func_decls: gemini_tools = [{"functionDeclarations": func_decls}] + if OAUTH_PROVIDER == "google-antigravity": + 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: + contents.insert(0, {"role": "user", "parts": [{"text": _GEMINI_AGENT_GUARDRAIL}]}) + + if OAUTH_PROVIDER == "google-antigravity" and isinstance(input_data, list): + latest_user = "" + 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 + if latest_user: + serialized = json.dumps(contents, ensure_ascii=False) + needle = latest_user.strip()[:120] + if needle and needle not in serialized: + print(f"[antigravity] WARNING: latest user instruction missing from contents! needle={needle[:60]}...", file=sys.stderr) + request_body = {"contents": contents} if system_parts: request_body["systemInstruction"] = {"parts": system_parts} @@ -4505,6 +4593,13 @@ class Handler(http.server.BaseHTTPRequestHandler): print(f"[{self._session_id}] finish without parts: {candidates[0].get('finishReason')}", file=sys.stderr) parts = candidates[0].get("content", {}).get("parts", []) for part in parts: + sig = _extract_gemini_sig(part) + if sig: + if part.get("functionCall"): + fc_id = part["functionCall"].get("id") or part["functionCall"].get("name") + if fc_id: + _gemini_store_sig(f"fc:{fc_id}", sig) + _gemini_store_sig(f"turn:{resp_id}", sig) if part.get("thought"): continue if "text" in part and not part.get("functionCall"): @@ -4530,6 +4625,12 @@ class Handler(http.server.BaseHTTPRequestHandler): output_items.append({"tool": True}) last_finish = candidates[0].get("finishReason", "") if last_finish: + part_kinds = [] + for p in parts: + if "text" in p: part_kinds.append("text") + if "functionCall" in p: part_kinds.append("functionCall") + if _extract_gemini_sig(p): part_kinds.append("thoughtSignature") + print(f"[{self._session_id}] [antigravity] finish={last_finish} parts={part_kinds} tool_calls={len(current_tool_calls)}", file=sys.stderr) if OAUTH_PROVIDER == "google-antigravity" and last_finish == "MAX_TOKENS" and full_text and not current_tool_calls: print(f"[{self._session_id}] MAX_TOKENS hit ({len(full_text)} chars), auto-continuing...", file=sys.stderr) break