"""edit_file.py – tool for str_replace editing of files inside the sandbox.""" from __future__ import annotations 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. """ _MAX_EDIT_BYTES = 1_000_000 # 1 MB logger.debug("Editing file inside sandbox: {!r}", path) try: data = sandbox.read_file(path) except (FileNotFoundError, IsADirectoryError, RuntimeError) as exc: return f"[ERROR reading {path!r} for edit] {exc}" if len(data) > _MAX_EDIT_BYTES: return ( f"[ERROR] {path!r} is {len(data)} bytes; edit_file only supports files " f"up to {_MAX_EDIT_BYTES} bytes." ) content = data.decode("utf-8", errors="replace") 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