Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
b1119ed
fix(resolver): Prioritize env var for runtime image and fix workflow …
openhands-agent Apr 25, 2025
9b086b7
feat: Implement code reviewer core logic and tests
openhands-agent Apr 25, 2025
4ed0ae1
feat(code-reviewer): Refactor async calls, add tests, fix errors
openhands-agent Apr 26, 2025
5185463
fix(workflow): Remove comment inside multi-line if condition
openhands-agent Apr 26, 2025
dad2b2f
fix(workflow): Install openhands from current branch
openhands-agent Apr 26, 2025
1990711
Revert "fix(workflow): Install openhands from current branch"
openhands-agent Apr 26, 2025
ed64992
fix(workflow): Install openhands from feat/code-reviewer-impl branch
openhands-agent Apr 26, 2025
9d5c1e9
fix: Handle SecretStr token and pass output file path
openhands-agent Apr 26, 2025
03093be
fix: Ensure output dir exists early and add job permissions
openhands-agent Apr 26, 2025
dc48f12
fix: Use correct inputs for App token permissions
openhands-agent Apr 26, 2025
bffe65d
feat: Add debug logging for GraphQL request
openhands-agent Apr 26, 2025
9a53b38
fix: Set log level to DEBUG in review_pr
openhands-agent Apr 26, 2025
ed21f7e
fix: Force logger reconfiguration to DEBUG
openhands-agent Apr 26, 2025
225c940
fix: Log response body on HTTPStatusError
openhands-agent Apr 26, 2025
ef745b1
chore: Remove debug logging for token tracing
openhands-agent Apr 26, 2025
5dd46be
fix(workflow): Run review script from workspace and set output dir
openhands-agent Apr 26, 2025
bbbd5f1
fix(workflow): Clean up /tmp/repo before cloning
openhands-agent Apr 26, 2025
6427347
fix(workflow): Use GITHUB_WORKSPACE instead of cloning repo in script
openhands-agent Apr 26, 2025
eb74954
fix(workflow): Ensure SANDBOX_BASE_CONTAINER_IMAGE has fallback value
openhands-agent Apr 26, 2025
f7ac98c
fix(workflow): Prioritize input then env var for SANDBOX_BASE_CONTAIN…
openhands-agent Apr 26, 2025
e0e0a54
fix(workflow): Pass SANDBOX_BASE_CONTAINER_IMAGE to review step env
openhands-agent Apr 26, 2025
1980e0a
refactor(workflow): Use step outputs for SANDBOX_BASE_CONTAINER_IMAGE
openhands-agent Apr 26, 2025
f968317
fix(workflow): Correctly check workflow env var in set_vars step
openhands-agent Apr 26, 2025
60a5532
chore(workflow): Add step to debug base image variable values
openhands-agent Apr 26, 2025
4a5ada8
fix(workflow): Directly use vars context for base image fallback
openhands-agent Apr 26, 2025
09f17d1
fix(review): Use stricter check before closing runtime
openhands-agent Apr 26, 2025
ef09028
chore(workflow): Add step to dump runtime Docker logs
openhands-agent Apr 26, 2025
9c537dd
fix(review): Catch TypeError during runtime cleanup
openhands-agent Apr 26, 2025
5be9f16
fix(review): Fix post-comment args and add loop logging
openhands-agent Apr 26, 2025
8ef61ca
fix(review): Remove fake_user_response_fn for non-interactive agent
openhands-agent Apr 26, 2025
c0ccd59
fix(review): Treat AWAITING_USER_INPUT as success if review generated
openhands-agent Apr 26, 2025
2644035
fix(review): Temporarily disable MAIN event subscribers during agent run
openhands-agent Apr 26, 2025
ac39c69
refactor(review): Align review_pr structure with resolve_issue
openhands-agent Apr 26, 2025
e8c21f6
refactor: Align review_pr and resolve_issue structure
openhands-agent Apr 26, 2025
9227071
Refactor: Simplify event stream handling in review_pr.py
openhands-agent Apr 26, 2025
6589574
Fix: Add missing parse_pr_url method to GithubPRHandler
openhands-agent Apr 26, 2025
5c01ace
Fix: Use get_converted_issues instead of get_issue_info in review_pr.py
openhands-agent Apr 26, 2025
c640613
fix(review): Implement checkout_pr and fix JSON serialization
openhands-agent Apr 26, 2025
529f178
fix(review): Improve JSON serialization robustness
openhands-agent Apr 26, 2025
94f10b0
fix(review): Correct prompt path and instruct agent to finish
openhands-agent Apr 26, 2025
6101ae7
fix(review): Handle AgentFinishAction and Issue serialization
openhands-agent Apr 26, 2025
cfd6f2f
fix(review): Update prompt to prevent markdown in JSON output
openhands-agent Apr 26, 2025
a6912ae
feat(code-reviewer): Modify prompt and parsing for git diff and finis…
openhands-agent Apr 26, 2025
4f7addd
fix(code-reviewer): Use dict access for issue in prompt
openhands-agent Apr 26, 2025
8c0a256
fix(code-reviewer): Improve error serialization and add logging for i…
openhands-agent Apr 26, 2025
b408b08
fix(code-review): Fix type errors and pre-commit issues
openhands-agent Apr 26, 2025
4877d5b
fix(code-review): Add git fetch instruction to prompt
openhands-agent Apr 26, 2025
f2ccdf4
fix(code-review): Reinforce JSON output format in prompt
openhands-agent Apr 26, 2025
26c3e74
fix(code-review): Parse review JSON from final_thought instead of mes…
openhands-agent Apr 26, 2025
e77e668
fix(code-review): Remove await from synchronous runtime.close() call
openhands-agent Apr 26, 2025
e53b88e
fix(code-review): Use json_default serializer for ReviewerOutput
openhands-agent Apr 26, 2025
b9e2a3d
fix(reviewer): Write output to file and fix workflow args
openhands-agent Apr 26, 2025
27ca7bb
fix(ci): Pass --output-file arg to review_pr.py
openhands-agent Apr 26, 2025
31445a1
fix(reviewer): Handle escaped JSON in final_thought and multi-line ou…
openhands-agent Apr 26, 2025
abb5253
feat: add debug logging for github token in post_review
openhands-agent Apr 26, 2025
c634a25
fix: correct type hint for token in GithubPRHandler
openhands-agent Apr 26, 2025
20e17ca
fix: use get_secret_value() for Authorization header
openhands-agent Apr 26, 2025
91241fa
Revert "fix: use get_secret_value() for Authorization header"
openhands-agent Apr 26, 2025
3b48c3a
Revert "fix: correct type hint for token in GithubPRHandler"
openhands-agent Apr 26, 2025
7499c74
refactor: remove SecretStr usage for token in post_review_comments
openhands-agent Apr 26, 2025
6dc1b19
chore: remove debug logging for token auth
openhands-agent Apr 26, 2025
92a25ae
refactor: remove redundant check in post_comments
openhands-agent Apr 26, 2025
66f7e85
refactor(code-reviewer): Remove GitLab support and fix pre-commit errors
openhands-agent Apr 26, 2025
2df1c35
feat(code-reviewer): Set default review level=line, depth=deep
openhands-agent Apr 26, 2025
a551be6
fix(workflow): Remove explicit review level/depth args to use defaults
openhands-agent Apr 26, 2025
03cad6b
fix(workflow): Set default review level/depth in workflow inputs
openhands-agent Apr 26, 2025
8d00d26
fix(workflow): Correct fallbacks for REVIEW_LEVEL/DEPTH env vars
openhands-agent Apr 26, 2025
b1f554b
fix(workflow): Restore explicit args for review level/depth
openhands-agent Apr 26, 2025
c0fda3f
feat(code-review): Emphasize adherence to review level/depth in prompt
openhands-agent Apr 26, 2025
a310542
refactor(code-review): Remove argparse defaults for level/depth
openhands-agent Apr 26, 2025
3e71219
fix(code-review): Pass workflow inputs directly to review_pr.py args
openhands-agent Apr 26, 2025
102dc1a
fix(workflow): Add missing line continuation for review_pr args
openhands-agent Apr 26, 2025
7c77d88
fix(code-review): Instruct agent to use AgentFinishAction.final_thought
openhands-agent Apr 26, 2025
00aec31
fix(code-review): Refine prompt for final_thought and truncate cmd logs
openhands-agent Apr 26, 2025
6da71ba
fix(code-review): Correct prompt to use finish tool message arg
openhands-agent Apr 26, 2025
ec2993e
fix(code-review): Remove fake_user_response_fn for non-interactive run
openhands-agent Apr 26, 2025
8b89ebb
fix(code-review): Log full thoughts and finish action message
openhands-agent Apr 26, 2025
fd71841
fix(code-review): Import AgentThinkAction for logging
openhands-agent Apr 26, 2025
40e7de9
refactor(code-review): Revert custom logging for thoughts/finish actions
openhands-agent Apr 26, 2025
a8760b1
feat(code-review): Add detailed instructions for git diff parsing to …
openhands-agent Apr 26, 2025
07b4189
feat(code-review): Require agent to verify line numbers against check…
openhands-agent Apr 26, 2025
273ec44
feat(code-review): Improve line verification logic in prompt
openhands-agent Apr 26, 2025
34945b2
fix(review): Refactor review_pr.py structure and fix pre-commit errors
openhands-agent Apr 26, 2025
d6893ff
feat(review): Update prompt to improve line number accuracy and preve…
openhands-agent Apr 26, 2025
cdf6541
feat(review): Add --llm-num-retries argument
openhands-agent Apr 26, 2025
6dd7fcd
chore: Upgrade litellm to 1.67.2
openhands-agent Apr 26, 2025
78c69f6
fix(codeact): Set wait_for_response=False for non-tool-call messages
openhands-agent Apr 26, 2025
f6d8805
ci(review): Pass --llm-num-retries to review_pr script
openhands-agent Apr 26, 2025
0e3b260
Revert "fix(codeact): Set wait_for_response=False for non-tool-call m…
openhands-agent Apr 26, 2025
5b9270e
fix: Use fake_user_response_fn to handle AWAITING_USER_INPUT
openhands-agent Apr 26, 2025
cde008b
fix(ci): Add missing line continuation in review_pr call
openhands-agent Apr 26, 2025
545bdba
feat: Add --llm-temperature argument and set to 2.0 in CI
openhands-agent Apr 26, 2025
4a4e1e8
feat: Implement conditional temperature retry for code review
openhands-agent Apr 26, 2025
e5b2236
Revert "feat: Implement conditional temperature retry for code review"
openhands-agent Apr 26, 2025
d10618c
Revert "Revert "feat: Implement conditional temperature retry for cod…
openhands-agent Apr 26, 2025
2babfec
feat: Increase temperature to 2.0 on LLMNoResponseError retry
openhands-agent Apr 26, 2025
d15d261
fix: Refine AWAITING_USER_INPUT handler message
openhands-agent Apr 26, 2025
69cd0d9
docs(prompt): Remove diff-based line number calculation instructions
openhands-agent Apr 26, 2025
7f5db79
docs(prompt): Explicitly forbid using diff for line numbers
openhands-agent Apr 26, 2025
7b86889
Fix: Remove unicode_escape decoding for agent final_thought JSON parsing
openhands-agent Apr 26, 2025
ba39b5c
Feat: Update code review prompt to avoid duplicates and ensure action…
openhands-agent Apr 26, 2025
cb7f452
Fix: Parse finish message instead of thought for review JSON
openhands-agent Apr 26, 2025
9839700
Revert "Fix: Parse finish message instead of thought for review JSON"
openhands-agent Apr 26, 2025
379352d
Fix: Add logging and strip() for parsing final_thought JSON
openhands-agent Apr 26, 2025
bc30ffb
Refactor: Remove temperature=2.0 retry logic
openhands-agent Apr 27, 2025
7a88984
feat(code-reviewer): Update basic-review prompt
openhands-agent Apr 27, 2025
16729e1
fix(code-reviewer): Use diff position for GitHub comments
openhands-agent Apr 27, 2025
7f40648
refactor(code-reviewer): Add detailed logging to _map_line_to_position
openhands-agent Apr 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 369 additions & 0 deletions .github/workflows/openhands-code-reviewer.yml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion .github/workflows/openhands-resolver.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ jobs:
else
echo "Using default GITHUB_TOKEN"
echo "AUTH_TOKEN=${{ github.token }}" >> $GITHUB_ENV
fi
- name: Log Auth Token Source
run: |
if [ -n "${{ steps.generate-token.outputs.token }}" ]; then
Expand Down Expand Up @@ -234,7 +235,7 @@ jobs:

