diff --git a/openhands/resolver/__init__.py b/openhands/resolver/__init__.py index 260c070a890d..7e0dc0e843c3 100644 --- a/openhands/resolver/__init__.py +++ b/openhands/resolver/__init__.py @@ -1 +1 @@ -__version__ = "0.3.1" +__version__ = "0.13.1" diff --git a/openhands/resolver/issue_definitions.py b/openhands/resolver/issue_definitions.py index b001ba840d22..5b9ac0157667 100644 --- a/openhands/resolver/issue_definitions.py +++ b/openhands/resolver/issue_definitions.py @@ -146,7 +146,7 @@ def get_converted_issues(self, comment_id: int | None = None) -> list[GithubIssu return converted_issues def get_instruction(self, issue: GithubIssue, prompt_template: str, repo_instruction: str | None = None) -> tuple[str, list[str]]: - """Generate instruction for the agent""" + """Generate instruction for the agent.""" # Format thread comments if they exist thread_context = "" if issue.thread_comments: @@ -164,7 +164,6 @@ def get_instruction(self, issue: GithubIssue, prompt_template: str, repo_instruc def guess_success(self, issue: GithubIssue, history: list[Event], llm_config: LLMConfig) -> tuple[bool, None | list[bool], str]: """Guess if the issue is fixed based on the history and the issue description.""" - last_message = history[-1].message # Include thread comments in the prompt if they exist issue_context = issue.body @@ -203,19 +202,21 @@ def __init__(self, owner: str, repo: str, token: str): def __download_pr_metadata(self, pull_number: int, comment_id: int | None = None) -> tuple[list[str], list[int], list[str], list[ReviewThread], list[str]]: - - """ - Run a GraphQL query against the GitHub API for information on - 1. unresolved review comments - 2. referenced issues the pull request would close + """Run a GraphQL query against the GitHub API for information. + + Retrieves information about: + 1. unresolved review comments + 2. referenced issues the pull request would close - Args: - query: The GraphQL query as a string. - variables: A dictionary of variables for the query. - token: Your GitHub personal access token. + Args: + pull_number: The number of the pull request to query. + comment_id: Optional ID of a specific comment to focus on. + query: The GraphQL query as a string. + variables: A dictionary of variables for the query. + token: Your GitHub personal access token. - Returns: - The JSON response from the GitHub API. + Returns: + The JSON response from the GitHub API. """ # Using graphql as REST API doesn't indicate resolved status for review comments # TODO: grabbing the first 10 issues, 100 review threads, and 100 coments; add pagination to retrieve all @@ -452,7 +453,7 @@ def get_converted_issues(self, comment_id: int | None = None) -> list[GithubIssu def get_instruction(self, issue: GithubIssue, prompt_template: str, repo_instruction: str | None = None) -> tuple[str, list[str]]: - """Generate instruction for the agent""" + """Generate instruction for the agent.""" template = jinja2.Template(prompt_template) images = [] @@ -497,7 +498,7 @@ def get_instruction(self, issue: GithubIssue, prompt_template: str, repo_instruc def _check_feedback_with_llm(self, prompt: str, llm_config: LLMConfig) -> tuple[bool, str]: - """Helper function to check feedback with LLM and parse response""" + """Helper function to check feedback with LLM and parse response.""" response = litellm.completion( model=llm_config.model, messages=[{"role": "user", "content": prompt}], @@ -513,7 +514,7 @@ def _check_feedback_with_llm(self, prompt: str, llm_config: LLMConfig) -> tuple[ return False, f"Failed to decode answer from LLM response: {answer}" def _check_review_thread(self, review_thread: ReviewThread, issues_context: str, last_message: str, llm_config: LLMConfig) -> tuple[bool, str]: - """Check if a review thread's feedback has been addressed""" + """Check if a review thread's feedback has been addressed.""" files_context = json.dumps(review_thread.files, indent=4) with open(os.path.join(os.path.dirname(__file__), "prompts/guess_success/pr-feedback-check.jinja"), 'r') as f: @@ -529,7 +530,7 @@ def _check_review_thread(self, review_thread: ReviewThread, issues_context: str, return self._check_feedback_with_llm(prompt, llm_config) def _check_thread_comments(self, thread_comments: list[str], issues_context: str, last_message: str, llm_config: LLMConfig) -> tuple[bool, str]: - """Check if thread comments feedback has been addressed""" + """Check if thread comments feedback has been addressed.""" thread_context = "\n---\n".join(thread_comments) with open(os.path.join(os.path.dirname(__file__), "prompts/guess_success/pr-thread-check.jinja"), 'r') as f: @@ -544,7 +545,7 @@ def _check_thread_comments(self, thread_comments: list[str], issues_context: str return self._check_feedback_with_llm(prompt, llm_config) def _check_review_comments(self, review_comments: list[str], issues_context: str, last_message: str, llm_config: LLMConfig) -> tuple[bool, str]: - """Check if review comments feedback has been addressed""" + """Check if review comments feedback has been addressed.""" review_context = "\n---\n".join(review_comments) with open(os.path.join(os.path.dirname(__file__), "prompts/guess_success/pr-review-check.jinja"), 'r') as f: @@ -560,7 +561,6 @@ def _check_review_comments(self, review_comments: list[str], issues_context: str def guess_success(self, issue: GithubIssue, history: list[Event], llm_config: LLMConfig) -> tuple[bool, None | list[bool], str]: """Guess if the issue is fixed based on the history and the issue description.""" - last_message = history[-1].message issues_context = json.dumps(issue.closing_issues, indent=4) success_list = [] diff --git a/openhands/resolver/resolve_all_issues.py b/openhands/resolver/resolve_all_issues.py index 86b4708ea6d3..c07b79c33a34 100644 --- a/openhands/resolver/resolve_all_issues.py +++ b/openhands/resolver/resolve_all_issues.py @@ -65,16 +65,17 @@ async def resolve_issues( repo: Github repository to resolve issues in form of `owner/repo`. token: Github token to access the repository. username: Github username to access the repository. - max_iterations: Maximum number of iterations to run + max_iterations: Maximum number of iterations to run. limit_issues: Limit the number of issues to resolve. num_workers: Number of workers to use for parallel processing. output_dir: Output directory to write the results. + llm_config: Configuration for the language model. runtime_container_image: Container image to use. prompt_template: Prompt template to use. + issue_type: Type of issue to resolve (issue or pr). repo_instruction: Repository instruction to use. issue_numbers: List of issue numbers to resolve. """ - issue_handler = issue_handler_factory(issue_type, owner, repo, token) # Load dataset diff --git a/openhands/resolver/resolve_issue.py b/openhands/resolver/resolve_issue.py index 7ed8a39fb0e6..0a24c9dd6988 100644 --- a/openhands/resolver/resolve_issue.py +++ b/openhands/resolver/resolve_issue.py @@ -318,14 +318,17 @@ async def resolve_issue( repo: Github repository to resolve issues in form of `owner/repo`. token: Github token to access the repository. username: Github username to access the repository. - max_iterations: Maximum number of iterations to run + max_iterations: Maximum number of iterations to run. output_dir: Output directory to write the results. + llm_config: Configuration for the language model. runtime_container_image: Container image to use. prompt_template: Prompt template to use. + issue_type: Type of issue to resolve (issue or pr). repo_instruction: Repository instruction to use. issue_number: Issue number to resolve. + comment_id: Optional ID of a specific comment to focus on. + reset_logger: Whether to reset the logger for multiprocessing. """ - issue_handler = issue_handler_factory(issue_type, owner, repo, token) # Load dataset diff --git a/openhands/resolver/send_pull_request.py b/openhands/resolver/send_pull_request.py index 1dd466c6812a..662d72c8754c 100644 --- a/openhands/resolver/send_pull_request.py +++ b/openhands/resolver/send_pull_request.py @@ -341,7 +341,6 @@ def update_existing_pull_request( comment_message: The main message to post as a comment on the PR. additional_message: The additional messages to post as a comment on the PR in json list format. """ - # Set up headers and base URL for GitHub API headers = { "Authorization": f"token {github_token}", diff --git a/tests/unit/resolver/test_pr_handler_guess_success.py b/tests/unit/resolver/test_pr_handler_guess_success.py index 11e1b53a6647..35ab5e20264f 100644 --- a/tests/unit/resolver/test_pr_handler_guess_success.py +++ b/tests/unit/resolver/test_pr_handler_guess_success.py @@ -7,7 +7,7 @@ from openhands.core.config import LLMConfig def test_guess_success_review_threads_litellm_call(): - """Test that the litellm.completion() call for review threads contains the expected content""" + """Test that the litellm.completion() call for review threads contains the expected content.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -83,7 +83,7 @@ def test_guess_success_review_threads_litellm_call(): assert 'Last message from AI agent:\n' + history[0].content in second_prompt def test_guess_success_thread_comments_litellm_call(): - """Test that the litellm.completion() call for thread comments contains the expected content""" + """Test that the litellm.completion() call for thread comments contains the expected content.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -144,7 +144,7 @@ def test_guess_success_thread_comments_litellm_call(): assert 'Last message from AI agent:\n' + history[0].content in prompt def test_check_feedback_with_llm(): - """Test the _check_feedback_with_llm helper function""" + """Test the _check_feedback_with_llm helper function.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -188,7 +188,7 @@ def test_check_feedback_with_llm(): assert (success, explanation) == case['expected'] def test_check_review_thread(): - """Test the _check_review_thread helper function""" + """Test the _check_review_thread helper function.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -236,7 +236,7 @@ def test_check_review_thread(): assert explanation == 'Changes look good' def test_check_thread_comments(): - """Test the _check_thread_comments helper function""" + """Test the _check_thread_comments helper function.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -284,7 +284,7 @@ def test_check_thread_comments(): assert explanation == 'Changes look good' def test_check_review_comments(): - """Test the _check_review_comments helper function""" + """Test the _check_review_comments helper function.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token') @@ -332,7 +332,7 @@ def test_check_review_comments(): assert explanation == 'Changes look good' def test_guess_success_review_comments_litellm_call(): - """Test that the litellm.completion() call for review comments contains the expected content""" + """Test that the litellm.completion() call for review comments contains the expected content.""" # Create a PR handler instance handler = PRHandler('test-owner', 'test-repo', 'test-token')