Merge pull request #76 from zmeir/zmeir-publish_inline_comments_single_api_call

Optimization of Inline Comments Publishing
This commit is contained in:
Hussam Lawen
2023-07-18 13:05:11 +03:00
committed by GitHub
6 changed files with 62 additions and 18 deletions

View File

@ -26,7 +26,7 @@ class BitbucketProvider:
self.set_pr(pr_url) self.set_pr(pr_url)
def is_supported(self, capability: str) -> bool: def is_supported(self, capability: str) -> bool:
if capability == 'get_issue_comments': if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']:
return False return False
return True return True
@ -64,6 +64,12 @@ class BitbucketProvider:
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str): def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
pass pass
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
raise NotImplementedError("Bitbucket provider does not support creating inline comments yet")
def publish_inline_comments(self, comments: list[dict]):
raise NotImplementedError("Bitbucket provider does not support publishing inline comments yet")
def get_title(self): def get_title(self):
return self.pr.title return self.pr.title

View File

@ -44,6 +44,14 @@ class GitProvider(ABC):
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str): def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
pass pass
@abstractmethod
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
pass
@abstractmethod
def publish_inline_comments(self, comments: list[dict]):
pass
@abstractmethod @abstractmethod
def publish_code_suggestion(self, body: str, relevant_file: str, def publish_code_suggestion(self, body: str, relevant_file: str,
relevant_lines_start: int, relevant_lines_end: int): relevant_lines_start: int, relevant_lines_end: int):

View File

@ -3,7 +3,7 @@ from datetime import datetime
from typing import Optional, Tuple from typing import Optional, Tuple
from urllib.parse import urlparse from urllib.parse import urlparse
from github import AppAuthentication, Github from github import AppAuthentication, Github, Auth
from pr_agent.config_loader import settings from pr_agent.config_loader import settings
@ -57,6 +57,9 @@ class GithubProvider(GitProvider):
self.pr.comments_list.append(response) self.pr.comments_list.append(response)
def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str): def publish_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
self.publish_inline_comments([self.create_inline_comment(body, relevant_file, relevant_line_in_file)])
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
self.diff_files = self.diff_files if self.diff_files else self.get_diff_files() self.diff_files = self.diff_files if self.diff_files else self.get_diff_files()
position = -1 position = -1
for file in self.diff_files: for file in self.diff_files:
@ -75,9 +78,16 @@ class GithubProvider(GitProvider):
if position == -1: if position == -1:
if settings.config.verbosity_level >= 2: if settings.config.verbosity_level >= 2:
logging.info(f"Could not find position for {relevant_file} {relevant_line_in_file}") logging.info(f"Could not find position for {relevant_file} {relevant_line_in_file}")
subject_type = "FILE"
else: else:
subject_type = "LINE"
path = relevant_file.strip() path = relevant_file.strip()
self.pr.create_review_comment(body=body, commit_id=self.last_commit_id, path=path, position=position) # placeholder for future API support (already supported in single inline comment)
# return dict(body=body, path=path, position=position, subject_type=subject_type)
return dict(body=body, path=path, position=position) if subject_type == "LINE" else {}
def publish_inline_comments(self, comments: list[dict]):
self.pr.create_review(commit=self.last_commit_id, comments=comments)
def publish_code_suggestion(self, body: str, def publish_code_suggestion(self, body: str,
relevant_file: str, relevant_file: str,
@ -218,7 +228,7 @@ class GithubProvider(GitProvider):
raise ValueError( raise ValueError(
"GitHub token is required when using user deployment. See: " "GitHub token is required when using user deployment. See: "
"https://github.com/Codium-ai/pr-agent#method-2-run-from-source") from e "https://github.com/Codium-ai/pr-agent#method-2-run-from-source") from e
return Github(token) return Github(auth=Auth.Token(token))
def _get_repo(self): def _get_repo(self):
return self.github_client.get_repo(self.repo) return self.github_client.get_repo(self.repo)

View File

@ -33,7 +33,7 @@ class GitLabProvider(GitProvider):
r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)") r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)")
def is_supported(self, capability: str) -> bool: def is_supported(self, capability: str) -> bool:
if capability == 'get_issue_comments': if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']:
return False return False
return True return True
@ -102,6 +102,12 @@ class GitLabProvider(GitProvider):
self.send_inline_comment(body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no, self.send_inline_comment(body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
target_file, target_line_no) target_file, target_line_no)
def create_inline_comment(self, body: str, relevant_file: str, relevant_line_in_file: str):
raise NotImplementedError("Gitlab provider does not support creating inline comments yet")
def create_inline_comment(self, comments: list[dict]):
raise NotImplementedError("Gitlab provider does not support publishing inline comments yet")
def send_inline_comment(self, body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no, def send_inline_comment(self, body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
target_file, target_line_no): target_file, target_line_no):
if not found: if not found:

View File

@ -99,6 +99,12 @@ class PRReviewer:
if settings.config.git_provider == 'github' and \ if settings.config.git_provider == 'github' and \
settings.pr_reviewer.inline_code_comments and \ settings.pr_reviewer.inline_code_comments and \
'Code suggestions' in data['PR Feedback']: 'Code suggestions' in data['PR Feedback']:
# keeping only code suggestions that can't be submitted as inline comments
data['PR Feedback']['Code suggestions'] = [
d for d in data['PR Feedback']['Code suggestions']
if any(key not in d for key in ('relevant file', 'relevant line in file', 'suggestion content'))
]
if not data['PR Feedback']['Code suggestions']:
del data['PR Feedback']['Code suggestions'] del data['PR Feedback']['Code suggestions']
markdown_text = convert_to_markdown(data) markdown_text = convert_to_markdown(data)
@ -125,16 +131,24 @@ class PRReviewer:
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
data = try_fix_json(review) data = try_fix_json(review)
if settings.pr_reviewer.num_code_suggestions > 0: comments = []
try:
for d in data['PR Feedback']['Code suggestions']: for d in data['PR Feedback']['Code suggestions']:
relevant_file = d['relevant file'].strip() relevant_file = d.get('relevant file', '').strip()
relevant_line_in_file = d['relevant line in file'].strip() relevant_line_in_file = d.get('relevant line in file', '').strip()
content = d['suggestion content'] content = d.get('suggestion content', '')
if not relevant_file or not relevant_line_in_file or not content:
logging.info("Skipping inline comment with missing file/line/content")
continue
if self.git_provider.is_supported("create_inline_comment"):
comment = self.git_provider.create_inline_comment(content, relevant_file, relevant_line_in_file)
if comment:
comments.append(comment)
else:
self.git_provider.publish_inline_comment(content, relevant_file, relevant_line_in_file) self.git_provider.publish_inline_comment(content, relevant_file, relevant_line_in_file)
except KeyError:
pass if comments:
self.git_provider.publish_inline_comments(comments)
def _get_user_answers(self): def _get_user_answers(self):
answer_str = question_str = "" answer_str = question_str = ""

View File

@ -1,6 +1,6 @@
dynaconf==3.1.12 dynaconf==3.1.12
fastapi==0.99.0 fastapi==0.99.0
PyGithub==1.58.2 PyGithub==1.59.*
retry==0.9.2 retry==0.9.2
openai==0.27.8 openai==0.27.8
Jinja2==3.1.2 Jinja2==3.1.2