Refactor pr_code_suggestions logic and update prompts for clarity and consistency

This commit is contained in:
mrT23
2024-05-13 18:03:13 +03:00
parent 6c0837491c
commit 95c7b3f55c
3 changed files with 27 additions and 41 deletions

View File

@ -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: |
... ...
``` ```

View File

@ -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

View File

@ -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(