From 1746b5a08284eb6fb68ccdd183bb322cb9451a19 Mon Sep 17 00:00:00 2001 From: AntiTopQuark Date: Tue, 26 Nov 2024 17:14:32 +0800 Subject: [PATCH] feat:support pr review comment reply (#498) feat:support pr comment reply --- server/agent/prompts/pull_request.py | 54 ++++++++++++++++++++++ server/event_handler/pull_request.py | 68 +++++++++++++++++++++++++++- server/github_app/handlers.py | 7 ++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/server/agent/prompts/pull_request.py b/server/agent/prompts/pull_request.py index 1c28ae6e..d3c7078f 100644 --- a/server/agent/prompts/pull_request.py +++ b/server/agent/prompts/pull_request.py @@ -55,6 +55,55 @@ - Respect the language of the PR’s title and description when providing summaries and comments (e.g., English or Chinese). """ +PR_REVIEW_COMMENT_PROMPT = """ +# Purpose +You are tasked with automatically responding to comments that in Pull Request (PR) review comments. Your goal is to provide clear, accurate, and helpful responses based on the content of the comments and your knowledge. + +# Guidelines +1. **Answer Questions**: + - If the comment includes a question, provide a concise and accurate answer using the context of the PR review comment. + - If additional information is needed and cannot be derived from the comment, politely request clarification. + +2. **Provide Explanations**: + - If the user is asking for an explanation regarding the review comment (e.g., "Why is this a problem?"), explain the reasoning in a simple and constructive manner. + - Use examples or references if necessary, but do not fabricate facts. + +3. **Acknowledge Feedback**: + - If the comment contains feedback or thanks, acknowledge it politely before proceeding with your response. + +4. **Avoid Over-Commitment**: + - Do not promise or attempt to resolve issues outside the scope of PR review comments. + - If the comment raises a valid concern but requires changes outside the diff scope, suggest opening a new issue or bringing it to the maintainers' attention (without creating a new issue yourself). + +5. **Adhere to Language**: + - Must use the same language as the user's comment for consistency. + - Avoid overly technical jargon unless the user is already using it. + +6. **Closing Statement**: + - End with the following wording, adjusted to the language used in the conversation: + "If you have more questions or need further clarification, feel free to reply and @mention me for assistance." + +# Input +pr_number: {pr_number} +pr_content: {pr_content} + +# Response Structure +- **Acknowledgement**: Acknowledge the comment, e.g., "Thank you for pointing this out." +- **Answer**: Provide a direct and clear response or explanation to the question or feedback. +- **Closing Statement**: Always end with the predefined closing statement mentioned above. + +# Constraints +- Do not create new PRs, issues, or tasks under any circumstances. +- Do not fabricate facts. Use your knowledge and context to assist as much as possible. +- Must use the same language as the user's comment for consistency. +- If the question cannot be answered based on the given context, politely explain the limitation and request clarification or additional details. +- If you don’t have any useful conclusions, use your own knowledge to assist the user as much as possible, but do not fabricate facts. +- Never attempt to create a new issue or PR under any circumstances; instead, express an apology. +- If you don’t have any useful conclusions, use your own knowledge to assist the user as much as possible, but do not fabricate facts. +- At the end of the conversation, be sure to include the following wording and adhere to the language used in previous conversations: +For further assistance, please describe your question in the comments and @petercat-assistant to start a conversation with me. +""" + def get_role_prompt(repo_name: str, pull_number: int, title: str, description: str): return PULL_REQUEST_ROLE.format( @@ -63,3 +112,8 @@ def get_role_prompt(repo_name: str, pull_number: int, title: str, description: s title=title, description=description, ) + +def generate_pr_review_comment_prompt(pr_number: str, pr_content: str): + return PR_REVIEW_COMMENT_PROMPT.format( + pr_number=pr_number, pr_content=pr_content + ) diff --git a/server/event_handler/pull_request.py b/server/event_handler/pull_request.py index c574f703..4eb06366 100644 --- a/server/event_handler/pull_request.py +++ b/server/event_handler/pull_request.py @@ -14,12 +14,14 @@ from utils.path_to_hunk import convert_patch_to_hunk from utils.random_str import random_str -from agent.prompts.pull_request import get_role_prompt +from agent.prompts.pull_request import ( + get_role_prompt, + generate_pr_review_comment_prompt, +) from agent.qa_chat import agent_chat from core.dao.repositoryConfigDAO import RepositoryConfigDAO from petercat_utils.data_class import ChatData, Message, TextContentBlock - def file_match(filename: str, patterns: List[str]): return any(fnmatch.fnmatch(filename, pattern) for pattern in patterns) @@ -129,3 +131,65 @@ async def execute(self): except GithubException as e: print(f"处理 GitHub 请求时出错:{e}") return {"success": False, "error": str(e)} + +class PullRequestReviewCommentEventHandler(PullRequestEventHandler): + def not_mentioned_me(self): + return "@petercat-assistant" not in self.event["comment"]["body"] + + async def execute(self): + try: + print(f"actions={self.event['action']},sender={self.event['sender']}") + if self.event["sender"]["type"] == "Bot": + return {"success": True} + if self.event["action"] in ["created", "edited"]: + if self.not_mentioned_me(): + return {"success": True} + + comment_id = self.event["comment"]['id'] + pr, diff, repo = self.get_pull_request() + file_diff = self.get_file_diff(diff) + + pr_review_comments = pr.get_review_comments() + + messages = [ + Message( + role="assistant" if comment.user.type == "Bot" else "user", + content=[TextContentBlock(type="text", text=comment.body)], + ) + for comment in pr_review_comments + ] + + pr_content = f""" + ### Pr Title + {pr.title} + ### Pr Description + {pr.body} + ### File Diff + {file_diff} + """ + + prompt = generate_pr_review_comment_prompt( + pr_number=pr.number, + pr_content=pr_content, + ) + + repository_config = RepositoryConfigDAO() + repo_config = repository_config.get_by_repo_name(repo.full_name) + if repo_config.robot_id: + bot = get_bot_by_id(repo_config.robot_id) + + analysis_result = await agent_chat( + ChatData( + prompt=f"{bot.prompt}\n{prompt}", + messages=messages, + bot_id=repo_config.robot_id, + ), + self.auth, + bot, + ) + + pr.create_review_comment_reply(comment_id, analysis_result["output"]) + + except GithubException as e: + print(f"处理 GitHub 请求时出错:{e}") + return {"success": False, "error": str(e)} diff --git a/server/github_app/handlers.py b/server/github_app/handlers.py index 0ea983a7..b7268a02 100644 --- a/server/github_app/handlers.py +++ b/server/github_app/handlers.py @@ -3,7 +3,10 @@ from petercat_utils import get_env_variable from github import Auth -from event_handler.pull_request import PullRequestEventHandler +from event_handler.pull_request import ( + PullRequestEventHandler, + PullRequestReviewCommentEventHandler, +) from event_handler.discussion import ( DiscussionEventHandler, DiscussionCommentEventHandler, @@ -21,6 +24,7 @@ def get_handler( IssueEventHandler, DiscussionEventHandler, DiscussionCommentEventHandler, + PullRequestReviewCommentEventHandler, None, ]: handlers = { @@ -29,6 +33,7 @@ def get_handler( "issue_comment": IssueCommentEventHandler, "discussion": DiscussionEventHandler, "discussion_comment": DiscussionCommentEventHandler, + "pull_request_review_comment":PullRequestReviewCommentEventHandler, } return ( handlers.get(event)(payload=payload, auth=auth, installation_id=installation_id)