feat: add output truncation and offset paging to all large-output tools
- coding_tools: list_files (max_entries=200, sorted), run_command (max_chars=8000, output_offset), grep_search (max_lines=100, offset); added _truncate_output helper for run_command paging - pr_tools: get_pull_request_diff / get_pull_request_patch now truncate at 15k chars with hunk-boundary awareness and char_offset paging; get_pull_request_comments gains limit/offset paging - issue_tools: get_issue_comments gains limit/offset paging - file_tools: get_file_content / get_file_content_with_ref now paginate by line (offset=1, limit=250), matching existing read_file convention; added _paginate_lines helper - research_tools: fetch_url gains char_offset parameter; _smart_truncate now slices from an offset and embeds next char_offset in the footer - gitea_tools facade: all new params threaded through
This commit is contained in:
+85
-7
@@ -5,6 +5,30 @@ from typing import Any
|
||||
from gitea.client import GiteaClient
|
||||
from gitea.models import PullRequestModel, CommentModel
|
||||
|
||||
_MAX_DIFF_CHARS: int = 15_000
|
||||
|
||||
|
||||
def _truncate_diff(
|
||||
text: str,
|
||||
max_chars: int = _MAX_DIFF_CHARS,
|
||||
char_offset: int = 0,
|
||||
) -> str:
|
||||
"""Slice [char_offset : char_offset+max_chars] from text, cutting at a hunk boundary."""
|
||||
total: int = len(text)
|
||||
chunk: str = text[char_offset : char_offset + max_chars]
|
||||
if char_offset > 0 or (char_offset + max_chars) < total:
|
||||
# Try to cut at a diff hunk boundary (@@) for coherence
|
||||
hunk_boundary: int = chunk.rfind("\n@@")
|
||||
if hunk_boundary > int(len(chunk) * 0.6):
|
||||
chunk = chunk[:hunk_boundary]
|
||||
next_offset: int = char_offset + len(chunk)
|
||||
chunk += (
|
||||
f"\n\n[Diff truncated — {total} chars total. "
|
||||
f"Showing chars {char_offset}–{next_offset}. "
|
||||
f"Re-call with char_offset={next_offset} to read more.]"
|
||||
)
|
||||
return chunk
|
||||
|
||||
|
||||
class PRTools:
|
||||
"""Tools for Gitea pull request operations."""
|
||||
@@ -26,10 +50,34 @@ class PRTools:
|
||||
except Exception as e:
|
||||
return f"Error closing pull request: {str(e)}"
|
||||
|
||||
def get_pull_request_comments(self, owner: str, repo: str, pull_number: int) -> str:
|
||||
def get_pull_request_comments(
|
||||
self,
|
||||
owner: str,
|
||||
repo: str,
|
||||
pull_number: int,
|
||||
limit: int = 20,
|
||||
offset: int = 0,
|
||||
) -> str:
|
||||
"""Get comments on a pull request with optional paging.
|
||||
|
||||
Args:
|
||||
limit: Maximum number of comments to return (default 20).
|
||||
offset: Zero-based comment index to start from (default 0).
|
||||
"""
|
||||
try:
|
||||
comments: list[CommentModel] = self._client.get_pull_request_comments(owner, repo, pull_number)
|
||||
return json.dumps([c.model_dump() for c in comments], indent=2)
|
||||
comments: list[CommentModel] = self._client.get_pull_request_comments(
|
||||
owner, repo, pull_number
|
||||
)
|
||||
total: int = len(comments)
|
||||
page: list[CommentModel] = comments[offset : offset + limit]
|
||||
result: str = json.dumps([c.model_dump() for c in page], indent=2)
|
||||
if total > offset + limit:
|
||||
next_offset: int = offset + limit
|
||||
result += (
|
||||
f"\n\n[{total} comments total — showing {offset}–{offset + len(page)}. "
|
||||
f"Re-call with offset={next_offset} to see more.]"
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"Error getting PR comments: {str(e)}"
|
||||
|
||||
@@ -72,15 +120,45 @@ class PRTools:
|
||||
except Exception as e:
|
||||
return f"Error adding label to PR #{pr_number}: {e}"
|
||||
|
||||
def get_pull_request_diff(self, owner: str, repo: str, pull_number: int) -> str:
|
||||
def get_pull_request_diff(
|
||||
self,
|
||||
owner: str,
|
||||
repo: str,
|
||||
pull_number: int,
|
||||
max_chars: int = _MAX_DIFF_CHARS,
|
||||
char_offset: int = 0,
|
||||
) -> str:
|
||||
"""Get the diff of a pull request, with truncation and offset paging.
|
||||
|
||||
Args:
|
||||
max_chars: Maximum characters to return (default 15 000).
|
||||
char_offset: Character offset to start reading from (default 0).
|
||||
Increment by max_chars to page through a large diff.
|
||||
"""
|
||||
try:
|
||||
return self._client.get_pull_request_diff(owner, repo, pull_number)
|
||||
diff: str = self._client.get_pull_request_diff(owner, repo, pull_number)
|
||||
return _truncate_diff(diff, max_chars, char_offset)
|
||||
except Exception as e:
|
||||
return f"Error getting PR diff: {str(e)}"
|
||||
|
||||
def get_pull_request_patch(self, owner: str, repo: str, pull_number: int) -> str:
|
||||
def get_pull_request_patch(
|
||||
self,
|
||||
owner: str,
|
||||
repo: str,
|
||||
pull_number: int,
|
||||
max_chars: int = _MAX_DIFF_CHARS,
|
||||
char_offset: int = 0,
|
||||
) -> str:
|
||||
"""Get the patch of a pull request, with truncation and offset paging.
|
||||
|
||||
Args:
|
||||
max_chars: Maximum characters to return (default 15 000).
|
||||
char_offset: Character offset to start reading from (default 0).
|
||||
Increment by max_chars to page through a large patch.
|
||||
"""
|
||||
try:
|
||||
return self._client.get_pull_request_patch(owner, repo, pull_number)
|
||||
patch: str = self._client.get_pull_request_patch(owner, repo, pull_number)
|
||||
return _truncate_diff(patch, max_chars, char_offset)
|
||||
except Exception as e:
|
||||
return f"Error getting PR patch: {str(e)}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user