echo "MAX_ITERATIONS=${{ inputs.max_iterations || 50 }}" >> $GITHUB_ENV
echo "SANDBOX_ENV_GITHUB_TOKEN=${{ env.AUTH_TOKEN }}" >> $GITHUB_ENV
echo "SANDBOX_ENV_BASE_CONTAINER_IMAGE=${{ inputs.base_container_image }}" >> $GITHUB_ENV
echo "SANDBOX_BASE_CONTAINER_IMAGE=${{ inputs.base_container_image }}" >> $GITHUB_ENV

# Set branch variables
echo "TARGET_BRANCH=${{ inputs.target_branch || 'main' }}" >> $GITHUB_ENV
Expand Down
2 changes: 1 addition & 1 deletion dev_config/python/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repos:
hooks:
- id: mypy
additional_dependencies:
[types-requests, types-setuptools, types-pyyaml, types-toml]
[types-requests, types-setuptools, types-pyyaml, types-toml, types-aiofiles]
entry: mypy --config-file dev_config/python/mypy.ini openhands/
always_run: true
pass_filenames: false
Empty file.
155 changes: 155 additions & 0 deletions openhands/code_reviewer/post_review_comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import argparse
import asyncio
import json
import os

from openhands.code_reviewer.reviewer_output import ReviewerOutput
from openhands.core.logger import openhands_logger as logger
from openhands.integrations.service_types import ProviderType
from openhands.resolver.interfaces.github import GithubPRHandler


