fix: better snapshot and gate

This commit is contained in:
Alex committed 2026-08-22 14:00:15 +01:00
1 parent 29f661f3c7
commit 3f88dc7b57
12 files changed
+449 -22

No files matched your search

+11 -3
View File
@@ -511,15 +511,23 @@ class TestBaseAgentToolExecution:
call.name = "action_999"
call.arguments = "{}"
tools_dict = {"1": {"name": "tool1", "config": {}, "actions": []}}
tools_dict = {
"1": {
"name": "tool1",
"config": {},
"actions": [{"name": "tool1_read", "description": "D"}],
}
}
results = list(agent._execute_tool_action(tools_dict, call))
assert results[0]["type"] == "tool_call"
assert results[0]["data"]["status"] == "error"
assert "no such tool" in results[0]["data"]["result"]
# Tool *names*, not internal ids — the model never sees the ids.
assert "tool1" in results[0]["data"]["result"]
# LLM-visible ACTION names. Not the tool name and never the internal
# id: both name a string the model cannot actually call, which just
# buys another failed round.
assert "tool1_read" in results[0]["data"]["result"]
def test_execute_tool_action_with_parameters(
self,
@@ -17,10 +17,16 @@ import pytest
from application.agents.tool_executor import ToolExecutor
def _action(name):
return {"name": name, "description": "D", "active": True, "parameters": {"properties": {}}}
def _tools_dict():
# Rows carry actions, as every production row does: the model calls the
# ACTION name, so that is what an error may advertise.
return {
"t1": {"name": "memory", "actions": [], "config": {}},
"t2": {"name": "read_webpage", "actions": [], "config": {}},
"t1": {"name": "memory", "actions": [_action("memory_view")], "config": {}},
"t2": {"name": "read_webpage", "actions": [_action("read_webpage")], "config": {}},
}
@@ -119,6 +125,39 @@ class TestErrorNamesWhatTheModelCanCall:
_events, (result, _call_id) = _drain(executor, _call("make_a_pdf"), tools=tools_dict)
assert "create_artifact" in result
def test_the_fallback_advertises_action_names_not_tool_names(self):
"""With no name mapping built, the fallback must still name callables.
``_tool_to_name`` is empty whenever a turn produced zero LLM schemas —
every action toggled off, an unsynced MCP row, an ``api_tool`` with no
``config.actions``. Advertising ``code_executor`` there invites a call
named ``code_executor``, which cannot resolve: the error feeds the very
loop it exists to break.
"""
executor = ToolExecutor()
tools_dict = {
"t1": {"name": "code_executor", "actions": [_action("run_code")]},
"t2": {
"name": "api_tool",
"config": {"actions": {"a": _action("fetch_invoice")}},
},
}
assert executor._tool_to_name == {}
_events, (result, _call_id) = _drain(executor, _call("bash"), tools=tools_dict)
assert "run_code" in result and "fetch_invoice" in result, result
assert "code_executor" not in result, result
assert "api_tool" not in result, result
def test_the_fallback_skips_inactive_actions(self):
"""An action the user switched off is not callable, so it is not offered."""
executor = ToolExecutor()
off = _action("run_code")
off["active"] = False
tools_dict = {"t1": {"name": "code_executor", "actions": [off]}}
_events, (result, _call_id) = _drain(executor, _call("bash"), tools=tools_dict)
assert "(none available)" in result, result
def test_only_advertises_tools_in_scope_for_this_call(self):
"""A narrowed ``tools_dict`` must not be told about out-of-scope tools.