mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-03 20:30:41 +08:00
Refactor pr_code_suggestions logic and update prompts for clarity and consistency
This commit is contained in:
@ -40,7 +40,7 @@ Specific instructions for generating code suggestions:
|
|||||||
- Suggestions should not repeat code already present in the '__new hunk__' sections.
|
- Suggestions should not repeat code already present in the '__new hunk__' sections.
|
||||||
- Provide the exact line numbers range (inclusive) for each suggestion. Use the line numbers from the '__new hunk__' sections.
|
- Provide the exact line numbers range (inclusive) for each suggestion. Use the line numbers from the '__new hunk__' sections.
|
||||||
- When quoting variables or names from the code, use backticks (`) instead of single quote (').
|
- When quoting variables or names from the code, use backticks (`) instead of single quote (').
|
||||||
- Be aware that you are reviewing a PR code diff, and that the entire codebase is not available for you as context. Hence, don't suggest changes that require knowledge of the entire codebase.
|
- Take into account that you are reviewing a PR code diff, and that the entire codebase is not available for you as context. Hence, avoid suggestions that might conflict with unseen parts of the codebase."
|
||||||
|
|
||||||
|
|
||||||
{%- if extra_instructions %}
|
{%- if extra_instructions %}
|
||||||
@ -59,14 +59,9 @@ class CodeSuggestion(BaseModel):
|
|||||||
relevant_file: str = Field(description="the relevant file full path")
|
relevant_file: str = Field(description="the relevant file full path")
|
||||||
language: str = Field(description="the code language of the relevant file")
|
language: str = Field(description="the code language of the relevant file")
|
||||||
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 not commitable_code_suggestions_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.")
|
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.")
|
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.")
|
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 %}
|
|
||||||
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. Use abbreviations if needed")
|
|
||||||
improved_code: str = Field(description="If relevant, 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". Retrun '...' if not applicable")
|
|
||||||
{%- endif %}
|
|
||||||
relevant_lines_start: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion starts (inclusive). Should be derived from the hunk line numbers, and correspond to the 'existing code' snippet above")
|
relevant_lines_start: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion starts (inclusive). Should be derived from the hunk line numbers, and correspond to the 'existing code' snippet above")
|
||||||
relevant_lines_end: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion ends (inclusive). Should be derived from the hunk line numbers, and correspond to the 'existing code' snippet above")
|
relevant_lines_end: int = Field(description="The relevant line number, from a '__new hunk__' section, where the suggestion ends (inclusive). Should be derived from the hunk line numbers, and correspond to the 'existing code' snippet above")
|
||||||
label: str = Field(description="a single label for the suggestion, to help the user understand the suggestion type. For example: 'security', 'possible bug', 'possible issue', 'performance', 'enhancement', 'best practice', 'maintainability', etc. Other labels are also allowed")
|
label: str = Field(description="a single label for the suggestion, to help the user understand the suggestion type. For example: 'security', 'possible bug', 'possible issue', 'performance', 'enhancement', 'best practice', 'maintainability', etc. Other labels are also allowed")
|
||||||
@ -85,7 +80,6 @@ code_suggestions:
|
|||||||
python
|
python
|
||||||
suggestion_content: |
|
suggestion_content: |
|
||||||
...
|
...
|
||||||
{%- if not commitable_code_suggestions_mode %}
|
|
||||||
existing_code: |
|
existing_code: |
|
||||||
...
|
...
|
||||||
improved_code: |
|
improved_code: |
|
||||||
@ -94,14 +88,6 @@ code_suggestions:
|
|||||||
...
|
...
|
||||||
relevant_lines_start: 12
|
relevant_lines_start: 12
|
||||||
relevant_lines_end: 13
|
relevant_lines_end: 13
|
||||||
{%- else %}
|
|
||||||
existing_code: |
|
|
||||||
...
|
|
||||||
relevant_lines_start: 12
|
|
||||||
relevant_lines_end: 13
|
|
||||||
improved_code: |
|
|
||||||
...
|
|
||||||
{%- endif %}
|
|
||||||
label: |
|
label: |
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
@ -46,9 +46,7 @@ __old hunk__
|
|||||||
The output must be a YAML object equivalent to type $PRCodeSuggestionsFeedback, according to the following Pydantic definitions:
|
The output must be a YAML object equivalent to type $PRCodeSuggestionsFeedback, according to the following Pydantic definitions:
|
||||||
=====
|
=====
|
||||||
class CodeSuggestionFeedback(BaseModel):
|
class CodeSuggestionFeedback(BaseModel):
|
||||||
{%- if not commitable_code_suggestions_mode %}
|
|
||||||
suggestion_summary: str = Field(description="repeated from the input")
|
suggestion_summary: str = Field(description="repeated from the input")
|
||||||
{%- endif %}
|
|
||||||
relevant_file: str = Field(description="repeated from the input")
|
relevant_file: str = Field(description="repeated from the input")
|
||||||
suggestion_score: int = Field(description="The actual output - the score of the suggestion, from 0 to 10. Give 0 if the suggestion is plain wrong. Otherwise, give a score from 1 to 10 (inclusive), where 1 is the lowest and 10 is the highest.")
|
suggestion_score: int = Field(description="The actual output - the score of the suggestion, from 0 to 10. Give 0 if the suggestion is plain wrong. Otherwise, give a score from 1 to 10 (inclusive), where 1 is the lowest and 10 is the highest.")
|
||||||
why: str = Field(description="Short and concise explanation of why the suggestion received the score (one to two sentences).")
|
why: str = Field(description="Short and concise explanation of why the suggestion received the score (one to two sentences).")
|
||||||
@ -61,13 +59,9 @@ class PRCodeSuggestionsFeedback(BaseModel):
|
|||||||
Example output:
|
Example output:
|
||||||
```yaml
|
```yaml
|
||||||
code_suggestions:
|
code_suggestions:
|
||||||
{%- if not commitable_code_suggestions_mode %}
|
|
||||||
- suggestion_content: |
|
- suggestion_content: |
|
||||||
Use a more descriptive variable name here
|
Use a more descriptive variable name here
|
||||||
relevant_file: "src/file1.py"
|
relevant_file: "src/file1.py"
|
||||||
{%- else %}
|
|
||||||
- relevant_file: "src/file1.py"
|
|
||||||
{%- endif %}
|
|
||||||
suggestion_score: 6
|
suggestion_score: 6
|
||||||
why: |
|
why: |
|
||||||
The variable name 't' is not descriptive enough
|
The variable name 't' is not descriptive enough
|
||||||
|
@ -57,7 +57,6 @@ class PRCodeSuggestions:
|
|||||||
"language": self.main_language,
|
"language": self.main_language,
|
||||||
"diff": "", # empty diff for initial calculation
|
"diff": "", # empty diff for initial calculation
|
||||||
"num_code_suggestions": num_code_suggestions,
|
"num_code_suggestions": num_code_suggestions,
|
||||||
"commitable_code_suggestions_mode": get_settings().pr_code_suggestions.commitable_code_suggestions,
|
|
||||||
"extra_instructions": get_settings().pr_code_suggestions.extra_instructions,
|
"extra_instructions": get_settings().pr_code_suggestions.extra_instructions,
|
||||||
"commit_messages_str": self.git_provider.get_commit_messages(),
|
"commit_messages_str": self.git_provider.get_commit_messages(),
|
||||||
}
|
}
|
||||||
@ -106,7 +105,8 @@ class PRCodeSuggestions:
|
|||||||
|
|
||||||
if get_settings().config.publish_output:
|
if get_settings().config.publish_output:
|
||||||
self.git_provider.remove_initial_comment()
|
self.git_provider.remove_initial_comment()
|
||||||
if (not get_settings().pr_code_suggestions.commitable_code_suggestions) and self.git_provider.is_supported("gfm_markdown"):
|
if ((not get_settings().pr_code_suggestions.commitable_code_suggestions) and
|
||||||
|
self.git_provider.is_supported("gfm_markdown")):
|
||||||
|
|
||||||
# generate summarized suggestions
|
# generate summarized suggestions
|
||||||
pr_body = self.generate_summarized_suggestions(data)
|
pr_body = self.generate_summarized_suggestions(data)
|
||||||
@ -226,8 +226,8 @@ class PRCodeSuggestions:
|
|||||||
one_sentence_summary_list = []
|
one_sentence_summary_list = []
|
||||||
for i, suggestion in enumerate(data['code_suggestions']):
|
for i, suggestion in enumerate(data['code_suggestions']):
|
||||||
try:
|
try:
|
||||||
if not get_settings().pr_code_suggestions.commitable_code_suggestions:
|
if (not suggestion or 'one_sentence_summary' not in suggestion or
|
||||||
if not suggestion or 'one_sentence_summary' not in suggestion or 'label' not in suggestion or 'relevant_file' not in suggestion:
|
'label' not in suggestion or 'relevant_file' not in suggestion):
|
||||||
get_logger().debug(f"Skipping suggestion {i + 1}, because it is invalid: {suggestion}")
|
get_logger().debug(f"Skipping suggestion {i + 1}, because it is invalid: {suggestion}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -241,10 +241,13 @@ class PRCodeSuggestions:
|
|||||||
|
|
||||||
if ('existing_code' in suggestion) and ('improved_code' in suggestion):
|
if ('existing_code' in suggestion) and ('improved_code' in suggestion):
|
||||||
if suggestion['existing_code'] == suggestion['improved_code']:
|
if suggestion['existing_code'] == suggestion['improved_code']:
|
||||||
get_logger().debug(f"skipping improved suggestion {i + 1}, because equal to existing code: {suggestion['existing_code']}")
|
get_logger().debug(
|
||||||
|
f"edited improved suggestion {i + 1}, because equal to existing code: {suggestion['existing_code']}")
|
||||||
|
if get_settings().pr_code_suggestions.commitable_code_suggestions:
|
||||||
|
suggestion['improved_code'] = "" # we need 'existing_code' to locate the code in the PR
|
||||||
|
else:
|
||||||
suggestion['existing_code'] = ""
|
suggestion['existing_code'] = ""
|
||||||
suggestion = self._truncate_if_needed(suggestion)
|
suggestion = self._truncate_if_needed(suggestion)
|
||||||
if not get_settings().pr_code_suggestions.commitable_code_suggestions:
|
|
||||||
one_sentence_summary_list.append(suggestion['one_sentence_summary'])
|
one_sentence_summary_list.append(suggestion['one_sentence_summary'])
|
||||||
suggestion_list.append(suggestion)
|
suggestion_list.append(suggestion)
|
||||||
else:
|
else:
|
||||||
@ -278,6 +281,9 @@ class PRCodeSuggestions:
|
|||||||
if new_code_snippet:
|
if new_code_snippet:
|
||||||
new_code_snippet = self.dedent_code(relevant_file, relevant_lines_start, new_code_snippet)
|
new_code_snippet = self.dedent_code(relevant_file, relevant_lines_start, new_code_snippet)
|
||||||
|
|
||||||
|
if d.get('score'):
|
||||||
|
body = f"**Suggestion:** {content} [{label}, importance: {d.get('score')}]\n```suggestion\n" + new_code_snippet + "\n```"
|
||||||
|
else:
|
||||||
body = f"**Suggestion:** {content} [{label}]\n```suggestion\n" + new_code_snippet + "\n```"
|
body = f"**Suggestion:** {content} [{label}]\n```suggestion\n" + new_code_snippet + "\n```"
|
||||||
code_suggestions.append({'body': body, 'relevant_file': relevant_file,
|
code_suggestions.append({'body': body, 'relevant_file': relevant_file,
|
||||||
'relevant_lines_start': relevant_lines_start,
|
'relevant_lines_start': relevant_lines_start,
|
||||||
@ -327,7 +333,8 @@ class PRCodeSuggestions:
|
|||||||
self.patches_diff_list = get_pr_multi_diffs(self.git_provider, self.token_handler, model,
|
self.patches_diff_list = get_pr_multi_diffs(self.git_provider, self.token_handler, model,
|
||||||
max_calls=get_settings().pr_code_suggestions.max_number_of_calls)
|
max_calls=get_settings().pr_code_suggestions.max_number_of_calls)
|
||||||
if self.patches_diff_list:
|
if self.patches_diff_list:
|
||||||
get_logger().debug(f"PR diff", artifact=self.patches_diff_list)
|
get_logger().info(f"Number of PR chunk calls: {len(self.patches_diff_list)}")
|
||||||
|
get_logger().debug(f"PR diff:", artifact=self.patches_diff_list)
|
||||||
|
|
||||||
# parallelize calls to AI:
|
# parallelize calls to AI:
|
||||||
if get_settings().pr_code_suggestions.parallel_calls:
|
if get_settings().pr_code_suggestions.parallel_calls:
|
||||||
@ -342,10 +349,10 @@ class PRCodeSuggestions:
|
|||||||
|
|
||||||
|
|
||||||
data = {"code_suggestions": []}
|
data = {"code_suggestions": []}
|
||||||
for i, predictions in enumerate(prediction_list):
|
for predictions in enumerate(prediction_list):
|
||||||
if "code_suggestions" in predictions:
|
if "code_suggestions" in enumerate(predictions):
|
||||||
score_threshold = max(1,get_settings().pr_code_suggestions.suggestions_score_threshold)
|
score_threshold = max(1,get_settings().pr_code_suggestions.suggestions_score_threshold)
|
||||||
for prediction in predictions["code_suggestions"]:
|
for i, prediction in predictions["code_suggestions"]:
|
||||||
try:
|
try:
|
||||||
if get_settings().pr_code_suggestions.self_reflect_on_suggestions:
|
if get_settings().pr_code_suggestions.self_reflect_on_suggestions:
|
||||||
score = int(prediction["score"])
|
score = int(prediction["score"])
|
||||||
@ -547,8 +554,7 @@ class PRCodeSuggestions:
|
|||||||
variables = {'suggestion_list': suggestion_list,
|
variables = {'suggestion_list': suggestion_list,
|
||||||
'suggestion_str': suggestion_str,
|
'suggestion_str': suggestion_str,
|
||||||
"diff": patches_diff,
|
"diff": patches_diff,
|
||||||
'num_code_suggestions': len(suggestion_list),
|
'num_code_suggestions': len(suggestion_list)}
|
||||||
'commitable_code_suggestions_mode': get_settings().pr_code_suggestions.commitable_code_suggestions,}
|
|
||||||
model = get_settings().config.model
|
model = get_settings().config.model
|
||||||
environment = Environment(undefined=StrictUndefined)
|
environment = Environment(undefined=StrictUndefined)
|
||||||
system_prompt_reflect = environment.from_string(get_settings().pr_code_suggestions_reflect_prompt.system).render(
|
system_prompt_reflect = environment.from_string(get_settings().pr_code_suggestions_reflect_prompt.system).render(
|
||||||
|
Reference in New Issue
Block a user