def get_pr_handler(
owner: str,
repo: str,
token: str | None,
platform: ProviderType,
) -> GithubPRHandler: # Return specific type now
"""Get the GitHub PR handler. Raises error for other platforms."""
if platform != ProviderType.GITHUB:
raise ValueError(
f'Unsupported platform for code review comments: {platform}. Only GitHub is supported.'
)

gh_token = token or os.environ.get('GITHUB_TOKEN')
if not gh_token:
raise ValueError('GitHub token is required for GitHub PR handler')

return GithubPRHandler(token=gh_token, owner=owner, repo=repo)


def post_comments(
output_file: str,
token: str | None,
selected_repo: str,
pr_number: int,
):
from openhands.code_reviewer.reviewer_output import ReviewComment

"""Reads review output and posts comments to the PR."""
logger.info(f'Reading review output from: {output_file}')
try:
with open(output_file, 'r') as f:
# Read the entire file content
file_content = f.read()
if not file_content:
logger.error(f'Output file is empty: {output_file}')
return
output_data = json.loads(file_content)
# Manually construct ReviewComment objects
comments_data = output_data.pop(
'comments', []
) # Get comments list, remove from dict
comments_objects = [ReviewComment(**c) for c in comments_data]
# Construct ReviewerOutput, passing the objects list
review_output = ReviewerOutput(**output_data, comments=comments_objects)
except FileNotFoundError:
logger.error(f'Output file not found: {output_file}')
return
except json.JSONDecodeError:
logger.error(f'Failed to decode JSON from output file: {output_file}')
return
except Exception as e:
logger.error(f'Error reading or parsing output file {output_file}: {e}')
return

