From 30dfda5e1bb35527098245cdbcc3f56d00f467d9 Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Fri, 6 Sep 2024 13:23:10 -0700 Subject: [PATCH 1/3] tools Co-authored-by: Ram Senthamarai --- src/seer/automation/autofix/tools.py | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/seer/automation/autofix/tools.py b/src/seer/automation/autofix/tools.py index 2fa507802..ae3f715eb 100644 --- a/src/seer/automation/autofix/tools.py +++ b/src/seer/automation/autofix/tools.py @@ -1,4 +1,6 @@ +import fnmatch import logging +import os import textwrap from langfuse.decorators import observe @@ -176,6 +178,40 @@ def keyword_search( return result_str + @observe(name="File Search") + @ai_track(description="File Search") + def file_search( + self, + filename: str, + repo_name: str | None = None, + ): + """ + Given a filename with extension returns the list of locations where a file with the name is found. + """ + repo_client = self.context.get_repo_client(repo_name=repo_name) + all_paths = repo_client.get_index_file_set() + found = [path for path in all_paths if os.path.basename(path) == filename] + if len(found) == 0: + return f"no file with name {filename} found in repository" + return ",".join(found) + + @observe(name="File Search Wildcard") + @ai_track(description="File Search Wildcard") + def file_search_wildcard( + self, + pattern: str, + repo_name: str | None = None, + ): + """ + Given a filename pattern with wildcards, returns the list of file paths that match the pattern. + """ + repo_client = self.context.get_repo_client(repo_name=repo_name) + all_paths = repo_client.get_index_file_set() + found = [path for path in all_paths if fnmatch.fnmatch(path, pattern)] + if len(found) == 0: + return f"No files matching pattern '{pattern}' found in repository" + return "\n".join(found) + def get_tools(self): tools = [ FunctionTool( @@ -245,6 +281,40 @@ def get_tools(self): }, ], ), + FunctionTool( + name="file_search", + fn=self.file_search, + description="Searches for a file in the codebase.", + parameters=[ + { + "name": "filename", + "type": "string", + "description": "The file to search for.", + }, + { + "name": "repo_name", + "type": "string", + "description": "Optional name of the repository to search in if you know it.", + }, + ], + ), + FunctionTool( + name="file_search_wildcard", + fn=self.file_search_wildcard, + description="Searches for files in a folder using a wildcard pattern.", + parameters=[ + { + "name": "pattern", + "type": "string", + "description": "The wildcard pattern to match files.", + }, + { + "name": "repo_name", + "type": "string", + "description": "Optional name of the repository to search in if you know it.", + }, + ], + ), ] return tools From c2071b4824e8ff11804a7de1511410d13b39e048 Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Fri, 6 Sep 2024 13:30:12 -0700 Subject: [PATCH 2/3] test file search --- .../automation/autofix/test_autofix_tools.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 tests/automation/autofix/test_autofix_tools.py diff --git a/tests/automation/autofix/test_autofix_tools.py b/tests/automation/autofix/test_autofix_tools.py new file mode 100644 index 000000000..a189b88a0 --- /dev/null +++ b/tests/automation/autofix/test_autofix_tools.py @@ -0,0 +1,79 @@ +from unittest.mock import MagicMock + +import pytest + +from seer.automation.autofix.tools import BaseTools + + +@pytest.fixture +def autofix_tools(): + context = MagicMock() + return BaseTools(context) + + +class TestFileSearch: + def test_file_search_found(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = { + "src/file1.py", + "tests/file2.py", + "src/subfolder/file2.py", + } + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + result = autofix_tools.file_search("file2.py") + assert result == "tests/file2.py,src/subfolder/file2.py" + + def test_file_search_not_found(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = { + "src/file1.py", + "tests/file2.py", + "src/subfolder/file3.py", + } + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + result = autofix_tools.file_search("nonexistent.py") + assert result == "no file with name nonexistent.py found in repository" + + def test_file_search_with_repo_name(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = {"src/file1.py"} + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + autofix_tools.file_search("file1.py", repo_name="test_repo") + autofix_tools.context.get_repo_client.assert_called_once_with(repo_name="test_repo") + + +class TestFileSearchWildcard: + def test_file_search_wildcard_found(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = { + "src/file1.py", + "tests/test_file1.py", + "src/subfolder/file2.py", + } + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + result = autofix_tools.file_search_wildcard("*.py") + assert result == "src/file1.py\ntests/test_file1.py\nsrc/subfolder/file2.py" + + def test_file_search_wildcard_not_found(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = { + "src/file1.py", + "tests/test_file1.py", + "src/subfolder/file2.py", + } + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + result = autofix_tools.file_search_wildcard("*.js") + assert result == "No files matching pattern '*.js' found in repository" + + def test_file_search_wildcard_with_repo_name(self, autofix_tools): + mock_repo_client = MagicMock() + mock_repo_client.get_index_file_set.return_value = {"src/file1.py"} + autofix_tools.context.get_repo_client.return_value = mock_repo_client + + autofix_tools.file_search_wildcard("*.py", repo_name="test_repo") + autofix_tools.context.get_repo_client.assert_called_once_with(repo_name="test_repo") From 1c1adf5d4e20837fb2ba138c4b94a7b9cc38a2bb Mon Sep 17 00:00:00 2001 From: "jenn.muengtaweepongsa" Date: Fri, 6 Sep 2024 13:55:01 -0700 Subject: [PATCH 3/3] fix tests --- src/seer/automation/autofix/tools.py | 6 ++++++ tests/automation/autofix/test_autofix_tools.py | 16 ++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/seer/automation/autofix/tools.py b/src/seer/automation/autofix/tools.py index ae3f715eb..f7e523fbd 100644 --- a/src/seer/automation/autofix/tools.py +++ b/src/seer/automation/autofix/tools.py @@ -193,6 +193,9 @@ def file_search( found = [path for path in all_paths if os.path.basename(path) == filename] if len(found) == 0: return f"no file with name {filename} found in repository" + + found = sorted(found) + return ",".join(found) @observe(name="File Search Wildcard") @@ -210,6 +213,9 @@ def file_search_wildcard( found = [path for path in all_paths if fnmatch.fnmatch(path, pattern)] if len(found) == 0: return f"No files matching pattern '{pattern}' found in repository" + + found = sorted(found) + return "\n".join(found) def get_tools(self): diff --git a/tests/automation/autofix/test_autofix_tools.py b/tests/automation/autofix/test_autofix_tools.py index a189b88a0..a3b9aae96 100644 --- a/tests/automation/autofix/test_autofix_tools.py +++ b/tests/automation/autofix/test_autofix_tools.py @@ -12,7 +12,7 @@ def autofix_tools(): class TestFileSearch: - def test_file_search_found(self, autofix_tools): + def test_file_search_found(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = { "src/file1.py", @@ -22,9 +22,9 @@ def test_file_search_found(self, autofix_tools): autofix_tools.context.get_repo_client.return_value = mock_repo_client result = autofix_tools.file_search("file2.py") - assert result == "tests/file2.py,src/subfolder/file2.py" + assert result == "src/subfolder/file2.py,tests/file2.py" - def test_file_search_not_found(self, autofix_tools): + def test_file_search_not_found(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = { "src/file1.py", @@ -36,7 +36,7 @@ def test_file_search_not_found(self, autofix_tools): result = autofix_tools.file_search("nonexistent.py") assert result == "no file with name nonexistent.py found in repository" - def test_file_search_with_repo_name(self, autofix_tools): + def test_file_search_with_repo_name(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = {"src/file1.py"} autofix_tools.context.get_repo_client.return_value = mock_repo_client @@ -46,7 +46,7 @@ def test_file_search_with_repo_name(self, autofix_tools): class TestFileSearchWildcard: - def test_file_search_wildcard_found(self, autofix_tools): + def test_file_search_wildcard_found(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = { "src/file1.py", @@ -56,9 +56,9 @@ def test_file_search_wildcard_found(self, autofix_tools): autofix_tools.context.get_repo_client.return_value = mock_repo_client result = autofix_tools.file_search_wildcard("*.py") - assert result == "src/file1.py\ntests/test_file1.py\nsrc/subfolder/file2.py" + assert result == "src/file1.py\nsrc/subfolder/file2.py\ntests/test_file1.py" - def test_file_search_wildcard_not_found(self, autofix_tools): + def test_file_search_wildcard_not_found(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = { "src/file1.py", @@ -70,7 +70,7 @@ def test_file_search_wildcard_not_found(self, autofix_tools): result = autofix_tools.file_search_wildcard("*.js") assert result == "No files matching pattern '*.js' found in repository" - def test_file_search_wildcard_with_repo_name(self, autofix_tools): + def test_file_search_wildcard_with_repo_name(self, autofix_tools: BaseTools): mock_repo_client = MagicMock() mock_repo_client.get_index_file_set.return_value = {"src/file1.py"} autofix_tools.context.get_repo_client.return_value = mock_repo_client