Refactor write file tool so it is easier for LLMs to use properly.

This commit is contained in:
AI Christianson 2025-02-12 11:51:19 -05:00
parent 589233aaff
commit 264f5025ed
6 changed files with 28 additions and 24 deletions

View File

@ -127,7 +127,7 @@ You must ONLY use ONE of the following functions (these are the ONLY functions t
</available functions>
You may use any of the above functions to complete your job. Use the best one for the current step you are on. Be efficient, avoid getting stuck in repetitive loops, and do not hesitate to call functions which delegate your work to make your life easier.
But you MUST NOT assume tools exist that are not in the above list, e.g. write_file_tool.
But you MUST NOT assume tools exist that are not in the above list, e.g. put_complete_file_contents.
Consider your task done only once you have taken *ALL* the steps required to complete it.
--- EXAMPLE BAD OUTPUTS ---
@ -135,13 +135,13 @@ Consider your task done only once you have taken *ALL* the steps required to com
This tool is not in available functions, so this is a bad tool call:
<example bad output>
write_file_tool(...)
put_complete_file_contents(...)
</example bad output>
This tool call has a syntax error (unclosed parenthesis, quotes), so it is bad:
<example bad output>
write_file_tool("asdf
put_complete_file_contents("asdf
</example bad output>
This tool call is bad because it includes a message as well as backticks:

View File

@ -618,7 +618,7 @@ Important Notes:
- Work incrementally, validating as you go. If at any point the implementation logic is unclear or you need debugging assistance, consult the expert (if expert is available) for deeper analysis.
- Do not add features not explicitly required.
- Only create or modify files directly related to this task.
- Use file_str_replace and write_file_tool for simple file modifications.
- Use file_str_replace and put_complete_file_contents for simple file modifications.
- Delegate to run_programming_task for more complex programming tasks. This is a capable human programmer that can work on multiple files at once.
Testing:
@ -642,7 +642,7 @@ You have often been criticized for:
- Doing changes outside of the specific scoped instructions.
- Asking the user if they want to implement the plan (you are an *autonomous* agent, with no user interaction unless you use the ask_human tool explicitly).
- Not calling tools/functions properly, e.g. leaving off required arguments, calling a tool in a loop, calling tools inappropriately.
- Using run_programming_task to simply write the full contents of files when you could have used write_file_tool instead.
- Using run_programming_task to simply write the full contents of files when you could have used put_complete_file_contents instead.
Instructions:
1. Review the provided base task, plan, and key facts.

View File

@ -30,7 +30,7 @@ from ra_aid.tools.agent import (
request_web_research,
)
from ra_aid.tools.memory import one_shot_completed
from ra_aid.tools.write_file import write_file_tool
from ra_aid.tools.write_file import put_complete_file_contents
# Read-only tools that don't modify system state
@ -72,7 +72,7 @@ def get_read_only_tools(
# Define constant tool groups
READ_ONLY_TOOLS = get_read_only_tools()
MODIFICATION_TOOLS = [run_programming_task, write_file_tool]
MODIFICATION_TOOLS = [run_programming_task, put_complete_file_contents]
COMMON_TOOLS = get_read_only_tools()
EXPERT_TOOLS = [emit_expert_context, ask_expert]
RESEARCH_TOOLS = [

View File

@ -26,7 +26,7 @@ from .research import existing_project_detected, monorepo_detected, ui_detected
from .ripgrep import ripgrep_search
from .shell import run_shell_command
from .web_search_tavily import web_search_tavily
from .write_file import write_file_tool
from .write_file import put_complete_file_contents
__all__ = [
"ask_expert",
@ -48,7 +48,7 @@ __all__ = [
"request_implementation",
"run_programming_task",
"run_shell_command",
"write_file_tool",
"put_complete_file_contents",
"ripgrep_search",
"file_str_replace",
"delete_tasks",

View File

@ -11,14 +11,18 @@ console = Console()
@tool
def write_file_tool(
def put_complete_file_contents(
filepath: str, content: str, encoding: str = "utf-8", verbose: bool = True
) -> Dict[str, any]:
"""Write content to a text file.
"""Write the complete contents of a file, creating it if it doesn't exist.
This tool is specifically for writing the entire contents of a file at once,
not for appending or partial writes.
`filepath` and `content` must ALWAYS be provided.
Args:
filepath: Path to the file to write
content: String content to write to the file
content: Complete string content to write to the file
encoding: File encoding to use (default: utf-8)
verbose: Whether to display a Rich panel with write statistics (default: True)

View File

@ -3,7 +3,7 @@ from unittest.mock import patch
import pytest
from ra_aid.tools.write_file import write_file_tool
from ra_aid.tools.write_file import put_complete_file_contents
@pytest.fixture
@ -19,7 +19,7 @@ def test_basic_write_functionality(temp_test_dir):
test_file = temp_test_dir / "test.txt"
content = "Hello, World!\nTest content"
result = write_file_tool.invoke({"filepath": str(test_file), "content": content})
result = put_complete_file_contents({"filepath": str(test_file), "content": content})
# Verify file contents
assert test_file.read_text() == content
@ -38,7 +38,7 @@ def test_directory_creation(temp_test_dir):
test_file = nested_dir / "test.txt"
content = "Test content"
result = write_file_tool.invoke({"filepath": str(test_file), "content": content})
result = put_complete_file_contents({"filepath": str(test_file), "content": content})
assert test_file.exists()
assert test_file.read_text() == content
@ -51,14 +51,14 @@ def test_different_encodings(temp_test_dir):
content = "Hello 世界" # Mixed ASCII and Unicode
# Test UTF-8
result_utf8 = write_file_tool.invoke(
result_utf8 = put_complete_file_contents(
{"filepath": str(test_file), "content": content, "encoding": "utf-8"}
)
assert result_utf8["success"] is True
assert test_file.read_text(encoding="utf-8") == content
# Test UTF-16
result_utf16 = write_file_tool.invoke(
result_utf16 = put_complete_file_contents(
{"filepath": str(test_file), "content": content, "encoding": "utf-16"}
)
assert result_utf16["success"] is True
@ -71,7 +71,7 @@ def test_permission_error(mock_open_func, temp_test_dir):
mock_open_func.side_effect = PermissionError("Permission denied")
test_file = temp_test_dir / "noperm.txt"
result = write_file_tool.invoke(
result = put_complete_file_contents(
{"filepath": str(test_file), "content": "test content"}
)
@ -86,7 +86,7 @@ def test_io_error(mock_open_func, temp_test_dir):
mock_open_func.side_effect = IOError("IO Error occurred")
test_file = temp_test_dir / "ioerror.txt"
result = write_file_tool.invoke(
result = put_complete_file_contents(
{"filepath": str(test_file), "content": "test content"}
)
@ -99,7 +99,7 @@ def test_empty_content(temp_test_dir):
"""Test writing empty content to a file."""
test_file = temp_test_dir / "empty.txt"
result = write_file_tool.invoke({"filepath": str(test_file), "content": ""})
result = put_complete_file_contents({"filepath": str(test_file), "content": ""})
assert test_file.exists()
assert test_file.read_text() == ""
@ -116,7 +116,7 @@ def test_overwrite_existing_file(temp_test_dir):
# Overwrite with new content
new_content = "New content"
result = write_file_tool.invoke(
result = put_complete_file_contents(
{"filepath": str(test_file), "content": new_content}
)
@ -130,7 +130,7 @@ def test_large_file_write(temp_test_dir):
test_file = temp_test_dir / "large.txt"
content = "Large content\n" * 1000 # Create substantial content
result = write_file_tool.invoke({"filepath": str(test_file), "content": content})
result = put_complete_file_contents({"filepath": str(test_file), "content": content})
assert test_file.exists()
assert test_file.read_text() == content
@ -143,7 +143,7 @@ def test_invalid_path_characters(temp_test_dir):
"""Test handling of invalid path characters."""
invalid_path = temp_test_dir / "invalid\0file.txt"
result = write_file_tool.invoke(
result = put_complete_file_contents(
{"filepath": str(invalid_path), "content": "test content"}
)
@ -161,7 +161,7 @@ def test_write_to_readonly_directory(temp_test_dir):
os.chmod(readonly_dir, 0o444)
try:
result = write_file_tool.invoke(
result = put_complete_file_contents(
{"filepath": str(test_file), "content": "test content"}
)
assert result["success"] is False