diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 72a0624c51..0bdd1f1e4b 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -868,6 +868,44 @@ def try_fix_yaml(response_text: str, except: pass + # 5.5 fallback - try to normalize diff-style removal markers ('-') within list items + response_text_lines_copy = response_text_lines.copy() + modified = False + + for i, line in enumerate(response_text_lines_copy): + if line.startswith('+'): + response_text_lines_copy[i] = ' ' + line[1:] + modified = True + + # normalize lines starting with '-'. Distinguish real YAML list items from diff deletions. + for i, line in enumerate(response_text_lines_copy): + if not line.startswith('-'): + continue + + remainder = line[1:] + if line.startswith('- '): + second_char = remainder[1] if len(remainder) > 1 else '' + if second_char and second_char not in (' ', '\t', '+', '-'): + continue # real list item → keep as-is + + # treat it as a diff "removed" marker inside block content + cleaned = remainder + while cleaned and cleaned[0] in ('+', '-'): + cleaned = cleaned[1:] + if cleaned and cleaned[0] not in (' ', '\t'): + cleaned = ' ' + cleaned + if cleaned != line: + response_text_lines_copy[i] = cleaned + modified = True + if modified: + try: + data = yaml.safe_load('\n'.join(response_text_lines_copy)) + get_logger().info("Successfully parsed AI prediction after normalizing diff removal markers") + return data + except: + pass + + # sixth fallback - replace tabs with spaces if '\t' in response_text: response_text_copy = copy.deepcopy(response_text) diff --git a/tests/unittest/test_try_fix_yaml.py b/tests/unittest/test_try_fix_yaml.py index 03823d7bb0..7e52c11b09 100644 --- a/tests/unittest/test_try_fix_yaml.py +++ b/tests/unittest/test_try_fix_yaml.py @@ -244,3 +244,36 @@ def test_wrong_indentation_code_block_scalar(self): ''' expected_output = {'code_suggestions': [{'relevant_file': 'a.c\n', 'existing_code': ' int sum(int a, int b) {\n return a + b;\n }\n\n int sub(int a, int b) {\n return a - b;\n }\n'}]} assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output + + def test_diff_markers_removed_within_list_item(self): + """ + Ensures diff-style '-' markers nested inside list items are normalised so the YAML parses + into the expected structure. + """ + review_text = '''\ +code_suggestions: +- relevant_file: | + example.rb + existing_code: | ++ puts 'hello' ++ puts 'world' +- relevant_file: | +- example.py +- existing_code: | +-+ print('hello') +-+ print('world') +''' + expected_output = { + 'code_suggestions': [ + { + 'relevant_file': 'example.rb\n', + 'existing_code': "puts 'hello'\nputs 'world'\n" + }, + { + 'relevant_file': 'example.py\n', + 'existing_code': "print('hello')\nprint('world')\n" + } + ] + } + + assert try_fix_yaml(review_text, first_key='code_suggestions', last_key='existing_code') == expected_output \ No newline at end of file