This commit is contained in:
2026-02-21 22:40:50 -05:00
parent 478dbb9b86
commit 00d14131a9

View File

@@ -27,7 +27,8 @@ comms_log_callback = None
# Signature: (script: str, result: str) -> None
tool_log_callback = None
MAX_TOOL_ROUNDS = 5
# Increased to allow thorough code exploration before forcing a summary
MAX_TOOL_ROUNDS = 10
# Maximum characters per text chunk sent to Anthropic.
# Kept well under the ~200k token API limit.
@@ -438,7 +439,8 @@ def _send_gemini(md_content: str, user_message: str, base_dir: str, file_items:
all_text_parts = []
for round_idx in range(MAX_TOOL_ROUNDS + 1):
# We allow MAX_TOOL_ROUNDS, plus 1 final loop to get the text synthesis
for round_idx in range(MAX_TOOL_ROUNDS + 2):
response = _gemini_chat.send_message(payload_to_send)
text_parts_raw = [
@@ -483,53 +485,48 @@ def _send_gemini(md_content: str, user_message: str, base_dir: str, file_items:
if not tool_calls:
break
if round_idx >= MAX_TOOL_ROUNDS:
if round_idx > MAX_TOOL_ROUNDS:
# The model ignored the MAX ROUNDS warning and kept calling tools.
# Force abort to prevent infinite loop.
break
function_responses = []
sent_results_log = []
for fc in tool_calls:
for i, fc in enumerate(tool_calls):
fc_name = fc.name
fc_args = dict(fc.args)
if fc_name in mcp_client.TOOL_NAMES:
_append_comms("OUT", "tool_call", {"name": fc_name, "args": fc_args})
output = mcp_client.dispatch(fc_name, fc_args)
_append_comms("IN", "tool_result", {"name": fc_name, "output": output})
function_responses.append(
types.Part.from_function_response(
name=fc_name,
response={"output": output}
)
)
sent_results_log.append({"tool_use_id": fc_name, "content": output})
elif fc_name == TOOL_NAME:
script = fc_args.get("script", "")
_append_comms("OUT", "tool_call", {
"name": TOOL_NAME,
"script": script,
})
_append_comms("OUT", "tool_call", {"name": TOOL_NAME, "script": script})
output = _run_script(script, base_dir)
_append_comms("IN", "tool_result", {
"name": TOOL_NAME,
"output": output,
})
function_responses.append(
types.Part.from_function_response(
name=TOOL_NAME,
response={"output": output}
)
)
sent_results_log.append({"tool_use_id": TOOL_NAME, "content": output})
_append_comms("IN", "tool_result", {"name": TOOL_NAME, "output": output})
else:
output = f"ERROR: unknown tool '{fc_name}'"
# Refresh file context after tool calls locally, but DO NOT inject as text part into Gemini.
# Gemini strictly expects only function_responses in this array.
if file_items:
file_items = _reread_file_items(file_items)
# Inject dynamic updates directly into the LAST tool's output string.
# Gemini strictly expects function_responses only, so we piggyback on the string.
if i == len(tool_calls) - 1:
if file_items:
file_items = _reread_file_items(file_items)
refreshed_ctx = _build_file_context_text(file_items)
if refreshed_ctx:
output += f"\n\n[SYSTEM: FILES UPDATED — current contents below. Do NOT re-read these files.]\n\n{refreshed_ctx}"
_append_comms("OUT", "tool_result_send", {
"results": sent_results_log
})
if round_idx == MAX_TOOL_ROUNDS:
output += "\n\n[SYSTEM WARNING: MAX TOOL ROUNDS REACHED. YOU MUST PROVIDE YOUR FINAL ANSWER NOW WITHOUT CALLING ANY MORE TOOLS.]"
function_responses.append(
types.Part.from_function_response(name=fc_name, response={"output": output})
)
sent_results_log.append({"tool_use_id": fc_name, "content": output})
_append_comms("OUT", "tool_result_send", {"results": sent_results_log})
payload_to_send = function_responses
final_text = "\n\n".join(all_text_parts)
@@ -641,7 +638,8 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
all_text_parts = []
for round_idx in range(MAX_TOOL_ROUNDS + 1):
# We allow MAX_TOOL_ROUNDS, plus 1 final loop to get the text synthesis
for round_idx in range(MAX_TOOL_ROUNDS + 2):
response = _anthropic_client.messages.create(
model=_model,
max_tokens=8096,
@@ -687,10 +685,12 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
"usage": usage_dict,
})
if response.stop_reason != "tool_use":
if response.stop_reason != "tool_use" or not tool_use_blocks:
break
if round_idx >= MAX_TOOL_ROUNDS:
if round_idx > MAX_TOOL_ROUNDS:
# The model ignored the MAX ROUNDS warning and kept calling tools.
# Force abort to prevent infinite loop.
break
tool_results = []
@@ -728,9 +728,6 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
"content": output,
})
if not tool_results:
break
# Refresh file context after tool calls and inject into tool result message
if file_items:
file_items = _reread_file_items(file_items)
@@ -745,6 +742,12 @@ def _send_anthropic(md_content: str, user_message: str, base_dir: str, file_item
),
})
if round_idx == MAX_TOOL_ROUNDS:
tool_results.append({
"type": "text",
"text": "SYSTEM WARNING: MAX TOOL ROUNDS REACHED. YOU MUST PROVIDE YOUR FINAL ANSWER NOW WITHOUT CALLING ANY MORE TOOLS."
})
_anthropic_history.append({
"role": "user",
"content": tool_results,