mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-19 12:00:37 +08:00
Merge pull request #597 from Codium-ai/hl/improve_ui_table
Hl/improve UI table
This commit is contained in:
@ -474,4 +474,13 @@ def clip_tokens(text: str, max_tokens: int, add_three_dots=True) -> str:
|
|||||||
return clipped_text
|
return clipped_text
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().warning(f"Failed to clip tokens: {e}")
|
get_logger().warning(f"Failed to clip tokens: {e}")
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def replace_code_tags(text):
|
||||||
|
"""
|
||||||
|
Replace odd instances of ` with <code> and even instances of ` with </code>
|
||||||
|
"""
|
||||||
|
parts = text.split('`')
|
||||||
|
for i in range(1, len(parts), 2):
|
||||||
|
parts[i] = '<code>' + parts[i] + '</code>'
|
||||||
|
return ''.join(parts)
|
@ -47,14 +47,15 @@ Extra instructions from the user:
|
|||||||
======
|
======
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
The output must be a YAML object equivalent to type PRCodeSuggestions, according to the following Pydantic definitions:
|
The output must be a YAML object equivalent to type $PRCodeSuggestions, according to the following Pydantic definitions:
|
||||||
=====
|
=====
|
||||||
class CodeSuggestion(BaseModel):
|
class CodeSuggestion(BaseModel):
|
||||||
relevant_file: str = Field(description="the relevant file full path")
|
relevant_file: str = Field(description="the relevant file full path")
|
||||||
suggestion_content: str = Field(description="an actionable suggestion for meaningfully improving the new code introduced in the PR")
|
suggestion_content: str = Field(description="an actionable suggestion for meaningfully improving the new code introduced in the PR")
|
||||||
{%- if summarize_mode %}
|
{%- if summarize_mode %}
|
||||||
existing_code: str = Field(description="a short code snippet from a '__new hunk__' section to illustrate the relevant existing code. Don't show the line numbers. Shorten parts of the code ('...') if needed")
|
existing_code: str = Field(description="a short code snippet from a '__new hunk__' section to illustrate the relevant existing code. Don't show the line numbers.")
|
||||||
improved_code: str = Field(description="a short code snippet to illustrate the improved code, after applying the suggestion. Shorten parts of the code ('...') if needed")
|
improved_code: str = Field(description="a short code snippet to illustrate the improved code, after applying the suggestion.")
|
||||||
|
one_sentence_summary:str = Field(description="a short summary of the suggestion action, in a single sentence. Focus on the 'what'. Be general, and avoid method or variable names.")
|
||||||
{%- else %}
|
{%- else %}
|
||||||
existing_code: str = Field(description="a code snippet, demonstrating the relevant code lines from a '__new hunk__' section. It must be contiguous, correctly formatted and indented, and without line numbers")
|
existing_code: str = Field(description="a code snippet, demonstrating the relevant code lines from a '__new hunk__' section. It must be contiguous, correctly formatted and indented, and without line numbers")
|
||||||
improved_code: str = Field(description="a new code snippet, that can be used to replace the relevant lines in '__new hunk__' code. Replacement suggestions should be complete, correctly formatted and indented, and without line numbers")
|
improved_code: str = Field(description="a new code snippet, that can be used to replace the relevant lines in '__new hunk__' code. Replacement suggestions should be complete, correctly formatted and indented, and without line numbers")
|
||||||
@ -75,12 +76,23 @@ code_suggestions:
|
|||||||
src/file1.py
|
src/file1.py
|
||||||
suggestion_content: |-
|
suggestion_content: |-
|
||||||
Add a docstring to func1()
|
Add a docstring to func1()
|
||||||
|
{%- if summarize_mode %}
|
||||||
|
existing_code: |-
|
||||||
|
def func1():
|
||||||
|
improved_code: |-
|
||||||
|
...
|
||||||
|
one_sentence_summary: |-
|
||||||
|
...
|
||||||
|
relevant_lines_start: 12
|
||||||
|
relevant_lines_end: 12
|
||||||
|
{%- else %}
|
||||||
existing_code: |-
|
existing_code: |-
|
||||||
def func1():
|
def func1():
|
||||||
relevant_lines_start: 12
|
relevant_lines_start: 12
|
||||||
relevant_lines_end: 12
|
relevant_lines_end: 12
|
||||||
improved_code: |-
|
improved_code: |-
|
||||||
...
|
...
|
||||||
|
{%- endif %}
|
||||||
label: |-
|
label: |-
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
@ -8,12 +8,13 @@ from pr_agent.algo.ai_handlers.base_ai_handler import BaseAiHandler
|
|||||||
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
|
from pr_agent.algo.ai_handlers.litellm_ai_handler import LiteLLMAIHandler
|
||||||
from pr_agent.algo.pr_processing import get_pr_diff, get_pr_multi_diffs, retry_with_fallback_models
|
from pr_agent.algo.pr_processing import get_pr_diff, get_pr_multi_diffs, retry_with_fallback_models
|
||||||
from pr_agent.algo.token_handler import TokenHandler
|
from pr_agent.algo.token_handler import TokenHandler
|
||||||
from pr_agent.algo.utils import load_yaml
|
from pr_agent.algo.utils import load_yaml, replace_code_tags
|
||||||
from pr_agent.config_loader import get_settings
|
from pr_agent.config_loader import get_settings
|
||||||
from pr_agent.git_providers import get_git_provider
|
from pr_agent.git_providers import get_git_provider
|
||||||
from pr_agent.git_providers.git_provider import get_main_pr_language
|
from pr_agent.git_providers.git_provider import get_main_pr_language
|
||||||
from pr_agent.log import get_logger
|
from pr_agent.log import get_logger
|
||||||
|
from pr_agent.tools.pr_description import insert_br_after_x_chars
|
||||||
|
import difflib
|
||||||
|
|
||||||
class PRCodeSuggestions:
|
class PRCodeSuggestions:
|
||||||
def __init__(self, pr_url: str, cli_mode=False, args: list = None,
|
def __init__(self, pr_url: str, cli_mode=False, args: list = None,
|
||||||
@ -299,7 +300,7 @@ class PRCodeSuggestions:
|
|||||||
|
|
||||||
def publish_summarizes_suggestions(self, data: Dict):
|
def publish_summarizes_suggestions(self, data: Dict):
|
||||||
try:
|
try:
|
||||||
data_markdown = "## PR Code Suggestions\n\n"
|
pr_body = "## PR Code Suggestions\n\n"
|
||||||
|
|
||||||
language_extension_map_org = get_settings().language_extension_map_org
|
language_extension_map_org = get_settings().language_extension_map_org
|
||||||
extension_to_language = {}
|
extension_to_language = {}
|
||||||
@ -307,30 +308,77 @@ class PRCodeSuggestions:
|
|||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
extension_to_language[ext] = language
|
extension_to_language[ext] = language
|
||||||
|
|
||||||
for s in data['code_suggestions']:
|
pr_body += "<table>"
|
||||||
try:
|
header = f"Suggestions"
|
||||||
extension_s = s['relevant_file'].rsplit('.')[-1]
|
delta = 77
|
||||||
code_snippet_link = self.git_provider.get_line_link(s['relevant_file'], s['relevant_lines_start'],
|
header += " " * delta
|
||||||
s['relevant_lines_end'])
|
pr_body += f"""<thead><tr><th></th><th>{header}</th></tr></thead>"""
|
||||||
label = s['label'].strip()
|
pr_body += """<tbody>"""
|
||||||
data_markdown += f"\n💡 [{label}]\n\n**{s['suggestion_content'].rstrip().rstrip()}**\n\n"
|
suggestions_labels = dict()
|
||||||
if code_snippet_link:
|
# add all suggestions related to to each label
|
||||||
data_markdown += f" File: [{s['relevant_file']} ({s['relevant_lines_start']}-{s['relevant_lines_end']})]({code_snippet_link})\n\n"
|
for suggestion in data['code_suggestions']:
|
||||||
|
label = suggestion['label'].strip().strip("'").strip('"')
|
||||||
|
if label not in suggestions_labels:
|
||||||
|
suggestions_labels[label] = []
|
||||||
|
suggestions_labels[label].append(suggestion)
|
||||||
|
|
||||||
|
for label, suggestions in suggestions_labels.items():
|
||||||
|
pr_body += f"""<tr><td><strong>{label}</strong></td>"""
|
||||||
|
pr_body += f"""<td>"""
|
||||||
|
# pr_body += f"""<details><summary>{len(suggestions)} suggestions</summary>"""
|
||||||
|
pr_body += f"""<table>"""
|
||||||
|
for suggestion in suggestions:
|
||||||
|
|
||||||
|
relevant_file = suggestion['relevant_file'].strip()
|
||||||
|
relevant_lines_start = int(suggestion['relevant_lines_start'])
|
||||||
|
relevant_lines_end = int(suggestion['relevant_lines_end'])
|
||||||
|
range_str = ""
|
||||||
|
if relevant_lines_start == relevant_lines_end:
|
||||||
|
range_str = f"[{relevant_lines_start}]"
|
||||||
else:
|
else:
|
||||||
data_markdown += f"File: {s['relevant_file']} ({s['relevant_lines_start']}-{s['relevant_lines_end']})\n\n"
|
range_str = f"[{relevant_lines_start}-{relevant_lines_end}]"
|
||||||
if self.git_provider.is_supported("gfm_markdown"):
|
code_snippet_link = self.git_provider.get_line_link(relevant_file, relevant_lines_start,
|
||||||
data_markdown += "<details> <summary> Example code:</summary>\n\n"
|
relevant_lines_end)
|
||||||
data_markdown += f"___\n\n"
|
# add html table for each suggestion
|
||||||
language_name = "python"
|
|
||||||
if extension_s and (extension_s in extension_to_language):
|
suggestion_content = suggestion['suggestion_content'].rstrip().rstrip()
|
||||||
language_name = extension_to_language[extension_s]
|
|
||||||
data_markdown += f"Existing code:\n```{language_name}\n{s['existing_code'].rstrip()}\n```\n"
|
suggestion_content = insert_br_after_x_chars(suggestion_content, 90)
|
||||||
data_markdown += f"Improved code:\n```{language_name}\n{s['improved_code'].rstrip()}\n```\n"
|
# pr_body += f"<tr><td><details><summary>{suggestion_content}</summary>"
|
||||||
if self.git_provider.is_supported("gfm_markdown"):
|
existing_code = suggestion['existing_code'].rstrip()+"\n"
|
||||||
data_markdown += "</details>\n"
|
improved_code = suggestion['improved_code'].rstrip()+"\n"
|
||||||
data_markdown += "\n___\n\n"
|
|
||||||
except Exception as e:
|
diff = difflib.unified_diff(existing_code.split('\n'),
|
||||||
get_logger().error(f"Could not parse suggestion: {s}, error: {e}")
|
improved_code.split('\n'), n=999)
|
||||||
self.git_provider.publish_comment(data_markdown)
|
patch_orig = "\n".join(diff)
|
||||||
|
patch = "\n".join(patch_orig.splitlines()[5:]).strip('\n')
|
||||||
|
|
||||||
|
example_code = ""
|
||||||
|
example_code += f"```diff\n{patch}\n```\n"
|
||||||
|
|
||||||
|
pr_body += f"""<tr><td>"""
|
||||||
|
suggestion_summary = suggestion['one_sentence_summary'].strip()
|
||||||
|
if '`' in suggestion_summary:
|
||||||
|
suggestion_summary = replace_code_tags(suggestion_summary)
|
||||||
|
suggestion_summary = suggestion_summary + max((77-len(suggestion_summary)), 0)*" "
|
||||||
|
pr_body += f"""\n\n<details><summary>{suggestion_summary}</summary>\n\n___\n\n"""
|
||||||
|
|
||||||
|
pr_body += f"""
|
||||||
|
|
||||||
|
|
||||||
|
**{suggestion_content}**
|
||||||
|
|
||||||
|
[{relevant_file} {range_str}]({code_snippet_link})
|
||||||
|
|
||||||
|
{example_code}
|
||||||
|
"""
|
||||||
|
pr_body += f"</details>"
|
||||||
|
pr_body += f"</td></tr>"
|
||||||
|
|
||||||
|
pr_body += """</table>"""
|
||||||
|
# pr_body += "</details>"
|
||||||
|
pr_body += """</td></tr>"""
|
||||||
|
pr_body += """</tr></tbody></table>"""
|
||||||
|
self.git_provider.publish_comment(pr_body)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
get_logger().info(f"Failed to publish summarized code suggestions, error: {e}")
|
get_logger().info(f"Failed to publish summarized code suggestions, error: {e}")
|
||||||
|
@ -399,7 +399,7 @@ class PRDescription:
|
|||||||
filename = filename.strip()
|
filename = filename.strip()
|
||||||
link = self.git_provider.get_line_link(filename, relevant_line_start=-1)
|
link = self.git_provider.get_line_link(filename, relevant_line_start=-1)
|
||||||
|
|
||||||
file_change_description = self._insert_br_after_x_chars(file_change_description, x=(delta - 5))
|
file_change_description = insert_br_after_x_chars(file_change_description, x=(delta - 5))
|
||||||
pr_body += f"""
|
pr_body += f"""
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@ -427,25 +427,25 @@ class PRDescription:
|
|||||||
pass
|
pass
|
||||||
return pr_body
|
return pr_body
|
||||||
|
|
||||||
def _insert_br_after_x_chars(self, text, x=70):
|
def insert_br_after_x_chars(text, x=70):
|
||||||
"""
|
"""
|
||||||
Insert <br> into a string after a word that increases its length above x characters.
|
Insert <br> into a string after a word that increases its length above x characters.
|
||||||
"""
|
"""
|
||||||
if len(text) < x:
|
if len(text) < x:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
words = text.split(' ')
|
words = text.split(' ')
|
||||||
new_text = ""
|
new_text = ""
|
||||||
current_length = 0
|
current_length = 0
|
||||||
|
|
||||||
for word in words:
|
for word in words:
|
||||||
# Check if adding this word exceeds x characters
|
# Check if adding this word exceeds x characters
|
||||||
if current_length + len(word) > x:
|
if current_length + len(word) > x:
|
||||||
new_text += "<br>" # Insert line break
|
new_text += "<br>" # Insert line break
|
||||||
current_length = 0 # Reset counter
|
current_length = 0 # Reset counter
|
||||||
|
|
||||||
# Add the word to the new text
|
# Add the word to the new text
|
||||||
new_text += word + " "
|
new_text += word + " "
|
||||||
current_length += len(word) + 1 # Add 1 for the space
|
current_length += len(word) + 1 # Add 1 for the space
|
||||||
|
|
||||||
return new_text.strip() # Remove trailing space
|
return new_text.strip() # Remove trailing space
|
||||||
|
Reference in New Issue
Block a user