if not review_output.success:
logger.error(f'Review generation failed. Error: {review_output.error}')
# Optionally post a general failure comment? For now, just log.
return

if not review_output.comments:
logger.warning('Review was successful, but no comments were generated.')
# Optionally post a comment indicating review completed with no findings?
return

logger.info(f'Successfully parsed {len(review_output.comments)} comments.')

try:
owner, repo = selected_repo.split('/')
except ValueError:
logger.error(f'Invalid repository format: {selected_repo}. Use owner/repo.')
return

# Assume GitHub platform
platform = ProviderType.GITHUB
try:
pr_handler = get_pr_handler(owner, repo, token, platform)
except ValueError as e: # Catch specific error from get_pr_handler
logger.error(f'Configuration error getting PR handler: {e}')
return

logger.info(
f'Posting {len(review_output.comments)} comments to PR #{pr_number} on {platform.value}...'
)

# Post comments using the handler
# The handler interface might need adjustment if post_review doesn't exist
# or takes different arguments. Assuming a method like post_review(pr_number, comments)
# Check if the handler has the post_review method
if not hasattr(pr_handler, 'post_review'):
logger.error(f'{type(pr_handler).__name__} does not have a post_review method.')
return
comments_to_post = review_output.comments
try:
asyncio.run(
pr_handler.post_review(pr_number=pr_number, comments=comments_to_post)
)
except Exception: # Catch errors during comment posting
logger.exception(
f'Failed to post comments to PR #{pr_number}'
) # Use logger.exception for stack trace
return # Exit if posting fails

