feat: Initial library extraction from PIN LLM benchmark
DockerSandbox + LangChain file/shell tools extracted into a standalone package. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
65
docker_agent_sandbox/tools/edit_file.py
Normal file
65
docker_agent_sandbox/tools/edit_file.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""edit_file.py – tool for str_replace editing of files inside the sandbox."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from shlex import quote
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from langchain_core.tools import BaseTool, tool
|
||||
from loguru import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from docker_agent_sandbox.sandbox import DockerSandbox
|
||||
|
||||
|
||||
def make_edit_file_tool(sandbox: "DockerSandbox") -> BaseTool:
|
||||
"""Return an edit_file tool bound to *sandbox*."""
|
||||
|
||||
@tool
|
||||
def edit_file(path: str, old_str: str, new_str: str) -> str:
|
||||
"""
|
||||
Replace the first exact occurrence of *old_str* with *new_str* in *path*.
|
||||
|
||||
This is the standard ``str_replace`` editing primitive: read the file,
|
||||
find the unique snippet you want to change, and supply the replacement.
|
||||
|
||||
Rules:
|
||||
- *old_str* must match **exactly** (including whitespace and indentation).
|
||||
- *old_str* must appear **at least once**; the tool returns an error if it
|
||||
is not found.
|
||||
- If *old_str* appears more than once the tool refuses and asks you to
|
||||
provide more surrounding context to make it unique.
|
||||
- To insert text without removing anything, set *old_str* to a line that
|
||||
will remain and include it verbatim in *new_str* (i.e. keep the anchor
|
||||
line and add your new lines around it).
|
||||
- To delete a block, set *new_str* to an empty string ``""``.
|
||||
|
||||
Returns a confirmation with the number of lines affected, or an error.
|
||||
"""
|
||||
logger.debug("Editing file inside sandbox: {!r}", path)
|
||||
exit_code, content = sandbox.exec(f"cat -- {quote(path)}")
|
||||
if exit_code != 0:
|
||||
return f"[ERROR reading {path!r} for edit] {content.strip()}"
|
||||
|
||||
count = content.count(old_str)
|
||||
if count == 0:
|
||||
return (
|
||||
f"[ERROR] old_str not found in {path!r}. "
|
||||
"Check that whitespace and indentation match exactly."
|
||||
)
|
||||
if count > 1:
|
||||
return (
|
||||
f"[ERROR] old_str appears {count} times in {path!r}. "
|
||||
"Provide more surrounding context to make it unique."
|
||||
)
|
||||
|
||||
new_content = content.replace(old_str, new_str, 1)
|
||||
old_lines = old_str.count("\n") + 1
|
||||
new_lines = new_str.count("\n") + 1 if new_str else 0
|
||||
try:
|
||||
sandbox.write_file(path, new_content)
|
||||
except Exception as exc:
|
||||
return f"[ERROR writing {path!r} after edit] {exc}"
|
||||
return f"[OK] Replaced {old_lines} line(s) with {new_lines} line(s) in {path}"
|
||||
|
||||
return edit_file
|
||||
Reference in New Issue
Block a user