Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions app/tools/read_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ def spec():
"type": "function",
"function": {
"name": "read_file",
"description": "Read and return the contents of a file",
"description": "Read and return the contents of a file. Supports optional offset and size parameters for partial reads.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "The path to the file to read"
},
"offset": {
"type": "integer",
"description": "Byte offset to start reading from (default: 0)"
},
"size": {
"type": "integer",
"description": "Maximum number of bytes to read (default: read to end of file)"
}
},
"required": ["file_path"]
Expand All @@ -23,12 +31,20 @@ def spec():
}

@staticmethod
def call(file_path: str) -> str:
log.info(f"read_file, file_path: {file_path}")
def call(file_path: str, offset: int = 0, size: int | None = None) -> str:
log.info(f"read_file, file_path: {file_path}, offset: {offset}, size: {size}")

if offset < 0:
return "Error: offset must be non-negative"
if size is not None and size < 0:
return "Error: size must be non-negative"

try:
Comment thread
Rikul marked this conversation as resolved.
with open(file_path, encoding="utf-8") as f:
return f.read()
with open(file_path, "rb") as f:
if offset:
f.seek(offset)
data = f.read(size) if size is not None else f.read()
return data.decode("utf-8", errors="replace")
except FileNotFoundError:
return f"Error: file {file_path} does not exist"
except Exception as e:
Expand Down
43 changes: 43 additions & 0 deletions tests/test_read_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,46 @@ def test_read_file_multiline(tmp_path):
f = tmp_path / "multi.txt"
f.write_text(content, encoding="utf-8")
assert read_file(str(f)) == content


def test_read_file_with_offset(tmp_path):
f = tmp_path / "offset.txt"
f.write_text("hello world", encoding="utf-8")
assert read_file(str(f), offset=6) == "world"


def test_read_file_with_size(tmp_path):
f = tmp_path / "size.txt"
f.write_text("hello world", encoding="utf-8")
assert read_file(str(f), size=5) == "hello"


def test_read_file_with_offset_and_size(tmp_path):
f = tmp_path / "both.txt"
f.write_text("hello world", encoding="utf-8")
assert read_file(str(f), offset=3, size=4) == "lo w"


def test_read_file_offset_beyond_end(tmp_path):
f = tmp_path / "short.txt"
f.write_text("hi", encoding="utf-8")
assert read_file(str(f), offset=100) == ""


def test_read_file_size_larger_than_remaining(tmp_path):
f = tmp_path / "small.txt"
f.write_text("hello", encoding="utf-8")
assert read_file(str(f), offset=3, size=100) == "lo"
Comment thread
Rikul marked this conversation as resolved.


def test_read_file_negative_offset_or_size(tmp_path):
f = tmp_path / "test.txt"
f.write_text("hello", encoding="utf-8")
assert "Error" in read_file(str(f), offset=-1)
assert "Error" in read_file(str(f), size=-1)


def test_read_file_utf8_split(tmp_path):
f = tmp_path / "utf8.txt"
f.write_bytes(b"abc\xf0\x9f\x9a\x80def")
assert read_file(str(f), offset=3, size=2) == "\ufffd"
Loading