fix: Fix 0 indexing for offset (#4086)

This commit is contained in:
Matthew Zhou
2025-08-21 14:29:51 -07:00
committed by GitHub
parent a4860eb72d
commit c3eefbc3d6
4 changed files with 24 additions and 20 deletions

View File

@@ -21,8 +21,8 @@ async def open_files(agent_state: "AgentState", file_requests: List[FileOpenRequ
Open multiple files with different view ranges:
file_requests = [
FileOpenRequest(file_name="project_utils/config.py", offset=1, length=50), # Lines 1-50
FileOpenRequest(file_name="project_utils/main.py", offset=100, length=100), # Lines 100-199
FileOpenRequest(file_name="project_utils/config.py", offset=0, length=50), # Lines 1-50
FileOpenRequest(file_name="project_utils/main.py", offset=100, length=100), # Lines 101-200
FileOpenRequest(file_name="project_utils/utils.py") # Entire file
]

View File

@@ -11,7 +11,7 @@ class SearchTask(BaseModel):
class FileOpenRequest(BaseModel):
file_name: str = Field(description="Name of the file to open")
offset: Optional[int] = Field(
default=None, description="Optional starting line number (1-indexed). If not specified, starts from beginning of file."
default=None, description="Optional offset for starting line number (0-indexed). If not specified, starts from beginning of file."
)
length: Optional[int] = Field(
default=None, description="Optional number of lines to view from offset (inclusive). If not specified, views to end of file."

View File

@@ -151,16 +151,16 @@ class LettaFileToolExecutor(ToolExecutor):
offset = file_request.offset
length = file_request.length
# Convert 1-indexed offset/length to 0-indexed start/end for LineChunker
# Use 0-indexed offset/length directly for LineChunker
start, end = None, None
if offset is not None or length is not None:
if offset is not None and offset < 1:
raise ValueError(f"Offset for file {file_name} must be >= 1 (1-indexed), got {offset}")
if offset is not None and offset < 0:
raise ValueError(f"Offset for file {file_name} must be >= 0 (0-indexed), got {offset}")
if length is not None and length < 1:
raise ValueError(f"Length for file {file_name} must be >= 1, got {length}")
# Convert to 0-indexed for LineChunker
start = (offset - 1) if offset is not None else None
# Use offset directly as it's already 0-indexed
start = offset if offset is not None else None
if start is not None and length is not None:
end = start + length
else:
@@ -193,7 +193,7 @@ class LettaFileToolExecutor(ToolExecutor):
visible_content=visible_content,
max_files_open=agent_state.max_files_open,
start_line=start + 1 if start is not None else None, # convert to 1-indexed for user display
end_line=end if end is not None else None, # end is already exclusive in slicing, so this is correct
end_line=end if end is not None else None, # end is already exclusive, shows as 1-indexed inclusive
)
opened_files.append(file_name)
@@ -220,10 +220,14 @@ class LettaFileToolExecutor(ToolExecutor):
for req in file_requests:
previous_info = format_previous_range(req.file_name)
if req.offset is not None and req.length is not None:
end_line = req.offset + req.length - 1
file_summaries.append(f"{req.file_name} (lines {req.offset}-{end_line}){previous_info}")
# Display as 1-indexed for user readability: (offset+1) to (offset+length)
start_line = req.offset + 1
end_line = req.offset + req.length
file_summaries.append(f"{req.file_name} (lines {start_line}-{end_line}){previous_info}")
elif req.offset is not None:
file_summaries.append(f"{req.file_name} (lines {req.offset}-end){previous_info}")
# Display as 1-indexed
start_line = req.offset + 1
file_summaries.append(f"{req.file_name} (lines {start_line}-end){previous_info}")
else:
file_summaries.append(f"{req.file_name}{previous_info}")

View File

@@ -424,7 +424,7 @@ def test_agent_uses_open_close_file_correctly(disable_pinecone, client: LettaSDK
assert initial_content_length > 10, f"Expected file content > 10 chars, got {initial_content_length}"
# Ask agent to open the file for a specific range using offset/length
offset, length = 1, 5 # 1-indexed offset, 5 lines
offset, length = 0, 5 # 0-indexed offset, 5 lines
print(f"Requesting agent to open file with offset={offset}, length={length}")
open_response1 = client.agents.messages.create(
agent_id=agent_state.id,
@@ -453,7 +453,7 @@ def test_agent_uses_open_close_file_correctly(disable_pinecone, client: LettaSDK
assert "5: " in old_value, f"Expected line 5 to be present, got: {old_value}"
# Ask agent to open the file for a different range
offset, length = 6, 5 # Different offset, same length
offset, length = 5, 5 # Different offset, same length
open_response2 = client.agents.messages.create(
agent_id=agent_state.id,
messages=[
@@ -482,8 +482,8 @@ def test_agent_uses_open_close_file_correctly(disable_pinecone, client: LettaSDK
assert "10: " in new_value, f"Expected line 10 to be present, got: {new_value}"
print(f"Comparing content ranges:")
print(f" First range (offset=1, length=5): '{old_value}'")
print(f" Second range (offset=6, length=5): '{new_value}'")
print(f" First range (offset=0, length=5): '{old_value}'")
print(f" Second range (offset=5, length=5): '{new_value}'")
assert new_value != old_value, f"Different view ranges should have different content. New: '{new_value}', Old: '{old_value}'"
@@ -703,7 +703,7 @@ def test_view_ranges_have_metadata(disable_pinecone, client: LettaSDKClient, age
assert block.value.startswith("[Viewing file start (out of 100 lines)]")
# Open a specific range using offset/length
offset = 50 # 1-indexed line 50
offset = 49 # 0-indexed for line 50
length = 5 # 5 lines (50-54)
open_response = client.agents.messages.create(
agent_id=agent_state.id,
@@ -960,9 +960,9 @@ def test_open_files_schema_descriptions(disable_pinecone, client: LettaSDKClient
# Check that examples are included
assert "Examples:" in description
assert 'FileOpenRequest(file_name="project_utils/config.py")' in description
assert 'FileOpenRequest(file_name="project_utils/config.py", offset=1, length=50)' in description
assert 'FileOpenRequest(file_name="project_utils/config.py", offset=0, length=50)' in description
assert "# Lines 1-50" in description
assert "# Lines 100-199" in description
assert "# Lines 101-200" in description
assert "# Entire file" in description
assert "close_all_others=True" in description
assert "View specific portions of large files (e.g. functions or definitions)" in description
@@ -1009,7 +1009,7 @@ def test_open_files_schema_descriptions(disable_pinecone, client: LettaSDKClient
# Check offset field
assert "offset" in file_request_properties
offset_prop = file_request_properties["offset"]
expected_offset_desc = "Optional starting line number (1-indexed). If not specified, starts from beginning of file."
expected_offset_desc = "Optional offset for starting line number (0-indexed). If not specified, starts from beginning of file."
assert offset_prop["description"] == expected_offset_desc
assert offset_prop["type"] == "integer"