From 31e91edebc2cae8d06f941c6153d2017e18d8731 Mon Sep 17 00:00:00 2001 From: zmeir Date: Thu, 17 Aug 2023 15:40:24 +0300 Subject: [PATCH 1/6] Allow keeping the original user description --- pr_agent/git_providers/bitbucket_provider.py | 10 +++------- pr_agent/git_providers/git_provider.py | 19 ++++++++++++++++++- pr_agent/git_providers/github_provider.py | 5 +---- pr_agent/git_providers/gitlab_provider.py | 5 +---- pr_agent/git_providers/local_git_provider.py | 2 +- pr_agent/settings/configuration.toml | 1 + pr_agent/tools/pr_description.py | 11 +++++++++-- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 3596f4bf..e6cc74c9 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -6,12 +6,11 @@ from urllib.parse import urlparse import requests from atlassian.bitbucket import Cloud -from ..algo.pr_processing import clip_tokens from ..config_loader import get_settings -from .git_provider import FilePatchInfo +from .git_provider import FilePatchInfo, GitProvider -class BitbucketProvider: +class BitbucketProvider(GitProvider): def __init__(self, pr_url: Optional[str] = None, incremental: Optional[bool] = False): s = requests.Session() s.headers['Authorization'] = f'Bearer {get_settings().get("BITBUCKET.BEARER_TOKEN", None)}' @@ -156,10 +155,7 @@ class BitbucketProvider: def get_pr_branch(self): return self.pr.source_branch - def get_pr_description(self): - max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) - if max_tokens: - return clip_tokens(self.pr.description, max_tokens) + def get_pr_description_full(self): return self.pr.description def get_user_id(self): diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 4d711a14..e7796d1e 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -82,9 +82,26 @@ class GitProvider(ABC): pass @abstractmethod - def get_pr_description(self): + def get_pr_description_full(self): pass + def get_pr_description(self): + from pr_agent.config_loader import get_settings + from pr_agent.algo.pr_processing import clip_tokens + max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) + description = self.get_pr_description_full() + if max_tokens: + return clip_tokens(description, max_tokens) + return description + + def get_user_description(self): + description = (self.get_pr_description_full() or "").strip() + if not description.startswith("## PR Type"): + return description + if "## User Description:" not in description: + return "" + return description.split("## User Description:", 1)[1].strip() + @abstractmethod def get_issue_comments(self): pass diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index be0fa645..2e07e969 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -233,10 +233,7 @@ class GithubProvider(GitProvider): def get_pr_branch(self): return self.pr.head.ref - def get_pr_description(self): - max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) - if max_tokens: - return clip_tokens(self.pr.body, max_tokens) + def get_pr_description_full(self): return self.pr.body def get_user_id(self): diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index ec6f236d..1d4d1680 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -299,10 +299,7 @@ class GitLabProvider(GitProvider): def get_pr_branch(self): return self.mr.source_branch - def get_pr_description(self): - max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) - if max_tokens: - return clip_tokens(self.mr.description, max_tokens) + def get_pr_description_full(self): return self.mr.description def get_issue_comments(self): diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index a4f21969..59b97a85 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -158,7 +158,7 @@ class LocalGitProvider(GitProvider): def get_user_id(self): return -1 # Not used anywhere for the local provider, but required by the interface - def get_pr_description(self): + def get_pr_description_full(self): commits_diff = list(self.repo.iter_commits(self.target_branch_name + '..HEAD')) # Get the commit messages and concatenate commit_messages = " ".join([commit.message for commit in commits_diff]) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index ce920efd..ef684f2c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -24,6 +24,7 @@ extra_instructions = "" [pr_description] # /describe # publish_description_as_comment=false +keep_user_description=true extra_instructions = "" [pr_questions] # /ask # diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index d55dd55a..13898e8f 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -42,6 +42,8 @@ class PRDescription: "extra_instructions": get_settings().pr_description.extra_instructions, "commit_messages_str": self.git_provider.get_commit_messages() } + + self.user_description = self.git_provider.get_user_description() # Initialize the token handler self.token_handler = TokenHandler( @@ -145,6 +147,9 @@ class PRDescription: # Load the AI prediction data into a dictionary data = load_yaml(self.prediction.strip()) + if get_settings().pr_description.keep_user_description and self.user_description: + data["User Description"] = self.user_description + # Initialization pr_types = [] @@ -167,7 +172,7 @@ class PRDescription: # Iterate over the remaining dictionary items and append the key and value to 'pr_body' in a markdown format, # except for the items containing the word 'walkthrough' pr_body = "" - for key, value in data.items(): + for idx, (key, value) in enumerate(data.items()): pr_body += f"## {key}:\n" if 'walkthrough' in key.lower(): # for filename, description in value.items(): @@ -179,7 +184,9 @@ class PRDescription: # if the value is a list, join its items by comma if type(value) == list: value = ', '.join(v for v in value) - pr_body += f"{value}\n\n___\n" + pr_body += f"{value}\n" + if idx < len(data) - 1: + pr_body += "\n___\n" if get_settings().config.verbosity_level >= 2: logging.info(f"title:\n{title}\n{pr_body}") From b3749d08e2655e40977e4f14b904c584978994af Mon Sep 17 00:00:00 2001 From: zmeir Date: Sun, 20 Aug 2023 19:00:56 +0300 Subject: [PATCH 2/6] Set default configuration to false to allow users to opt-in --- pr_agent/settings/configuration.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index ef684f2c..a881c500 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -24,7 +24,7 @@ extra_instructions = "" [pr_description] # /describe # publish_description_as_comment=false -keep_user_description=true +keep_user_description=false extra_instructions = "" [pr_questions] # /ask # From 81c38f9646c43a068e7618c25723ce3d4fa39ec0 Mon Sep 17 00:00:00 2001 From: zmeir Date: Mon, 21 Aug 2023 09:22:58 +0300 Subject: [PATCH 3/6] Added type hints --- pr_agent/git_providers/git_provider.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index e7796d1e..9db0a5bd 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -82,10 +82,10 @@ class GitProvider(ABC): pass @abstractmethod - def get_pr_description_full(self): + def get_pr_description_full(self) -> str: pass - def get_pr_description(self): + def get_pr_description(self) -> str: from pr_agent.config_loader import get_settings from pr_agent.algo.pr_processing import clip_tokens max_tokens = get_settings().get("CONFIG.MAX_DESCRIPTION_TOKENS", None) @@ -94,7 +94,7 @@ class GitProvider(ABC): return clip_tokens(description, max_tokens) return description - def get_user_description(self): + def get_user_description(self) -> str: description = (self.get_pr_description_full() or "").strip() if not description.startswith("## PR Type"): return description From 2b22f712fb9105c9a2b220c6d61db5e55615e41a Mon Sep 17 00:00:00 2001 From: zmeir Date: Tue, 22 Aug 2023 09:55:56 +0300 Subject: [PATCH 4/6] Renamed keep_user_description --> add_original_user_description --- pr_agent/settings/configuration.toml | 2 +- pr_agent/tools/pr_description.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index a881c500..d3098f4f 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -24,7 +24,7 @@ extra_instructions = "" [pr_description] # /describe # publish_description_as_comment=false -keep_user_description=false +add_original_user_description=false extra_instructions = "" [pr_questions] # /ask # diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 13898e8f..98fbe0ce 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -147,7 +147,7 @@ class PRDescription: # Load the AI prediction data into a dictionary data = load_yaml(self.prediction.strip()) - if get_settings().pr_description.keep_user_description and self.user_description: + if get_settings().pr_description.add_original_user_description and self.user_description: data["User Description"] = self.user_description # Initialization From 09ef809080cd72051734d3fe94313efc71b272c1 Mon Sep 17 00:00:00 2001 From: zmeir Date: Tue, 22 Aug 2023 10:04:21 +0300 Subject: [PATCH 5/6] Added comments explaining the logic behind `get_user_description` --- pr_agent/git_providers/git_provider.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 9db0a5bd..0837155c 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -96,10 +96,14 @@ class GitProvider(ABC): def get_user_description(self) -> str: description = (self.get_pr_description_full() or "").strip() + # if the existing description wasn't generated by the pr-agent, just return it as-is if not description.startswith("## PR Type"): return description + # if the existing description was generated by the pr-agent, but it doesn't contain the user description, + # return nothing (empty string) because it means there is no user description if "## User Description:" not in description: return "" + # otherwise, extract the original user description from the existing pr-agent description and return it return description.split("## User Description:", 1)[1].strip() @abstractmethod From 82fb611a265527de1761ee4b48da1e860cafbe36 Mon Sep 17 00:00:00 2001 From: zmeir Date: Tue, 22 Aug 2023 10:32:58 +0300 Subject: [PATCH 6/6] Add options to keep original user title --- pr_agent/settings/configuration.toml | 1 + pr_agent/tools/pr_description.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index d3098f4f..73b32878 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -25,6 +25,7 @@ extra_instructions = "" [pr_description] # /describe # publish_description_as_comment=false add_original_user_description=false +keep_original_user_title=false extra_instructions = "" [pr_questions] # /ask # diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 98fbe0ce..440675fd 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -166,8 +166,14 @@ class PRDescription: elif type(data['PR Type']) == str: pr_types = data['PR Type'].split(',') - # Assign the value of the 'PR Title' key to 'title' variable and remove it from the dictionary - title = data.pop('PR Title') + # Remove the 'PR Title' key from the dictionary + ai_title = data.pop('PR Title') + if get_settings().pr_description.keep_original_user_title: + # Assign the original PR title to the 'title' variable + title = self.vars["title"] + else: + # Assign the value of the 'PR Title' key to 'title' variable + title = ai_title # Iterate over the remaining dictionary items and append the key and value to 'pr_body' in a markdown format, # except for the items containing the word 'walkthrough'