mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-02 11:50:37 +08:00
Improve handling of tagging and Github app user interaction
This commit is contained in:
@ -10,11 +10,16 @@ class PRAgent:
|
|||||||
self.installation_id = installation_id
|
self.installation_id = installation_id
|
||||||
|
|
||||||
async def handle_request(self, pr_url, request):
|
async def handle_request(self, pr_url, request):
|
||||||
if 'please review' in request.lower():
|
if 'please review' in request.lower() or 'review' == request.lower().strip() or len(request) == 0:
|
||||||
reviewer = PRReviewer(pr_url, self.installation_id)
|
reviewer = PRReviewer(pr_url, self.installation_id)
|
||||||
await reviewer.review()
|
await reviewer.review()
|
||||||
|
|
||||||
elif 'please answer' in request.lower():
|
else:
|
||||||
question = re.split(r'(?i)please answer', request)[1].strip()
|
if "please answer" in request.lower():
|
||||||
|
question = re.split(r'(?i)please answer', request)[1].strip()
|
||||||
|
elif request.lower().strip().startswith("answer"):
|
||||||
|
question = re.split(r'(?i)answer', request)[1].strip()
|
||||||
|
else:
|
||||||
|
question = request
|
||||||
answerer = PRQuestions(pr_url, question, self.installation_id)
|
answerer = PRQuestions(pr_url, question, self.installation_id)
|
||||||
await answerer.answer()
|
await answerer.answer()
|
||||||
|
@ -24,6 +24,7 @@ class GithubProvider:
|
|||||||
self.repo = None
|
self.repo = None
|
||||||
self.pr_num = None
|
self.pr_num = None
|
||||||
self.pr = None
|
self.pr = None
|
||||||
|
self.github_user_id = None
|
||||||
if pr_url:
|
if pr_url:
|
||||||
self.set_pr(pr_url)
|
self.set_pr(pr_url)
|
||||||
|
|
||||||
@ -42,6 +43,8 @@ class GithubProvider:
|
|||||||
|
|
||||||
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
|
def publish_comment(self, pr_comment: str, is_temporary: bool = False):
|
||||||
response = self.pr.create_issue_comment(pr_comment)
|
response = self.pr.create_issue_comment(pr_comment)
|
||||||
|
if hasattr(response, "user") and hasattr(response.user, "login"):
|
||||||
|
self.github_user_id = response.user.login
|
||||||
response.is_temporary = is_temporary
|
response.is_temporary = is_temporary
|
||||||
if not hasattr(self.pr, 'comments_list'):
|
if not hasattr(self.pr, 'comments_list'):
|
||||||
self.pr.comments_list = []
|
self.pr.comments_list = []
|
||||||
@ -109,6 +112,14 @@ class GithubProvider:
|
|||||||
def get_pr_branch(self):
|
def get_pr_branch(self):
|
||||||
return self.pr.head.ref
|
return self.pr.head.ref
|
||||||
|
|
||||||
|
def get_user_id(self):
|
||||||
|
if not self.github_user_id:
|
||||||
|
try:
|
||||||
|
self.github_user_id = self.github_client.get_user().login
|
||||||
|
except Exception as e:
|
||||||
|
logging.exception(f"Failed to get user id, error: {e}")
|
||||||
|
return self.github_user_id
|
||||||
|
|
||||||
def get_notifications(self, since: datetime):
|
def get_notifications(self, since: datetime):
|
||||||
deployment_type = settings.get("GITHUB.DEPLOYMENT_TYPE", "user")
|
deployment_type = settings.get("GITHUB.DEPLOYMENT_TYPE", "user")
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ async def handle_request(body):
|
|||||||
if "comment" not in body:
|
if "comment" not in body:
|
||||||
return {}
|
return {}
|
||||||
comment_body = body.get("comment", {}).get("body", None)
|
comment_body = body.get("comment", {}).get("body", None)
|
||||||
if "says 'Please" in comment_body:
|
if 'sender' in body and 'login' in body['sender'] and 'bot' in body['sender']['login']:
|
||||||
return {}
|
return {}
|
||||||
if "issue" not in body and "pull_request" not in body["issue"]:
|
if "issue" not in body and "pull_request" not in body["issue"]:
|
||||||
return {}
|
return {}
|
||||||
|
@ -7,6 +7,7 @@ import aiohttp
|
|||||||
|
|
||||||
from pr_agent.agent.pr_agent import PRAgent
|
from pr_agent.agent.pr_agent import PRAgent
|
||||||
from pr_agent.config_loader import settings
|
from pr_agent.config_loader import settings
|
||||||
|
from pr_agent.git_providers import get_git_provider
|
||||||
|
|
||||||
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
||||||
NOTIFICATION_URL = "https://api.github.com/notifications"
|
NOTIFICATION_URL = "https://api.github.com/notifications"
|
||||||
@ -21,6 +22,8 @@ def now() -> str:
|
|||||||
async def polling_loop():
|
async def polling_loop():
|
||||||
since = [now()]
|
since = [now()]
|
||||||
last_modified = [None]
|
last_modified = [None]
|
||||||
|
git_provider = get_git_provider()()
|
||||||
|
user_id = git_provider.get_user_id()
|
||||||
try:
|
try:
|
||||||
deployment_type = settings.github.deployment_type
|
deployment_type = settings.github.deployment_type
|
||||||
token = settings.github.user_token
|
token = settings.github.user_token
|
||||||
@ -58,12 +61,18 @@ async def polling_loop():
|
|||||||
async with session.get(latest_comment, headers=headers) as comment_response:
|
async with session.get(latest_comment, headers=headers) as comment_response:
|
||||||
if comment_response.status == 200:
|
if comment_response.status == 200:
|
||||||
comment = await comment_response.json()
|
comment = await comment_response.json()
|
||||||
|
if hasattr(comment, 'user') and hasattr(comment['user'], 'login'):
|
||||||
|
if comment['user']['login'] == user_id:
|
||||||
|
continue
|
||||||
comment_body = comment['body'] if 'body' in comment else ''
|
comment_body = comment['body'] if 'body' in comment else ''
|
||||||
commenter_github_user = comment['user']['login'] if 'user' in comment else ''
|
commenter_github_user = comment['user']['login'] if 'user' in comment else ''
|
||||||
logging.info(f"Commenter: {commenter_github_user}\nComment: {comment_body}")
|
logging.info(f"Commenter: {commenter_github_user}\nComment: {comment_body}")
|
||||||
if comment_body.strip().startswith("@"):
|
user_tag = "@" + user_id
|
||||||
agent = PRAgent()
|
if user_tag not in comment_body:
|
||||||
await agent.handle_request(pr_url, comment_body)
|
continue
|
||||||
|
rest_of_comment = comment_body.split(user_tag)[1].strip()
|
||||||
|
agent = PRAgent()
|
||||||
|
await agent.handle_request(pr_url, rest_of_comment)
|
||||||
elif response.status != 304:
|
elif response.status != 304:
|
||||||
print(f"Failed to fetch notifications. Status code: {response.status}")
|
print(f"Failed to fetch notifications. Status code: {response.status}")
|
||||||
|
|
||||||
|
@ -82,8 +82,18 @@ class PRReviewer:
|
|||||||
logging.error("Unable to decode JSON response from AI")
|
logging.error("Unable to decode JSON response from AI")
|
||||||
data = {}
|
data = {}
|
||||||
markdown_text = convert_to_markdown(data)
|
markdown_text = convert_to_markdown(data)
|
||||||
markdown_text += "\nAdd a comment that says 'Please review' to ask for a new review after you update the PR.\n"
|
user = self.git_provider.get_user_id()
|
||||||
markdown_text += "Add a comment that says 'Please answer <QUESTION...>' to ask a question about this PR.\n"
|
markdown_text += "\n### How to use\n"
|
||||||
|
if user and '[bot]' not in user:
|
||||||
|
markdown_text += f"> Tag me in a comment '@{user}' to ask for a new review after you update the PR.\n"
|
||||||
|
markdown_text += "> You can also tag me and ask any question, " \
|
||||||
|
f"for example '@{user} is the PR ready for merge?'"
|
||||||
|
else:
|
||||||
|
markdown_text += "> Add a comment that says 'review' to ask for a new review " \
|
||||||
|
"after you update the PR.\n"
|
||||||
|
markdown_text += "> You can also add a comment that says 'answer QUESTION', " \
|
||||||
|
"for example 'answer is the PR ready for merge?'"
|
||||||
|
|
||||||
if settings.config.verbosity_level >= 2:
|
if settings.config.verbosity_level >= 2:
|
||||||
logging.info(f"Markdown response:\n{markdown_text}")
|
logging.info(f"Markdown response:\n{markdown_text}")
|
||||||
return markdown_text
|
return markdown_text
|
||||||
|
Reference in New Issue
Block a user