53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
"""read_file.py – tool for reading 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_read_file_tool(sandbox: "DockerSandbox") -> BaseTool:
|
||
"""Return a read_file tool bound to *sandbox*."""
|
||
|
||
@tool
|
||
def read_file(path: str, offset: int = 0, length: int = 5000) -> str:
|
||
"""
|
||
Read a file at *path*.
|
||
|
||
*path* can be absolute (``/tmp/re-agent/result.csv``) or relative to the
|
||
working directory.
|
||
|
||
*offset* is the number of bytes to skip from the start of the file.
|
||
*length* is the maximum number of bytes to return. If the file is
|
||
longer than ``offset + length``, the output is trimmed and a summary
|
||
line is appended showing how many bytes were omitted.
|
||
|
||
Returns the (possibly trimmed) file contents as text, or an error message.
|
||
"""
|
||
logger.debug(
|
||
"Reading file inside sandbox: {} offset={} length={}", path, offset, length
|
||
)
|
||
try:
|
||
data = sandbox.read_file(path)
|
||
except (FileNotFoundError, IsADirectoryError, RuntimeError) as exc:
|
||
return f"[ERROR reading {path!r}] {exc}"
|
||
|
||
total = len(data)
|
||
chunk = data[offset : offset + length]
|
||
text = chunk.decode("utf-8", errors="replace")
|
||
|
||
suffix = ""
|
||
if offset + length < total:
|
||
remaining = total - (offset + length)
|
||
suffix = f"\n[... {remaining} more bytes not shown (total {total} bytes). Use offset/length to read further.]"
|
||
elif offset > 0 or total > length:
|
||
suffix = f"\n[File total: {total} bytes, showing {len(chunk)} bytes from offset {offset}.]"
|
||
return text + suffix
|
||
|
||
return read_file
|