logger.info(f'Successfully posted comments to PR #{pr_number}.')


def main():
parser = argparse.ArgumentParser(description='Post review comments to a PR.')
parser.add_argument(
'--output-file',
type=str,
required=True,
help='Path to the review_output.jsonl file.',
)
parser.add_argument(
'--selected-repo',
type=str,
required=True,
help='Repository where the PR exists in the format `owner/repo`.',
)
parser.add_argument(
'--pr-number',
type=int,
required=True,
help='Pull Request number to post comments to.',
)
parser.add_argument(
'--token',
type=str,
default=None,
help='Platform token (GitHub PAT or GitLab access token). Reads from env vars (GITHUB_TOKEN/GITLAB_TOKEN) if not provided.',
)

args = parser.parse_args()

post_comments(
output_file=args.output_file,
token=args.token,
selected_repo=args.selected_repo,
pr_number=args.pr_number,
)


if __name__ == '__main__':
main()
56 changes: 56 additions & 0 deletions openhands/code_reviewer/prompts/review/basic-review.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
You are an AI code reviewer. Your task is to review the following pull request for the repository located in /workspace.
An environment with the repository checked out at the PR's head commit is available for you to analyze the code.

# Pull Request Details
Title: {{ pr_data.title }}
{% if pr_data.body %}
Body:
{{ pr_data.body }}
{% endif %}

# Review Task
First, ensure the latest changes are fetched using `git fetch origin`. Then, analyze the code changes between the base branch (`{{ pr_data['base']['ref'] }}`) and the head branch (`{{ pr_data['head']['ref'] }}`) using git commands (e.g., `git diff origin/{{ pr_data['base']['ref'] }}...origin/{{ pr_data['head']['ref'] }}`). Base your review on the following parameters:
- Review Level: `{{ review_level }}` (Specifies the granularity: 'line' for specific lines, 'file' for overall file changes, 'pr' for a high-level summary)
- Review Depth: `{{ review_depth }}` (Specifies the thoroughness: 'quick' for obvious issues, 'medium' for standard checks, 'deep' for in-depth analysis including potential bugs and security concerns)
**It is crucial that you strictly adhere to the specified Review Level and Review Depth.**

