diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 9a5ccf36..5d1f5c76 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -2,8 +2,10 @@ import re from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions from pr_agent.tools.pr_description import PRDescription +from pr_agent.tools.pr_information_from_user import PRInformationFromUser from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer +from pr_agent.config_loader import settings class PRAgent: @@ -11,8 +13,13 @@ class PRAgent: pass async def handle_request(self, pr_url, request) -> bool: - if any(cmd in request for cmd in ["/review", "/review_pr"]): - await PRReviewer(pr_url).review() + if any(cmd in request for cmd in ["/answer"]): + await PRReviewer(pr_url, is_answer=True).review() + elif any(cmd in request for cmd in ["/review", "/review_pr"]): + if settings.pr_reviewer.ask_and_reflect: + await PRInformationFromUser(pr_url).generate_questions() + else: + await PRReviewer(pr_url).review() elif any(cmd in request for cmd in ["/describe", "/describe_pr"]): await PRDescription(pr_url).describe() elif any(cmd in request for cmd in ["/improve", "/improve_code"]): diff --git a/pr_agent/cli.py b/pr_agent/cli.py index a430b4d3..e11ad677 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -30,7 +30,8 @@ improve / improve_code - Suggest improvements to the code in the PR as pull requ 'ask', 'ask_question', 'describe', 'describe_pr', 'improve', 'improve_code', - 'user_questions'], default='review') + 'user_questions', 'user_answers'], + default='review') parser.add_argument('rest', nargs=argparse.REMAINDER, default=[]) args = parser.parse_args() logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) @@ -60,6 +61,10 @@ improve / improve_code - Suggest improvements to the code in the PR as pull requ print(f"Asking the PR author questions: {args.pr_url}") reviewer = PRInformationFromUser(args.pr_url) asyncio.run(reviewer.generate_questions()) + elif command in ['user_answers']: + print(f"Processing author answers and sending review: {args.pr_url}") + reviewer = PRReviewer(args.pr_url, cli_mode=True, is_answer=True) + asyncio.run(reviewer.review()) else: print(f"Unknown command: {command}") parser.print_help() diff --git a/pr_agent/servers/github_action_runner.py b/pr_agent/servers/github_action_runner.py index ba6ffe9c..a856cc58 100644 --- a/pr_agent/servers/github_action_runner.py +++ b/pr_agent/servers/github_action_runner.py @@ -6,6 +6,7 @@ import re from pr_agent.config_loader import settings from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions from pr_agent.tools.pr_description import PRDescription +from pr_agent.tools.pr_information_from_user import PRInformationFromUser from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer @@ -53,8 +54,13 @@ async def run_action(): pr_url = event_payload.get("issue", {}).get("pull_request", {}).get("url", None) if pr_url: body = comment_body.strip().lower() - if any(cmd in body for cmd in ["/review", "/review_pr"]): - await PRReviewer(pr_url).review() + if any(cmd in body for cmd in ["/answer"]): + await PRReviewer(pr_url, is_answer=True).review() + elif any(cmd in body for cmd in ["/review", "/review_pr", "/answer"]): + if settings.pr_reviewer.ask_and_reflect: + await PRInformationFromUser(pr_url).generate_questions() + else: + await PRReviewer(pr_url).review() elif any(cmd in body for cmd in ["/describe", "/describe_pr"]): await PRDescription(pr_url).describe() elif any(cmd in body for cmd in ["/improve", "/improve_code"]): diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 44355877..01bf0a86 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -2,7 +2,7 @@ model="gpt-4-0613" git_provider="github" publish_review=true -verbosity_level=2 # 0,1,2 +verbosity_level=0 # 0,1,2 [pr_reviewer] require_focused_review=true @@ -10,6 +10,7 @@ require_tests_review=true require_security_review=true num_code_suggestions=3 inline_code_comments = true +ask_and_reflect=false [pr_questions] diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index af6f3ee8..309d95d8 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -108,6 +108,16 @@ Description: '{{description}}' Main language: {{language}} {%- endif %} +{%- if question_str %} +###### +Here are questions to better understand the PR. Use the answers to provide better feedback. + +{{question_str|trim}} + +User answers: +{{answer_str|trim}} +###### +{%- endif %} The PR Git Diff: ``` diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 6a8f33cb..c6e9d9ff 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -15,12 +15,14 @@ from pr_agent.servers.help import bot_help_text, actions_help_text class PRReviewer: - def __init__(self, pr_url: str, cli_mode=False): + def __init__(self, pr_url: str, cli_mode=False, is_answer: bool = False): self.git_provider = get_git_provider()(pr_url) self.main_language = get_main_pr_language( self.git_provider.get_languages(), self.git_provider.get_files() ) + self.is_answer = is_answer + answer_str = question_str = self._get_user_answers() self.ai_handler = AiHandler() self.patches_diff = None self.prediction = None @@ -35,6 +37,9 @@ class PRReviewer: "require_security": settings.pr_reviewer.require_security_review, "require_focused": settings.pr_reviewer.require_focused_review, 'num_code_suggestions': settings.pr_reviewer.num_code_suggestions, + # + 'question_str': question_str, + 'answer_str': answer_str, } self.token_handler = TokenHandler(self.git_provider.pr, self.vars, @@ -118,4 +123,17 @@ class PRReviewer: relevant_line_in_file = d['relevant line in file'].strip() content = d['suggestion content'] - self.git_provider.publish_inline_comment(content, relevant_file, relevant_line_in_file) \ No newline at end of file + self.git_provider.publish_inline_comment(content, relevant_file, relevant_line_in_file) + + def _get_user_answers(self): + answer_str = question_str = "" + if self.is_answer: + discussion_messages = self.git_provider.pr.get_issue_comments() + for message in discussion_messages.reversed: + if "Questions to better understand the PR:" in message.body: + question_str = message.body + elif '/answer' in message.body: + answer_str = message.body + if answer_str and question_str: + break + return question_str, answer_str