"""Tests for the ReAct parsing module."""
import json
from loader.runtime.parsing import format_tool_result, parse_tool_calls
class TestParseToolCalls:
"""Tests for parse_tool_calls function."""
def test_parse_tool_call_xml_style(self):
text = '''I need to read the file.
{"name": "read", "arguments": {"file_path": "/tmp/test.txt"}}
'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "read"
assert result.tool_calls[0].arguments == {"file_path": "/tmp/test.txt"}
assert not result.is_final_answer
def test_parse_multiple_tool_calls(self):
text = '''
{"name": "read", "arguments": {"file_path": "a.txt"}}
{"name": "read", "arguments": {"file_path": "b.txt"}}
'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 2
assert result.tool_calls[0].arguments["file_path"] == "a.txt"
assert result.tool_calls[1].arguments["file_path"] == "b.txt"
def test_parse_final_answer(self):
text = '''Thought: I have all the information needed.
Final Answer: The file contains a hello world program.'''
result = parse_tool_calls(text)
assert result.is_final_answer
assert len(result.tool_calls) == 0
assert "hello world" in result.content.lower()
def test_parse_no_tool_calls(self):
text = "Just some regular text without any tool calls."
result = parse_tool_calls(text)
assert len(result.tool_calls) == 0
assert not result.is_final_answer
assert "regular text" in result.content
def test_parse_bare_json(self):
text = '''Let me read that file.
{"name": "read", "arguments": {"file_path": "/test.txt"}}'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "read"
def test_parse_bare_json_todowrite_with_nested_items(self):
text = json.dumps(
{
"name": "TodoWrite",
"arguments": {
"todos": [
{
"content": "Run tests",
"active_form": "Running tests",
"status": "in_progress",
}
]
},
}
)
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "TodoWrite"
assert result.tool_calls[0].arguments["todos"][0]["content"] == "Run tests"
def test_parse_bare_json_patch_with_nested_hunks(self):
text = json.dumps(
{
"name": "patch",
"arguments": {
"file_path": "sample.txt",
"hunks": [
{
"old_start": 2,
"old_lines": 1,
"new_start": 2,
"new_lines": 1,
"lines": ["-beta", "+beta updated"],
}
],
},
}
)
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "patch"
assert result.tool_calls[0].arguments["hunks"][0]["lines"] == [
"-beta",
"+beta updated",
]
def test_parse_bare_json_ask_user_question_with_option_objects(self):
text = json.dumps(
{
"name": "AskUserQuestion",
"arguments": {
"question": "Which path should we take?",
"options": [
{
"label": "Plan first",
"description": "Write the plan before changing code.",
},
{
"label": "Execute now",
"description": "Start implementing immediately.",
},
],
},
}
)
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "AskUserQuestion"
assert result.tool_calls[0].arguments["options"][1]["label"] == "Execute now"
def test_parse_removes_react_labels(self):
text = '''Thought: I need to check this.
Action:
{"name": "read", "arguments": {"file_path": "test.txt"}}
'''
result = parse_tool_calls(text)
assert "Thought:" not in result.content
assert "Action:" not in result.content
def test_parse_invalid_json_ignored(self):
text = '''
{invalid json here}
'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 0
def test_parse_empty_arguments(self):
text = '''
{"name": "pwd", "arguments": {}}
'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].arguments == {}
def test_parse_parameters_alias(self):
"""Test that 'parameters' is accepted as alias for 'arguments'."""
text = '''
{"name": "read", "parameters": {"file_path": "/tmp/test.txt"}}
'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "read"
assert result.tool_calls[0].arguments == {"file_path": "/tmp/test.txt"}
def test_parse_malformed_closing_tag(self):
"""Test handling of malformed at start."""
text = ''' {"name": "read", "parameters": {"file_path": "test.txt"}}
'''
result = parse_tool_calls(text)
# Should clean up the malformed tags
assert "" not in result.content
def test_parse_cleans_orphaned_tags(self):
"""Test that orphaned tool_call tags are removed from content."""
text = '''Some text more text end'''
result = parse_tool_calls(text)
assert "" not in result.content
assert "" not in result.content
def test_parse_bracketed_calls_format(self):
"""Test parsing [calls tool with: key=value] format."""
text = '''I'll create the file now.
[calls write tool with: file_path=/tmp/test.txt, content="hello world"]
Created the file.'''
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "write"
assert result.tool_calls[0].arguments["file_path"] == "/tmp/test.txt"
assert result.tool_calls[0].arguments["content"] == "hello world"
# Bracketed call should be removed from content
assert "[calls" not in result.content
def test_parse_bracketed_use_format(self):
"""Test parsing [USE tool: key=value] format."""
text = '[USE bash tool: command="ls -la"]'
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "bash"
assert result.tool_calls[0].arguments["command"] == "ls -la"
def test_parse_bracketed_edit_format(self):
"""Test parsing bracketed format with edit tool."""
text = '[calls edit tool with: file_path="test.py", old_string="foo", new_string="bar"]'
result = parse_tool_calls(text)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "edit"
assert result.tool_calls[0].arguments["file_path"] == "test.py"
assert result.tool_calls[0].arguments["old_string"] == "foo"
assert result.tool_calls[0].arguments["new_string"] == "bar"
def test_parse_bracketed_mixed_case_tool_uses_allowed_name(self):
text = '[calls askuserquestion tool with: question="Which path should we take?"]'
result = parse_tool_calls(
text,
allowed_tool_names=["AskUserQuestion", "TodoWrite", "read"],
)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "AskUserQuestion"
assert result.tool_calls[0].arguments == {
"question": "Which path should we take?"
}
def test_parse_bare_json_filters_unknown_tool_when_allowed_names_provided(self):
text = '{"name": "TotallyUnknownTool", "arguments": {"question": "ignored"}}'
result = parse_tool_calls(
text,
allowed_tool_names=["AskUserQuestion", "TodoWrite", "read"],
)
assert result.tool_calls == []
assert "TotallyUnknownTool" in result.content
def test_parse_bare_json_maps_read_file_alias_to_read(self):
text = '{"name": "read_file", "arguments": {"file_path": "/tmp/test.txt"}}'
result = parse_tool_calls(
text,
allowed_tool_names=["read", "write", "patch"],
)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "read"
assert result.tool_calls[0].arguments == {"file_path": "/tmp/test.txt"}
def test_parse_fenced_read_command_into_tool_call(self):
text = "Let me inspect the file first.\n```bash\nread /tmp/test.txt\n```"
result = parse_tool_calls(
text,
allowed_tool_names=["read", "glob", "bash"],
)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "read"
assert result.tool_calls[0].arguments == {"file_path": "/tmp/test.txt"}
def test_parse_fenced_glob_command_into_tool_call(self):
text = "```bash\nglob /tmp/guide/chapters/*.html\n```"
result = parse_tool_calls(
text,
allowed_tool_names=["read", "glob", "bash"],
)
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "glob"
assert result.tool_calls[0].arguments == {
"pattern": "/tmp/guide/chapters/*.html"
}
class TestFormatToolResult:
"""Tests for format_tool_result function."""
def test_format_success(self):
result = format_tool_result("read", "file contents here")
assert "Observation" in result
assert "read" in result
assert "Result" in result
assert "file contents here" in result
def test_format_error(self):
result = format_tool_result("write", "Permission denied", is_error=True)
assert "Observation" in result
assert "write" in result
assert "Error" in result
assert "Permission denied" in result
def test_format_todowrite_compacts_payload(self):
result = format_tool_result(
"TodoWrite",
json.dumps(
{
"old_todos": [
{
"content": "Create index.html",
"active_form": "Creating index.html",
"status": "completed",
}
],
"new_todos": [
{
"content": "Create index.html",
"active_form": "Creating index.html",
"status": "completed",
},
{
"content": "Create installation chapter (02-installation.html)",
"active_form": "Creating installation chapter",
"status": "pending",
},
],
"verification_nudge_needed": False,
"store_path": "/tmp/.loader/todos/active.json",
}
),
)
assert "Observation [TodoWrite]: Result: updated todo list" in result
assert "1 completed" in result
assert "1 pending" in result
assert "next pending: Create installation chapter (02-installation.html)" in result
assert "old_todos" not in result
assert "new_todos" not in result