When reviewing changes:
- Identify the file path from the `git diff` output (usually the line starting with `+++ b/`). This is the path in the head commit.
- Comments should only be placed on lines that exist in the head commit (lines starting with `+` or ` ` within the diff hunk). Do not comment on removed lines (starting with `-`).
- **Check Existing Comments:** Before adding a comment, consider if similar feedback has already been provided by other reviewers on this PR. Avoid adding duplicate comments.
- **Write Actionable Feedback:** Ensure your comments are constructive and actionable. Suggest specific improvements or changes rather than just explaining what the code does.
- **Line Number Determination and Verification:** Line numbers for comments MUST be determined **exclusively** by reading the file content from the `/workspace` directory (which is checked out to the head commit). Use `cat <file_path>` to get the content and `grep -n` (or manual counting in the `cat` output) to find the line number of the *exact* code you want to comment on. **Do NOT use the `git diff` output to determine or calculate line numbers.** This is unreliable. The line number reported by `grep -n` on the `/workspace` file is the **only** valid source for the `line` field in your comment. If the exact line text appears multiple times, use the surrounding code context (from the `cat` output) to identify the correct instance. The line numbers you provide MUST correspond to the line numbers in the head commit version.


{% if repo_instruction %}
# Repository Guidelines/Instructions
Please also consider the following repository-specific guidelines during your review:
{{ repo_instruction }}
{% endif %}

# Output Format
Your final action **MUST** be the `finish` tool call.
- The `message` argument of this tool call **MUST** contain **ONLY** a single, raw JSON list containing review comment objects. It must NOT contain any other text, explanations, or markdown formatting.
- You can include any summary or explanation of your review process in the `thought` that accompanies the `finish` tool call.

Each comment object in the JSON list should have the following structure:
- `path`: (string) The full path to the file being commented on, relative to the repository root (e.g., "openhands/core/config.py").
- `comment`: (string) The text of your review comment.
- `line`: (integer) The line number in the *head commit version* of the file the comment refers to, determined and verified as described above. Required for all comments unless `review_level` is 'pr'.
- `code_snippet`: (string) The exact line(s) of code from the head commit version that the comment refers to. Include 1-3 lines for context if helpful. Required for all comments unless `review_level` is 'pr'.
- `line_number_justification`: (string) A brief explanation of how the `line` number was determined using file content verification (e.g., "Verified line 42 with `grep -n 'exact code line' /workspace/src/utils/parser.py`"). Required for all comments unless `review_level` is 'pr'.

Example structure of the JSON list (this exact string goes into the `message` argument of the `finish` tool call):
`[{"path": "src/utils/parser.py", "line": 42, "code_snippet": "... code line(s) ...", "line_number_justification": "...", "comment": "..."}, {"path": "src/main.py", "comment": "..."}]`

IMPORTANT:
- Focus your review on the changes between the base branch (`{{ pr_data['base']['ref'] }}`) and the head branch (`{{ pr_data['head']['ref'] }}`).
- Adhere strictly to the specified JSON output format for your final response.
- The `message` argument of your `finish` tool call **MUST** contain **ONLY** the raw JSON list string. No extra text, no markdown.
- Any explanatory text belongs in the accompanying `thought`, NOT the `message` argument.
- Do NOT attempt to modify any files. Your role is only to review.
- Do NOT ask for human help or clarification. Provide the review based on the information given.
- Use the `finish` tool call with the JSON review in the `message` argument as your **very last step** to signal completion.
- If no issues are found, the `message` argument should contain the exact string `[]`.
- You are running in a non-interactive environment. Do NOT ask questions or wait for user input. Proceed directly to the `finish` action with the JSON review when your analysis is complete.
- Do not use any other action other than `finish` to output the review.
Loading
Loading