v10.13.8: hard tool-strip on budget cap, compaction output stripping, null-tool loop detection

This commit is contained in:
Roman | RyzenAdvanced
2026-05-27 17:08:21 +04:00
Unverified
parent da43297953
commit 6861700c0d
3 changed files with 81 additions and 55 deletions

View File

@@ -1879,7 +1879,7 @@ def _crof_compact_for_retry(input_data, model, aggression=0):
return head + tail
summary_lines = [f"[Auto-compacted: {len(body)} turns removed (adaptive limit={limit})]"]
for item in body[-5:]:
for item in body[-3:]:
summary_lines.append(_item_summary(item, max_len=120))
summary_msg = {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "\n".join(summary_lines)}]}
@@ -1908,9 +1908,13 @@ def _item_summary(item, max_len=200):
return f"[tool call] {name}({args[:max_len]})"
elif t == "function_call_output":
output = item.get("output", "")
if len(output) > max_len:
return f"[tool result] {output[:max_len]}..."
return f"[tool result] {output}"
out_len = len(output) if isinstance(output, str) else len(str(output))
exit_match = re.search(r'Process exited with code (\d+)', output if isinstance(output, str) else str(output))
exit_code = exit_match.group(1) if exit_match else "?"
tok_match = re.search(r'Original token count: (\d+)', output if isinstance(output, str) else str(output))
tok_count = tok_match.group(1) if tok_match else ""
tok_info = f" ({tok_count} tokens)" if tok_count else ""
return f"[tool result] exit={exit_code}{tok_info} {out_len} chars"
return f"[{t}]"
def _extract_files(items):
@@ -1998,6 +2002,10 @@ def _compact_input(input_data):
tool_summaries.append(_item_summary(item, max_len=150))
files = _extract_files(body)
n_read_tools = sum(1 for it in body if it.get("type") == "function_call"
and any(k in it.get("arguments", "") for k in ["cat ", "head ", "tail ", "sed -n", "grep ", "less ", "more ", "python3 -c", ".read()"]))
n_write_tools = sum(1 for it in body if it.get("type") == "function_call"
and any(k in it.get("arguments", "") for k in ["write(", ".write", " > ", " >> ", "sed -i", "patch "]))
summary_lines = [f"[Auto-compacted: {len(body)} earlier turns summarized to preserve context]"]
if user_queries:
@@ -2005,11 +2013,13 @@ def _compact_input(input_data):
if assistant_msgs:
summary_lines.append(f"Assistant responses: {'; '.join(assistant_msgs[-3:])}")
if tool_summaries:
summary_lines.append(f"Actions taken ({len(tool_summaries)} steps):")
for ts in tool_summaries[-15:]:
summary_lines.append(f"Actions taken ({len(tool_summaries)} steps, {n_read_tools} reads, {n_write_tools} writes):")
for ts in tool_summaries[-5:]:
summary_lines.append(f" {ts}")
if files:
summary_lines.append(f"Files touched: {', '.join(sorted(files)[-10:])}")
if n_read_tools > 10 and n_write_tools == 0:
summary_lines.append("⚠ You have already read these files extensively but made NO edits. You MUST write your changes NOW. Do NOT read any more files.")
summary_text = "\n".join(summary_lines)
summary_msg = {
@@ -2135,7 +2145,7 @@ def _adaptive_compact(input_data, model, policy=None):
if not body:
return head + tail, True
summary_lines = [f"[Auto-compacted: {len(body)} turns removed (budget={input_budget}tok, model={model})]"]
for item in body[-5:]:
for item in body[-3:]:
summary_lines.append(_item_summary(item, max_len=120))
summary_msg = {"type": "message", "role": "user",
"content": [{"type": "input_text", "text": "\n".join(summary_lines)}]}
@@ -5907,6 +5917,18 @@ class Handler(http.server.BaseHTTPRequestHandler):
file=sys.stderr)
break
null_tool_names = {"get_goal", "get_remaining_tokens", "get_completion_budget", "status"}
consecutive_null = 0
for item in reversed(input_data):
if isinstance(item, dict):
if item.get("type") == "function_call" and item.get("name") in null_tool_names:
consecutive_null += 1
elif item.get("type") == "function_call":
break
if consecutive_null >= 3:
ag_state["force_finalize"] = True
print(f"[{getattr(self, '_session_id', '?')}] [antigravity-loop] NULL-TOOL LOOP: {consecutive_null} consecutive {null_tool_names} calls, forcing finalize", file=sys.stderr)
last_tool_key = None
for item in reversed(input_data):
if isinstance(item, dict) and item.get("type") == "function_call":
@@ -5940,9 +5962,12 @@ class Handler(http.server.BaseHTTPRequestHandler):
request_body["systemInstruction"] = {"role": "user", "parts": system_parts}
if gen_config:
request_body["generationConfig"] = gen_config
if gemini_tools:
_budget_exceeded = ag_state.get("total_tool_calls", 0) > _ANTIGRAVITY_MAX_TOOL_CALLS_PER_TASK
if gemini_tools and not _budget_exceeded and not ag_state.get("force_finalize"):
request_body["tools"] = gemini_tools
if _is_claude_model and gemini_tools:
elif _budget_exceeded or ag_state.get("force_finalize"):
print(f"[{getattr(self, '_session_id', '?')}] [antigravity-budget] TOOLS STRIPPED from request (budget exceeded or force_finalize)", file=sys.stderr)
if _is_claude_model and "tools" in request_body:
request_body["toolConfig"] = {"functionCallingConfig": {"mode": "VALIDATED"}}
import platform as _plat
@@ -6720,22 +6745,34 @@ class Handler(http.server.BaseHTTPRequestHandler):
f"{ft['path_counts'][dp]}x, total={ft['total_reads']}", file=sys.stderr)
break
last_tool_key = None
for item in reversed(input_data):
if isinstance(item, dict) and item.get("type") == "function_call":
fname = item.get("name", "")
args_str = json.dumps(item.get("arguments", {}), sort_keys=True)[:100]
last_tool_key = f"{fname}:{args_str}"
break
if last_tool_key:
if last_tool_key == ag_state["last_tool"]:
ag_state["last_tool_count"] += 1
if ag_state["last_tool_count"] >= 5:
ag_state["repeated_tool"] = True
ag_state["force_finalize"] = True
else:
ag_state["last_tool"] = last_tool_key
ag_state["last_tool_count"] = 1
null_tool_names = {"get_goal", "get_remaining_tokens", "get_completion_budget", "status"}
consecutive_null = 0
for item in reversed(input_data):
if isinstance(item, dict):
if item.get("type") == "function_call" and item.get("name") in null_tool_names:
consecutive_null += 1
elif item.get("type") == "function_call":
break
if consecutive_null >= 3:
ag_state["force_finalize"] = True
print(f"[{getattr(self, '_session_id', '?')}] [antigravity-loop] NULL-TOOL LOOP: {consecutive_null} consecutive {null_tool_names} calls, forcing finalize", file=sys.stderr)
last_tool_key = None
for item in reversed(input_data):
if isinstance(item, dict) and item.get("type") == "function_call":
fname = item.get("name", "")
args_str = json.dumps(item.get("arguments", {}), sort_keys=True)[:100]
last_tool_key = f"{fname}:{args_str}"
break
if last_tool_key:
if last_tool_key == ag_state["last_tool"]:
ag_state["last_tool_count"] += 1
if ag_state["last_tool_count"] >= 5:
ag_state["repeated_tool"] = True
ag_state["force_finalize"] = True
else:
ag_state["last_tool"] = last_tool_key
ag_state["last_tool_count"] = 1
_EDIT_WORDS = ("change", "fix", "update", "redesign", "rewrite", "modify", "improve", "replace", "edit", "make it", "add", "remove", "delete", "rename", "move", "convert")
latest_lower = ""
@@ -6789,10 +6826,13 @@ class Handler(http.server.BaseHTTPRequestHandler):
request_body["systemInstruction"] = {"parts": system_parts}
if gen_config:
request_body["generationConfig"] = gen_config
if gemini_tools:
_budget_exceeded_oa = ag_state.get("total_tool_calls", 0) > _ANTIGRAVITY_MAX_TOOL_CALLS_PER_TASK
if gemini_tools and not _budget_exceeded_oa and not ag_state.get("force_finalize"):
request_body["tools"] = gemini_tools
elif _budget_exceeded_oa or ag_state.get("force_finalize"):
print(f"[antigravity-budget] TOOLS STRIPPED from OA request (budget exceeded or force_finalize)", file=sys.stderr)
if OAUTH_PROVIDER == "google-antigravity" and _is_claude_model and gemini_tools:
if OAUTH_PROVIDER == "google-antigravity" and _is_claude_model and "tools" in request_body:
request_body["toolConfig"] = {"functionCallingConfig": {"mode": "VALIDATED"}}
if _is_claude_thinking:
print(f"[antigravity-claude] applied VALIDATED toolConfig for thinking model", file=sys.stderr)