From d2a129fe30f7c15e7f73cb4cebef0e1e11039c54 Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Mon, 4 Dec 2023 18:22:35 +0200 Subject: [PATCH 01/14] Add labeling files --- pr_agent/settings/configuration.toml | 3 ++ pr_agent/settings/pr_description_prompts.toml | 27 +++++++++++++++++- pr_agent/tools/pr_description.py | 28 ++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 14f4f6ae..34a01d3d 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -46,8 +46,11 @@ keep_original_user_title=false use_bullet_points=true extra_instructions = "" enable_pr_type=true +enable_file_walkthrough=false +enable_semantic_files_types=true final_update_message = true + # markers use_description_markers=false include_generated_by_header=true diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 73f03b97..3b430472 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -31,18 +31,31 @@ class PRType(str, Enum): {%- endif %} +{%- if enable_file_walkthrough %} class FileWalkthrough(BaseModel): filename: str = Field(description="the relevant file full path") changes_in_file: str = Field(description="minimal and concise description of the changes in the relevant file") +{%- endif %} + +{%- if enable_semantic_files_types %} +class SemanticLabelFiles(BaseModel): + label: PRType = Field(description="A label that represent a type of semantic code changes. Use the label value, not the name") + files: List[str] = Field(description="a list of files that are relevant to the label. A file may appear in multiple labels") +{%- endif %} Class PRDescription(BaseModel): title: str = Field(description="an informative title for the PR, describing its main theme") - type: List[PRType] = Field(description="one or more types that describe the PR type. . Return the label value, not the name.") + type: List[PRType] = Field(description="one or more types that describe the PR type. Return the label value, not the name.") description: str = Field(description="an informative and concise description of the PR. {%- if use_bullet_points %} Use bullet points.{% endif %}") {%- if enable_custom_labels %} labels: List[Label] = Field(min_items=0, description="custom labels that describe the PR. Return the label value, not the name.") {%- endif %} +{%- if enable_file_walkthrough %} main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) +{%- endif %} +{%- if enable_semantic_files_types %} + pr_files_labels[SemanticLabelFiles] +{%- endif %} ===== @@ -61,9 +74,21 @@ labels: {%- endif %} description: |- ... +{%- if enable_file_walkthrough %} main_files_walkthrough: - ... - ... +{%- endif %} +{%- if enable_semantic_files_types %} +pr_files_labels: +- label: ... + files: + - | + ... + - | + ... +... +{%- endif %} ``` Answer should be a valid YAML, and nothing else. Each YAML output MUST be after a newline, with proper indent, and block scalar indicator ('|-') diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 501de707..96f01412 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -45,6 +45,8 @@ class PRDescription: "commit_messages_str": self.git_provider.get_commit_messages(), "enable_custom_labels": get_settings().config.enable_custom_labels, "custom_labels_class": "", # will be filled if necessary in 'set_custom_labels' function + "enable_file_walkthrough": get_settings().pr_description.enable_file_walkthrough, + "enable_semantic_files_types": get_settings().pr_description.enable_semantic_files_types, } self.user_description = self.git_provider.get_user_description() @@ -257,7 +259,10 @@ class PRDescription: # except for the items containing the word 'walkthrough' pr_body = "" for idx, (key, value) in enumerate(self.data.items()): - pr_body += f"## {key}:\n" + key_publish = key.rstrip(':').replace("_", " ").capitalize() + if key == 'pr_files_labels': + key_publish = 'PR Files Labels' + pr_body += f"## {key_publish}\n" if 'walkthrough' in key.lower(): # for filename, description in value.items(): if self.git_provider.is_supported("gfm_markdown"): @@ -268,6 +273,27 @@ class PRDescription: pr_body += f'- `{filename}`: {description}\n' if self.git_provider.is_supported("gfm_markdown"): pr_body +="\n" + elif 'pr_files_labels' in key.lower(): + pr_body += """\n| | Relevant Files """ + for i in range(60): + pr_body += "  " + pr_body += """|\n|-----------|-------------|\n""" + for semantic_label in value: + # for filename, description in value.items(): + if self.git_provider.is_supported("gfm_markdown"): + # pr_body += f"
{semantic_label['label']}\n\n" + pr_body += f"| **{semantic_label['label']}** |
files:
    " + + for file in semantic_label['files']: + filename = file.replace("'", "`") + # description = file['changes_in_file'] + # pr_body += f'- `{filename}`\n' + if self.git_provider.is_supported("gfm_markdown"): + pr_body += f"
  • {filename}
  • " + else: + pr_body += f'- `{filename}`\n' + if self.git_provider.is_supported("gfm_markdown"): + pr_body += "
