From bd0dfc61a95cc8e82de3f43eb2f17f0e0b4c1694 Mon Sep 17 00:00:00 2001 From: codegen-bot Date: Tue, 18 Mar 2025 18:02:42 +0000 Subject: [PATCH 1/2] CG-12243: Add pagination to SearchFilesByNameTool --- src/codegen/extensions/langchain/tools.py | 6 +- .../extensions/tools/search_files_by_name.py | 65 +++++++++++++++++-- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/codegen/extensions/langchain/tools.py b/src/codegen/extensions/langchain/tools.py index e800a9c78..9c38e544b 100644 --- a/src/codegen/extensions/langchain/tools.py +++ b/src/codegen/extensions/langchain/tools.py @@ -1099,6 +1099,8 @@ class SearchFilesByNameInput(BaseModel): """Input for searching files by name pattern.""" pattern: str = Field(..., description="`fd`-compatible glob pattern to search for (e.g. '*.py', 'test_*.py')") + page: int = Field(default=1, description="Page number to return (1-based, default: 1)") + files_per_page: int = Field(default=10, description="Number of files to return per page (default: 10)") class SearchFilesByNameTool(BaseTool): @@ -1117,6 +1119,6 @@ class SearchFilesByNameTool(BaseTool): def __init__(self, codebase: Codebase): super().__init__(codebase=codebase) - def _run(self, pattern: str) -> str: + def _run(self, pattern: str, page: int = 1, files_per_page: int = 10) -> str: """Execute the glob pattern search using fd.""" - return search_files_by_name(self.codebase, pattern).render() + return search_files_by_name(self.codebase, pattern, page=page, files_per_page=files_per_page).render() \ No newline at end of file diff --git a/src/codegen/extensions/tools/search_files_by_name.py b/src/codegen/extensions/tools/search_files_by_name.py index bc595a25c..c245df4e5 100644 --- a/src/codegen/extensions/tools/search_files_by_name.py +++ b/src/codegen/extensions/tools/search_files_by_name.py @@ -20,6 +20,18 @@ class SearchFilesByNameResultObservation(Observation): files: list[str] = Field( description="List of matching file paths", ) + page: int = Field( + description="Current page number (1-based)", + ) + total_pages: int = Field( + description="Total number of pages available", + ) + total_files: int = Field( + description="Total number of files with matches", + ) + files_per_page: int = Field( + description="Number of files shown per page", + ) str_template: ClassVar[str] = "Found {total} files matching pattern: {pattern}" @@ -27,18 +39,40 @@ class SearchFilesByNameResultObservation(Observation): def total(self) -> int: return len(self.files) + def render_as_string(self) -> str: + """Render the search results in a readable format.""" + if self.status == "error": + return f"Error: {self.error}" + + if self.total_files == 0: + return f"No files found matching pattern: {self.pattern}" + + lines = [f"Found {self.total_files} files matching pattern: {self.pattern} (showing page {self.page} of {self.total_pages})"] + + for file_path in self.files: + lines.append(f"- {file_path}") + + return "\n".join(lines) + def search_files_by_name( codebase: Codebase, pattern: str, + page: int = 1, + files_per_page: int = 10, ) -> SearchFilesByNameResultObservation: """Search for files by name pattern in the codebase. Args: codebase: The codebase to search in pattern: Glob pattern to search for (e.g. "*.py", "test_*.py") + page: Page number to return (1-based, default: 1) + files_per_page: Number of files to return per page (default: 10) """ try: + # Get all matching files + all_files = [] + if shutil.which("fd") is None: logger.warning("fd is not installed, falling back to find") results = subprocess.check_output( @@ -46,7 +80,7 @@ def search_files_by_name( cwd=codebase.repo_path, timeout=30, ) - files = [path.removeprefix("./") for path in results.decode("utf-8").strip().split("\n")] if results.strip() else [] + all_files = [path.removeprefix("./") for path in results.decode("utf-8").strip().split("\n")] if results.strip() else [] else: logger.info(f"Searching for files with pattern: {pattern}") @@ -55,12 +89,31 @@ def search_files_by_name( cwd=codebase.repo_path, timeout=30, ) - files = results.decode("utf-8").strip().split("\n") if results.strip() else [] + all_files = results.decode("utf-8").strip().split("\n") if results.strip() else [] + + # Sort files alphabetically + all_files.sort() + + # Calculate pagination + total_files = len(all_files) + total_pages = (total_files + files_per_page - 1) // files_per_page if total_files > 0 else 1 + + # Adjust page number if out of bounds + page = max(1, min(page, total_pages)) + + # Get the current page of results + start_idx = (page - 1) * files_per_page + end_idx = start_idx + files_per_page + paginated_files = all_files[start_idx:end_idx] return SearchFilesByNameResultObservation( status="success", pattern=pattern, - files=files, + files=paginated_files, + page=page, + total_pages=total_pages, + total_files=total_files, + files_per_page=files_per_page, ) except Exception as e: @@ -69,4 +122,8 @@ def search_files_by_name( error=f"Error searching files: {e!s}", pattern=pattern, files=[], - ) + page=page, + total_pages=0, + total_files=0, + files_per_page=files_per_page, + ) \ No newline at end of file From 26571c633697d6685cbad320b461635666b18754 Mon Sep 17 00:00:00 2001 From: "codegen-sh[bot]" <131295404+codegen-sh[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 18:03:28 +0000 Subject: [PATCH 2/2] Automated pre-commit update --- src/codegen/extensions/langchain/tools.py | 2 +- .../extensions/tools/search_files_by_name.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/codegen/extensions/langchain/tools.py b/src/codegen/extensions/langchain/tools.py index 9c38e544b..cdb7621d1 100644 --- a/src/codegen/extensions/langchain/tools.py +++ b/src/codegen/extensions/langchain/tools.py @@ -1121,4 +1121,4 @@ def __init__(self, codebase: Codebase): def _run(self, pattern: str, page: int = 1, files_per_page: int = 10) -> str: """Execute the glob pattern search using fd.""" - return search_files_by_name(self.codebase, pattern, page=page, files_per_page=files_per_page).render() \ No newline at end of file + return search_files_by_name(self.codebase, pattern, page=page, files_per_page=files_per_page).render() diff --git a/src/codegen/extensions/tools/search_files_by_name.py b/src/codegen/extensions/tools/search_files_by_name.py index c245df4e5..f7625d4b2 100644 --- a/src/codegen/extensions/tools/search_files_by_name.py +++ b/src/codegen/extensions/tools/search_files_by_name.py @@ -48,10 +48,10 @@ def render_as_string(self) -> str: return f"No files found matching pattern: {self.pattern}" lines = [f"Found {self.total_files} files matching pattern: {self.pattern} (showing page {self.page} of {self.total_pages})"] - + for file_path in self.files: lines.append(f"- {file_path}") - + return "\n".join(lines) @@ -72,7 +72,7 @@ def search_files_by_name( try: # Get all matching files all_files = [] - + if shutil.which("fd") is None: logger.warning("fd is not installed, falling back to find") results = subprocess.check_output( @@ -90,17 +90,17 @@ def search_files_by_name( timeout=30, ) all_files = results.decode("utf-8").strip().split("\n") if results.strip() else [] - + # Sort files alphabetically all_files.sort() - + # Calculate pagination total_files = len(all_files) total_pages = (total_files + files_per_page - 1) // files_per_page if total_files > 0 else 1 - + # Adjust page number if out of bounds page = max(1, min(page, total_pages)) - + # Get the current page of results start_idx = (page - 1) * files_per_page end_idx = start_idx + files_per_page @@ -126,4 +126,4 @@ def search_files_by_name( total_pages=0, total_files=0, files_per_page=files_per_page, - ) \ No newline at end of file + )