|\n" else: # if the value is a list, join its items by comma if type(value) == list: From 21a7a0f136e4031f7407353c4bc94f86b7da38f7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 4 Dec 2023 21:06:56 +0200 Subject: [PATCH 02/14] feat: Enhance link generation for relevant lines and refactor code in git providers and PR description tools --- pr_agent/git_providers/bitbucket_provider.py | 5 ++++- pr_agent/git_providers/github_provider.py | 4 +++- pr_agent/git_providers/gitlab_provider.py | 4 +++- pr_agent/settings/pr_description_prompts.toml | 8 ++++---- pr_agent/tools/pr_description.py | 11 ++++++++--- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index 386577a2..ee8ad48f 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -229,7 +229,10 @@ class BitbucketProvider(GitProvider): return response def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: - link = f"{self.pr_url}/#L{relevant_file}T{relevant_line_start}" + if relevant_line_start == -1: + link = f"{self.pr_url}/#L{relevant_file}" + else: + link = f"{self.pr_url}/#L{relevant_file}T{relevant_line_start}" return link def generate_link_to_relevant_line_number(self, suggestion) -> str: diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index c001f81e..ab4acb9a 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -506,7 +506,9 @@ class GithubProvider(GitProvider): def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: sha_file = hashlib.sha256(relevant_file.encode('utf-8')).hexdigest() - if relevant_line_end: + if relevant_line_start == -1: + link = f"https://github.com/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}" + elif relevant_line_end: link = f"https://github.com/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}R{relevant_line_start}-R{relevant_line_end}" else: link = f"https://github.com/{self.repo}/pull/{self.pr_num}/files#diff-{sha_file}R{relevant_line_start}" diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 3a593439..c583b087 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -424,7 +424,9 @@ class GitLabProvider(GitProvider): return "" def get_line_link(self, relevant_file: str, relevant_line_start: int, relevant_line_end: int = None) -> str: - if relevant_line_end: + if relevant_line_start == -1: + link = f"https://gitlab.com/codiumai/pr-agent/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads" + elif relevant_line_end: link = f"https://gitlab.com/codiumai/pr-agent/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{relevant_line_start}-L{relevant_line_end}" else: link = f"https://gitlab.com/codiumai/pr-agent/-/blob/{self.mr.source_branch}/{relevant_file}?ref_type=heads#L{relevant_line_start}" diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 3b430472..fa7c87a6 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -38,9 +38,9 @@ class FileWalkthrough(BaseModel): {%- endif %} {%- if enable_semantic_files_types %} -class SemanticLabelFiles(BaseModel): - label: PRType = Field(description="A label that represent a type of semantic code changes. Use the label value, not the name") - files: List[str] = Field(description="a list of files that are relevant to the label. A file may appear in multiple labels") +class SemanticLabel(BaseModel): + label: Any(PRType, str) = Field(description="a semantic label that represents a type of code changes that occurred in the PR. You can use a label from the $PRType enum, or use additional custom labels that you define.") + files: List[str] = Field(description="a list of file names related to the semantic label. A file may appear in multiple labels. Present the file full path, and nothing else.") {%- endif %} Class PRDescription(BaseModel): @@ -54,7 +54,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files_labels[SemanticLabelFiles] + pr_files_labels[List[SemanticLabel]] = Field(min_items=3, description="A list of semantic labels that describe the type of changes in the PR files.") {%- endif %} ===== diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 96f01412..f9af0b4c 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -275,8 +275,7 @@ class PRDescription: pr_body +="
\n" elif 'pr_files_labels' in key.lower(): pr_body += """\n| | Relevant Files """ - for i in range(60): - pr_body += "  " + pr_body += "  " * 60 pr_body += """|\n|-----------|-------------|\n""" for semantic_label in value: # for filename, description in value.items(): @@ -288,6 +287,12 @@ class PRDescription: filename = file.replace("'", "`") # description = file['changes_in_file'] # pr_body += f'- `{filename}`\n' + + # try to add line numbers link to code suggestions + if hasattr(self.git_provider, 'get_line_link'): + link = self.git_provider.get_line_link(filename, relevant_line_start=-1) + if link: + filename = f"[{filename}]({link})" if self.git_provider.is_supported("gfm_markdown"): pr_body += f"
  • {filename}
  • " else: @@ -296,7 +301,7 @@ class PRDescription: pr_body += "|\n" else: # if the value is a list, join its items by comma - if type(value) == list: + if isinstance(value, list): value = ', '.join(v for v in value) pr_body += f"{value}\n" if idx < len(self.data) - 1: From 863eb0105d83b82cd823b826cbb399b072c0700c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 4 Dec 2023 21:23:59 +0200 Subject: [PATCH 03/14] feat: Refactor semantic labels in PR description and improve clarity in pr_description.py and pr_description_prompts.toml --- pr_agent/settings/pr_description_prompts.toml | 8 ++++---- pr_agent/tools/pr_description.py | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index fa7c87a6..a8ae0117 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -38,9 +38,9 @@ class FileWalkthrough(BaseModel): {%- endif %} {%- if enable_semantic_files_types %} -class SemanticLabel(BaseModel): - label: Any(PRType, str) = Field(description="a semantic label that represents a type of code changes that occurred in the PR. You can use a label from the $PRType enum, or use additional custom labels that you define.") - files: List[str] = Field(description="a list of file names related to the semantic label. A file may appear in multiple labels. Present the file full path, and nothing else.") +class SemanticFileLabels(BaseModel): + label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration', 'logging', 'dependencies', 'new feature', ...") + files: List[str] = Field(description="a list of file names related to the chosen semantic label. A file may appear in multiple labels. Present the file full path, and nothing else.") {%- endif %} Class PRDescription(BaseModel): @@ -54,7 +54,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files_labels[List[SemanticLabel]] = Field(min_items=3, description="A list of semantic labels that describe the type of changes in the PR files.") + pr_files_labels[List[SemanticFileLabels]] = Field(min_items=3, description="A list of semantic labels that describe the type of changes in the PR files.") {%- endif %} ===== diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index f9af0b4c..99fd6d4f 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -261,7 +261,7 @@ class PRDescription: for idx, (key, value) in enumerate(self.data.items()): key_publish = key.rstrip(':').replace("_", " ").capitalize() if key == 'pr_files_labels': - key_publish = 'PR Files Labels' + key_publish = 'PR Changes Analysis' pr_body += f"## {key_publish}\n" if 'walkthrough' in key.lower(): # for filename, description in value.items(): @@ -279,9 +279,10 @@ class PRDescription: pr_body += """|\n|-----------|-------------|\n""" for semantic_label in value: # for filename, description in value.items(): + s_label = semantic_label['label'].strip("'").strip('"') if self.git_provider.is_supported("gfm_markdown"): # pr_body += f"
    {semantic_label['label']}\n\n" - pr_body += f"| **{semantic_label['label']}** |
    files:
      " + pr_body += f"| **{s_label}** |
      files:
        " for file in semantic_label['files']: filename = file.replace("'", "`") @@ -290,6 +291,7 @@ class PRDescription: # try to add line numbers link to code suggestions if hasattr(self.git_provider, 'get_line_link'): + filename = filename.strip() link = self.git_provider.get_line_link(filename, relevant_line_start=-1) if link: filename = f"[{filename}]({link})" From 2feaee4306bd2557dd6383f0ae13b32f5f5ce317 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 4 Dec 2023 21:45:22 +0200 Subject: [PATCH 04/14] feat: Update field descriptions in pr_description_prompts.toml for clarity --- pr_agent/settings/pr_description_prompts.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index a8ae0117..b11af105 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -39,8 +39,8 @@ class FileWalkthrough(BaseModel): {%- if enable_semantic_files_types %} class SemanticFileLabels(BaseModel): - label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration', 'logging', 'dependencies', 'new feature', ...") - files: List[str] = Field(description="a list of file names related to the chosen semantic label. A file may appear in multiple labels. Present the file full path, and nothing else.") + label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'logging', 'dependencies', 'new feature', ...") + files: List[str] = Field(description="a list of file names related to the chosen semantic label. A file may appear in multiple labels. Present the file full path, and nothing else. Make sure the semantic label properly describes the type of changes in each file") {%- endif %} Class PRDescription(BaseModel): From cf3401536aa42b9883c12e63763e0f386c193f06 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 5 Dec 2023 07:48:21 +0200 Subject: [PATCH 05/14] feat: Remove 'Refactoring' label from custom labels and update related descriptions --- pr_agent/algo/utils.py | 4 ++-- pr_agent/git_providers/github_provider.py | 2 +- pr_agent/settings/configuration.toml | 2 +- pr_agent/settings/custom_labels.toml | 4 +--- pr_agent/settings/pr_custom_labels.toml | 1 - pr_agent/settings/pr_description_prompts.toml | 7 +++---- pr_agent/settings/pr_reviewer_prompts.toml | 1 - 7 files changed, 8 insertions(+), 13 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index ded1b52c..d5e1a3c6 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -343,7 +343,7 @@ def set_custom_labels(variables): labels = get_settings().custom_labels if not labels: # set default labels - labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Refactoring', 'Enhancement', 'Documentation', 'Other'] + labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Enhancement', 'Documentation', 'Other'] labels_list = "\n - ".join(labels) if labels else "" labels_list = f" - {labels_list}" if labels_list else "" variables["custom_labels"] = labels_list @@ -367,7 +367,7 @@ def get_user_labels(current_labels: List[str] = None): current_labels = [] user_labels = [] for label in current_labels: - if label.lower() in ['bug fix', 'tests', 'refactoring', 'enhancement', 'documentation', 'other']: + if label.lower() in ['bug fix', 'tests', 'enhancement', 'documentation', 'other']: continue if get_settings().config.enable_custom_labels: if label in get_settings().custom_labels: diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index ab4acb9a..7e13e0ee 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -442,7 +442,7 @@ class GithubProvider(GitProvider): def publish_labels(self, pr_types): try: label_color_map = {"Bug fix": "1d76db", "Tests": "e99695", "Bug fix with tests": "c5def5", - "Refactoring": "bfdadc", "Enhancement": "bfd4f2", "Documentation": "d4c5f9", + "Enhancement": "bfd4f2", "Documentation": "d4c5f9", "Other": "d1bcf9"} post_parameters = [] for p in pr_types: diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 34a01d3d..259383d7 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -55,7 +55,7 @@ final_update_message = true use_description_markers=false include_generated_by_header=true -#custom_labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Refactoring', 'Enhancement', 'Documentation', 'Other'] +#custom_labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Enhancement', 'Documentation', 'Other'] [pr_questions] # /ask # diff --git a/pr_agent/settings/custom_labels.toml b/pr_agent/settings/custom_labels.toml index 9c751d0e..ee45fb19 100644 --- a/pr_agent/settings/custom_labels.toml +++ b/pr_agent/settings/custom_labels.toml @@ -8,10 +8,8 @@ enable_custom_labels=false #description = """Adds or modifies tests""" #[custom_labels."Bug fix with tests"] #description = """Fixes a bug in the code and adds or modifies tests""" -#[custom_labels."Refactoring"] -#description = """Code refactoring without changing functionality""" #[custom_labels."Enhancement"] -#description = """Adds new features or functionality""" +#description = """Adds new features or modifies existing ones""" #[custom_labels."Documentation"] #description = """Adds or modifies documentation""" #[custom_labels."Other"] diff --git a/pr_agent/settings/pr_custom_labels.toml b/pr_agent/settings/pr_custom_labels.toml index 46ee0684..d9a5e004 100644 --- a/pr_agent/settings/pr_custom_labels.toml +++ b/pr_agent/settings/pr_custom_labels.toml @@ -24,7 +24,6 @@ The output must be a YAML object equivalent to type $Labels, according to the fo class Label(str, Enum): bug_fix = "Bug fix" tests = "Tests" - refactoring = "Refactoring" enhancement = "Enhancement" documentation = "Documentation" other = "Other" diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index b11af105..fe669819 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -20,7 +20,6 @@ The output must be a YAML object equivalent to type $PRDescription, according to class PRType(str, Enum): bug_fix = "Bug fix" tests = "Tests" - refactoring = "Refactoring" enhancement = "Enhancement" documentation = "Documentation" other = "Other" @@ -39,8 +38,8 @@ class FileWalkthrough(BaseModel): {%- if enable_semantic_files_types %} class SemanticFileLabels(BaseModel): - label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'logging', 'dependencies', 'new feature', ...") - files: List[str] = Field(description="a list of file names related to the chosen semantic label. A file may appear in multiple labels. Present the file full path, and nothing else. Make sure the semantic label properly describes the type of changes in each file") + label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'logging', 'dependencies', 'new feature', 'other', ...") + files: List[str] = Field(description="a list of file names related to the chosen semantic label. Present the file full path, and nothing else.") {%- endif %} Class PRDescription(BaseModel): @@ -54,7 +53,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files_labels[List[SemanticFileLabels]] = Field(min_items=3, description="A list of semantic labels that describe the type of changes in the PR files.") + pr_files_labels[List[SemanticFileLabels]] = Field(min_items=2, description="A list of semantic labels that describe the type of changes in the PR files. A file should appear only in a single label, that best describes the type of changes in the file.") {%- endif %} ===== diff --git a/pr_agent/settings/pr_reviewer_prompts.toml b/pr_agent/settings/pr_reviewer_prompts.toml index d38dc287..ce32bfc9 100644 --- a/pr_agent/settings/pr_reviewer_prompts.toml +++ b/pr_agent/settings/pr_reviewer_prompts.toml @@ -57,7 +57,6 @@ PR Analysis: enum: - Bug fix - Tests - - Refactoring - Enhancement - Documentation - Other From 862c2360760c726dc611de277a29de00429596ad Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 5 Dec 2023 18:10:13 +0200 Subject: [PATCH 06/14] s --- pr_agent/tools/pr_description.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 99fd6d4f..a43847f7 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -283,6 +283,8 @@ class PRDescription: if self.git_provider.is_supported("gfm_markdown"): # pr_body += f"
        {semantic_label['label']}\n\n" pr_body += f"| **{s_label}** |
        files:
          " + else: + pr_body += f"| **{s_label}** | " for file in semantic_label['files']: filename = file.replace("'", "`") @@ -298,9 +300,11 @@ class PRDescription: if self.git_provider.is_supported("gfm_markdown"): pr_body += f"
        • {filename}
        • " else: - pr_body += f'- `{filename}`\n' + pr_body += f"{filename}    " if self.git_provider.is_supported("gfm_markdown"): pr_body += "
        |\n" + else: + pr_body += "|" else: # if the value is a list, join its items by comma if isinstance(value, list): From 0b70e07b8c43d91db8b8476ed60f4cc6af0aa408 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 5 Dec 2023 18:26:35 +0200 Subject: [PATCH 07/14] feat: Improve formatting in help.py command descriptions --- pr_agent/servers/help.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pr_agent/servers/help.py b/pr_agent/servers/help.py index c32c5666..7c3bf287 100644 --- a/pr_agent/servers/help.py +++ b/pr_agent/servers/help.py @@ -1,19 +1,19 @@ -commands_text = "> **/review**: Request a review of your Pull Request.\n" \ - "> **/describe**: Update the PR title and description based on the contents of the PR.\n" \ - "> **/improve [--extended]**: Suggest code improvements. Extended mode provides a higher quality feedback.\n" \ - "> **/ask \\**: Ask a question about the PR.\n" \ - "> **/update_changelog**: Update the changelog based on the PR's contents.\n" \ - "> **/add_docs**: Generate docstring for new components introduced in the PR.\n" \ - "> **/generate_labels**: Generate labels for the PR based on the PR's contents.\n" \ +commands_text = "> **/review**: Request a review of your Pull Request. \n" \ + "> **/describe**: Update the PR title and description based on the contents of the PR. \n" \ + "> **/improve [--extended]**: Suggest code improvements. Extended mode provides a higher quality feedback. \n" \ + "> **/ask \\**: Ask a question about the PR. \n" \ + "> **/update_changelog**: Update the changelog based on the PR's contents. \n" \ + "> **/add_docs**: Generate docstring for new components introduced in the PR. \n" \ + "> **/generate_labels**: Generate labels for the PR based on the PR's contents. \n" \ "> see the [tools guide](https://github.com/Codium-ai/pr-agent/blob/main/docs/TOOLS_GUIDE.md) for more details.\n\n" \ - ">To edit any configuration parameter from the [configuration.toml](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml), add --config_path=new_value.\n" \ - ">For example: /review --pr_reviewer.extra_instructions=\"focus on the file: ...\" \n" \ - ">To list the possible configuration parameters, add a **/config** comment.\n" \ + ">To edit any configuration parameter from the [configuration.toml](https://github.com/Codium-ai/pr-agent/blob/main/pr_agent/settings/configuration.toml), add --config_path=new_value. \n" \ + ">For example: /review --pr_reviewer.extra_instructions=\"focus on the file: ...\" \n" \ + ">To list the possible configuration parameters, add a **/config** comment. \n" \ def bot_help_text(user: str): - return f"> Tag me in a comment '@{user}' and add one of the following commands:\n" + commands_text + return f"> Tag me in a comment '@{user}' and add one of the following commands: \n" + commands_text -actions_help_text = "> To invoke the PR-Agent, add a comment using one of the following commands:\n" + \ +actions_help_text = "> To invoke the PR-Agent, add a comment using one of the following commands: \n" + \ commands_text From c1ed3ee511a270f8b573851c365ce14d222614e2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 08:08:01 +0200 Subject: [PATCH 08/14] feat: Refine field descriptions in pr_description_prompts.toml for semantic file labels --- pr_agent/settings/pr_description_prompts.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index fe669819..b418219a 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -39,7 +39,7 @@ class FileWalkthrough(BaseModel): {%- if enable_semantic_files_types %} class SemanticFileLabels(BaseModel): label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'logging', 'dependencies', 'new feature', 'other', ...") - files: List[str] = Field(description="a list of file names related to the chosen semantic label. Present the file full path, and nothing else.") + files: List[str] = Field(description="a list of file names related to the chosen semantic label. Present the file full path, and nothing else. A file should appear only in a single label, that best describes the type of changes in the file. If you are not sure, prefer a more general label.") {%- endif %} Class PRDescription(BaseModel): @@ -53,7 +53,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files_labels[List[SemanticFileLabels]] = Field(min_items=2, description="A list of semantic labels that describe the type of changes in the PR files. A file should appear only in a single label, that best describes the type of changes in the file.") + pr_files_labels[List[SemanticFileLabels]] = Field(min_items=2, description="A list of semantic labels that describe the type of changes in the PR files.") {%- endif %} ===== From f629755a9a43f3d25073291d178e9da68e94754a Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 10:59:44 +0200 Subject: [PATCH 09/14] feat: Refine field descriptions in pr_description_prompts.toml for semantic file labels --- pr_agent/settings/pr_description_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index b418219a..bcf4ad69 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -38,7 +38,7 @@ class FileWalkthrough(BaseModel): {%- if enable_semantic_files_types %} class SemanticFileLabels(BaseModel): - label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'logging', 'dependencies', 'new feature', 'other', ...") + label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formating', 'miscellaneous', ...") files: List[str] = Field(description="a list of file names related to the chosen semantic label. Present the file full path, and nothing else. A file should appear only in a single label, that best describes the type of changes in the file. If you are not sure, prefer a more general label.") {%- endif %} From 4b073b32a5fa75b777faa34d1879ac95e6e6ff98 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 12:30:51 +0200 Subject: [PATCH 10/14] feat: Enhance PR description with file label dictionary and update prompts in pr_description_prompts.toml --- pr_agent/settings/pr_description_prompts.toml | 19 +++++---- pr_agent/tools/pr_description.py | 42 ++++++++++++------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index bcf4ad69..bd94d3c1 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -37,9 +37,10 @@ class FileWalkthrough(BaseModel): {%- endif %} {%- if enable_semantic_files_types %} -class SemanticFileLabels(BaseModel): - label: str = Field(description="a semantic label that represents a type of code changes that occurred in the PR. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formating', 'miscellaneous', ...") - files: List[str] = Field(description="a list of file names related to the chosen semantic label. Present the file full path, and nothing else. A file should appear only in a single label, that best describes the type of changes in the file. If you are not sure, prefer a more general label.") +Class FileDescription(BaseModel): + filename: str = Field(description="the relevant file full path") + changes_summary: str = Field(description="minimal and concise description of the important changes in the relevant file") + label: str = Field(description="a single semantic label that represents a type of code changes that occurred in the File. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formating', 'miscellaneous', ...") {%- endif %} Class PRDescription(BaseModel): @@ -53,7 +54,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files_labels[List[SemanticFileLabels]] = Field(min_items=2, description="A list of semantic labels that describe the type of changes in the PR files.") + pr_files[List[FileDescription]] = Field(max_items=15) {%- endif %} ===== @@ -79,12 +80,12 @@ main_files_walkthrough: - ... {%- endif %} {%- if enable_semantic_files_types %} -pr_files_labels: -- label: ... - files: - - | +pr_files: +- filename: | ... - - | + changes_summary: | + ... + label: | ... ... {%- endif %} diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index a43847f7..94a1ee83 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -81,6 +81,19 @@ class PRDescription: else: return None + self.file_label_dict = {} + for file in self.data['pr_files']: + try: + filename = file['filename'].replace("'", "`").replace('"', '`') + changes_summary = file['changes_summary'] + label = file['label'] + if label not in self.file_label_dict: + self.file_label_dict[label] = [] + self.file_label_dict[label].append((filename, changes_summary)) + except Exception as e: + get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") + pass + pr_labels = [] if get_settings().pr_description.publish_labels: pr_labels = self._prepare_labels() @@ -260,8 +273,8 @@ class PRDescription: pr_body = "" for idx, (key, value) in enumerate(self.data.items()): key_publish = key.rstrip(':').replace("_", " ").capitalize() - if key == 'pr_files_labels': - key_publish = 'PR Changes Analysis' + if key == 'pr_files': + value = self.file_label_dict pr_body += f"## {key_publish}\n" if 'walkthrough' in key.lower(): # for filename, description in value.items(): @@ -273,21 +286,21 @@ class PRDescription: pr_body += f'- `{filename}`: {description}\n' if self.git_provider.is_supported("gfm_markdown"): pr_body +="
        \n" - elif 'pr_files_labels' in key.lower(): + elif 'pr_files' in key.lower(): pr_body += """\n| | Relevant Files """ - pr_body += "  " * 60 + pr_body += "  " * 70 pr_body += """|\n|-----------|-------------|\n""" - for semantic_label in value: - # for filename, description in value.items(): - s_label = semantic_label['label'].strip("'").strip('"') + for semantic_label in value.keys(): + s_label = semantic_label.strip("'").strip('"') if self.git_provider.is_supported("gfm_markdown"): # pr_body += f"
        {semantic_label['label']}\n\n" pr_body += f"| **{s_label}** |
        files:
          " else: pr_body += f"| **{s_label}** | " - for file in semantic_label['files']: - filename = file.replace("'", "`") + list_tuples = value[semantic_label] + for filename,file_change_description in list_tuples: + filename = filename.replace("'", "`") # description = file['changes_in_file'] # pr_body += f'- `{filename}`\n' @@ -298,13 +311,14 @@ class PRDescription: if link: filename = f"[{filename}]({link})" if self.git_provider.is_supported("gfm_markdown"): - pr_body += f"
        • {filename}
        • " - else: - pr_body += f"{filename}    " + pr_body += f"
          {filename}" + pr_body += f"
            Change summary:
            **{file_change_description}**
          " + # else: + # pr_body += f"{filename}    " if self.git_provider.is_supported("gfm_markdown"): pr_body += "
        |\n" - else: - pr_body += "|" + # else: + # pr_body += "|" else: # if the value is a list, join its items by comma if isinstance(value, list): From eeb20b055af2eca315af147b7a2eb18120b1b6f7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 15:29:45 +0200 Subject: [PATCH 11/14] feat: Add line count to file patch info and enhance PR description formatting --- pr_agent/git_providers/git_provider.py | 2 + pr_agent/git_providers/github_provider.py | 9 ++- pr_agent/settings/pr_description_prompts.toml | 2 +- pr_agent/tools/pr_description.py | 68 +++++++++++-------- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 3ad1b2e0..deb5df3d 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -26,6 +26,8 @@ class FilePatchInfo: tokens: int = -1 edit_type: EDIT_TYPE = EDIT_TYPE.UNKNOWN old_filename: str = None + num_plus_lines: int = -1 + num_minus_lines: int = -1 class GitProvider(ABC): diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 7e13e0ee..3ae97742 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -143,8 +143,15 @@ class GithubProvider(GitProvider): else: get_logger().error(f"Unknown edit type: {file.status}") edit_type = EDIT_TYPE.UNKNOWN + + # count number of lines added and removed + patch_lines = patch.splitlines(keepends=True) + num_plus_lines = len([line for line in patch_lines if line.startswith('+')]) + num_minus_lines = len([line for line in patch_lines if line.startswith('-')]) file_patch_canonical_structure = FilePatchInfo(original_file_content_str, new_file_content_str, patch, - file.filename, edit_type=edit_type) + file.filename, edit_type=edit_type, + num_plus_lines=num_plus_lines, + num_minus_lines=num_minus_lines,) diff_files.append(file_patch_canonical_structure) self.diff_files = diff_files diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index bd94d3c1..2225088d 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -54,7 +54,7 @@ Class PRDescription(BaseModel): main_files_walkthrough: List[FileWalkthrough] = Field(max_items=10) {%- endif %} {%- if enable_semantic_files_types %} - pr_files[List[FileDescription]] = Field(max_items=15) + pr_files[List[FileDescription]] = Field(max_items=15") {%- endif %} ===== diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 94a1ee83..5a074390 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -81,18 +81,8 @@ class PRDescription: else: return None - self.file_label_dict = {} - for file in self.data['pr_files']: - try: - filename = file['filename'].replace("'", "`").replace('"', '`') - changes_summary = file['changes_summary'] - label = file['label'] - if label not in self.file_label_dict: - self.file_label_dict[label] = [] - self.file_label_dict[label].append((filename, changes_summary)) - except Exception as e: - get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") - pass + if get_settings().pr_description.enable_semantic_files_types: + self._prepare_file_labels() pr_labels = [] if get_settings().pr_description.publish_labels: @@ -272,12 +262,13 @@ class PRDescription: # except for the items containing the word 'walkthrough' pr_body = "" for idx, (key, value) in enumerate(self.data.items()): - key_publish = key.rstrip(':').replace("_", " ").capitalize() if key == 'pr_files': value = self.file_label_dict + key_publish = "PR changes summary" + else: + key_publish = key.rstrip(':').replace("_", " ").capitalize() pr_body += f"## {key_publish}\n" if 'walkthrough' in key.lower(): - # for filename, description in value.items(): if self.git_provider.is_supported("gfm_markdown"): pr_body += "
        files:\n\n" for file in value: @@ -295,30 +286,39 @@ class PRDescription: if self.git_provider.is_supported("gfm_markdown"): # pr_body += f"
        {semantic_label['label']}\n\n" pr_body += f"| **{s_label}** |
        files:
          " - else: - pr_body += f"| **{s_label}** | " list_tuples = value[semantic_label] - for filename,file_change_description in list_tuples: + for filename, file_change_description in list_tuples: filename = filename.replace("'", "`") - # description = file['changes_in_file'] - # pr_body += f'- `{filename}`\n' + filename_publish = filename.split("/")[-1] + filename_publish = f"**{filename_publish}**" + diff_plus_minus = "" + diff_files = self.git_provider.diff_files + for f in diff_files: + if f.filename.lower() == filename.lower(): + num_plus_lines = f.num_plus_lines + num_minus_lines = f.num_minus_lines + diff_plus_minus += f" ( +{num_plus_lines}/-{num_minus_lines} )" + break # try to add line numbers link to code suggestions if hasattr(self.git_provider, 'get_line_link'): filename = filename.strip() link = self.git_provider.get_line_link(filename, relevant_line_start=-1) if link: - filename = f"[{filename}]({link})" + diff_plus_minus = f"[{diff_plus_minus}]({link})" + diff_plus_minus= f" {diff_plus_minus}" + + if diff_plus_minus: + filename_publish += diff_plus_minus if self.git_provider.is_supported("gfm_markdown"): - pr_body += f"
          {filename}" - pr_body += f"
            Change summary:
            **{file_change_description}**
          " - # else: - # pr_body += f"{filename}    " + pr_body += f"
          {filename_publish}" + if diff_plus_minus: + pr_body += f"
            Change summary:
            **{file_change_description}**
          " + else: + pr_body += f"
            Change summary:
            **{file_change_description}**
        " if self.git_provider.is_supported("gfm_markdown"): pr_body += "
      |\n" - # else: - # pr_body += "|" else: # if the value is a list, join its items by comma if isinstance(value, list): @@ -330,4 +330,18 @@ class PRDescription: if get_settings().config.verbosity_level >= 2: get_logger().info(f"title:\n{title}\n{pr_body}") - return title, pr_body \ No newline at end of file + return title, pr_body + + def _prepare_file_labels(self): + self.file_label_dict = {} + for file in self.data['pr_files']: + try: + filename = file['filename'].replace("'", "`").replace('"', '`') + changes_summary = file['changes_summary'] + label = file['label'] + if label not in self.file_label_dict: + self.file_label_dict[label] = [] + self.file_label_dict[label].append((filename, changes_summary)) + except Exception as e: + get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") + pass \ No newline at end of file From 429aed04b1b9fa09e516978eec5c21f6a21ae4cc Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 16:32:53 +0200 Subject: [PATCH 12/14] s --- pr_agent/git_providers/gitlab_provider.py | 10 +++++- pr_agent/settings/pr_description_prompts.toml | 4 +-- pr_agent/tools/pr_description.py | 36 +++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index c583b087..7b11ef54 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -115,12 +115,20 @@ class GitLabProvider(GitProvider): if not patch: patch = load_large_diff(filename, new_file_content_str, original_file_content_str) + + # count number of lines added and removed + patch_lines = patch.splitlines(keepends=True) + num_plus_lines = len([line for line in patch_lines if line.startswith('+')]) + num_minus_lines = len([line for line in patch_lines if line.startswith('-')]) diff_files.append( FilePatchInfo(original_file_content_str, new_file_content_str, patch=patch, filename=filename, edit_type=edit_type, - old_filename=None if diff['old_path'] == diff['new_path'] else diff['old_path'])) + old_filename=None if diff['old_path'] == diff['new_path'] else diff['old_path'], + num_plus_lines=num_plus_lines, + num_minus_lines=num_minus_lines, )) + self.diff_files = diff_files return diff_files diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index 2225088d..cf418834 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -33,13 +33,13 @@ class PRType(str, Enum): {%- if enable_file_walkthrough %} class FileWalkthrough(BaseModel): filename: str = Field(description="the relevant file full path") - changes_in_file: str = Field(description="minimal and concise description of the changes in the relevant file") + changes_in_file: str = Field(description="minimal and concise summary of the changes in the relevant file") {%- endif %} {%- if enable_semantic_files_types %} Class FileDescription(BaseModel): filename: str = Field(description="the relevant file full path") - changes_summary: str = Field(description="minimal and concise description of the important changes in the relevant file") + changes_summary: str = Field(description="minimal and concise summary of the changes in the relevant file") label: str = Field(description="a single semantic label that represents a type of code changes that occurred in the File. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formating', 'miscellaneous', ...") {%- endif %} diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 5a074390..950b4f37 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -30,6 +30,11 @@ class PRDescription: ) self.pr_id = self.git_provider.get_pr_id() + if get_settings().pr_description.enable_semantic_files_types and not self.git_provider.is_supported( + "gfm_markdown"): + get_logger().debug(f"Disabling semantic files types for {self.pr_id}") + get_settings().pr_description.enable_semantic_files_types = False + # Initialize the AI handler self.ai_handler = AiHandler() @@ -313,10 +318,11 @@ class PRDescription: filename_publish += diff_plus_minus if self.git_provider.is_supported("gfm_markdown"): pr_body += f"
      {filename_publish}" + file_change_description= self._insert_br_after_x_chars(file_change_description) if diff_plus_minus: - pr_body += f"
        Change summary:
        **{file_change_description}**
      " + pr_body += f"
        Changes summary:
        **{file_change_description}**
    " else: - pr_body += f"
      Change summary:
      **{file_change_description}**
    " + pr_body += f"" if self.git_provider.is_supported("gfm_markdown"): pr_body += "|\n" else: @@ -344,4 +350,28 @@ class PRDescription: self.file_label_dict[label].append((filename, changes_summary)) except Exception as e: get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") - pass \ No newline at end of file + pass + + def _insert_br_after_x_chars(self, text): + """ + Insert
    into a string after a word that increases its length above x characters. + """ + x = 70 + if len(text) < x: + return text + + words = text.split(' ') + new_text = "" + current_length = 0 + + for word in words: + # Check if adding this word exceeds x characters + if current_length + len(word) > x: + new_text += "
    " # Insert line break + current_length = 0 # Reset counter + + # Add the word to the new text + new_text += word + " " + current_length += len(word) + 1 # Add 1 for the space + + return new_text.strip() # Remove trailing space From 93b6d315059acde92253a1055d453a69d1aa56e7 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 16:36:27 +0200 Subject: [PATCH 13/14] s --- pr_agent/settings/pr_description_prompts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/settings/pr_description_prompts.toml b/pr_agent/settings/pr_description_prompts.toml index cf418834..6e0c395e 100644 --- a/pr_agent/settings/pr_description_prompts.toml +++ b/pr_agent/settings/pr_description_prompts.toml @@ -40,7 +40,7 @@ class FileWalkthrough(BaseModel): Class FileDescription(BaseModel): filename: str = Field(description="the relevant file full path") changes_summary: str = Field(description="minimal and concise summary of the changes in the relevant file") - label: str = Field(description="a single semantic label that represents a type of code changes that occurred in the File. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formating', 'miscellaneous', ...") + label: str = Field(description="a single semantic label that represents a type of code changes that occurred in the File. Possible values (partial list): 'bug fix', 'tests', 'enhancement', 'documentation', 'error handling', 'configuration changes', 'dependencies', 'formatting', 'miscellaneous', ...") {%- endif %} Class PRDescription(BaseModel): From a61e492fe1a401c1a2e4963b34c4721215964a5b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 6 Dec 2023 17:01:21 +0200 Subject: [PATCH 14/14] feat: Refactor PR files processing into separate function in pr_description.py --- pr_agent/tools/pr_description.py | 98 ++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 950b4f37..88baaac5 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -281,50 +281,9 @@ class PRDescription: description = file['changes_in_file'] pr_body += f'- `{filename}`: {description}\n' if self.git_provider.is_supported("gfm_markdown"): - pr_body +="\n" + pr_body += "\n" elif 'pr_files' in key.lower(): - pr_body += """\n| | Relevant Files """ - pr_body += "  " * 70 - pr_body += """|\n|-----------|-------------|\n""" - for semantic_label in value.keys(): - s_label = semantic_label.strip("'").strip('"') - if self.git_provider.is_supported("gfm_markdown"): - # pr_body += f"
    {semantic_label['label']}\n\n" - pr_body += f"| **{s_label}** |
    files:
      " - - list_tuples = value[semantic_label] - for filename, file_change_description in list_tuples: - filename = filename.replace("'", "`") - filename_publish = filename.split("/")[-1] - filename_publish = f"**{filename_publish}**" - diff_plus_minus = "" - diff_files = self.git_provider.diff_files - for f in diff_files: - if f.filename.lower() == filename.lower(): - num_plus_lines = f.num_plus_lines - num_minus_lines = f.num_minus_lines - diff_plus_minus += f" ( +{num_plus_lines}/-{num_minus_lines} )" - break - - # try to add line numbers link to code suggestions - if hasattr(self.git_provider, 'get_line_link'): - filename = filename.strip() - link = self.git_provider.get_line_link(filename, relevant_line_start=-1) - if link: - diff_plus_minus = f"[{diff_plus_minus}]({link})" - diff_plus_minus= f" {diff_plus_minus}" - - if diff_plus_minus: - filename_publish += diff_plus_minus - if self.git_provider.is_supported("gfm_markdown"): - pr_body += f"
      {filename_publish}" - file_change_description= self._insert_br_after_x_chars(file_change_description) - if diff_plus_minus: - pr_body += f"
        Changes summary:
        **{file_change_description}**
      " - else: - pr_body += f"
        Changes summary:
        **{file_change_description}**
    " - if self.git_provider.is_supported("gfm_markdown"): - pr_body += "
    |\n" + pr_body = self.process_pr_files_prediction(pr_body, value) else: # if the value is a list, join its items by comma if isinstance(value, list): @@ -352,6 +311,59 @@ class PRDescription: get_logger().error(f"Error preparing file label dict {self.pr_id}: {e}") pass + def process_pr_files_prediction(self, pr_body, value): + if not self.git_provider.is_supported("gfm_markdown"): + get_logger().info(f"Disabling semantic files types for {self.pr_id} since gfm_markdown is not supported") + return pr_body + + try: + pr_body += """\n| | Relevant Files """ + pr_body += "  " * 70 + pr_body += """|\n|-----------|-------------|\n""" + for semantic_label in value.keys(): + s_label = semantic_label.strip("'").strip('"') + if self.git_provider.is_supported("gfm_markdown"): + # pr_body += f"
    {semantic_label['label']}\n\n" + pr_body += f"| **{s_label}** |
    files:
      " + + list_tuples = value[semantic_label] + for filename, file_change_description in list_tuples: + filename = filename.replace("'", "`") + filename_publish = filename.split("/")[-1] + filename_publish = f"**{filename_publish}**" + diff_plus_minus = "" + diff_files = self.git_provider.diff_files + for f in diff_files: + if f.filename.lower() == filename.lower(): + num_plus_lines = f.num_plus_lines + num_minus_lines = f.num_minus_lines + diff_plus_minus += f" ( +{num_plus_lines}/-{num_minus_lines} )" + break + + # try to add line numbers link to code suggestions + if hasattr(self.git_provider, 'get_line_link'): + filename = filename.strip() + link = self.git_provider.get_line_link(filename, relevant_line_start=-1) + if link: + diff_plus_minus = f"[{diff_plus_minus}]({link})" + diff_plus_minus = f" {diff_plus_minus}" + + if diff_plus_minus: + filename_publish += diff_plus_minus + if self.git_provider.is_supported("gfm_markdown"): + pr_body += f"
      {filename_publish}" + file_change_description = self._insert_br_after_x_chars(file_change_description) + if diff_plus_minus: + pr_body += f"
        Changes summary:
        **{file_change_description}**
      " + else: + pr_body += f"
        Changes summary:
        **{file_change_description}**
    " + if self.git_provider.is_supported("gfm_markdown"): + pr_body += "
    |\n" + except Exception as e: + get_logger().error(f"Error processing pr files to markdown {self.pr_id}: {e}") + pass + return pr_body + def _insert_br_after_x_chars(self, text): """ Insert
    into a string after a word that increases its length above x characters.