refactor: Extract reasoning tag handling to separate module

This commit is contained in:
Paul Gauthier (aider) 2025-03-07 16:10:21 -08:00
parent d9551b3106
commit e7f16f07f7
2 changed files with 72 additions and 43 deletions

View file

@ -30,16 +30,16 @@ from aider.llm import litellm
from aider.models import RETRY_TIMEOUT
from aider.repo import ANY_GIT_ERROR, GitRepo
from aider.repomap import RepoMap
from aider.reasoning_tags import (
REASONING_TAG, REASONING_START, REASONING_END,
replace_reasoning_tags, format_reasoning_content, detect_reasoning_tag
)
from aider.run_cmd import run_cmd
from aider.utils import format_content, format_messages, format_tokens, is_image_file
from ..dump import dump # noqa: F401
from .chat_chunks import ChatChunks
REASONING_TAG = "thinking-content-" + "7bbeb8e1441453ad999a0bbba8a46d4b"
REASONING_START = "> Thinking ..."
REASONING_END = "> ... done thinking.\n\n------"
class UnknownEditFormat(ValueError):
def __init__(self, edit_format, valid_formats):
@ -1697,39 +1697,7 @@ class Coder:
if args:
self.io.ai_output(json.dumps(args, indent=4))
def replace_reasoning_tags(self, text):
"""
Replace opening and closing reasoning tags with standard formatting.
Ensures exactly one blank line before START and END markers.
Args:
text (str): The text containing the tags
Returns:
str: Text with reasoning tags replaced with standard format
"""
if not text:
return text
# Helper function to ensure exactly one blank line before replacement
def ensure_one_blank_line(match):
content_before = match.string[: match.start()].rstrip()
# If we're not at the start of the text, add exactly one blank line
if content_before:
return f"{content_before}\n\n{match.group(1)}"
# If we're at the start, don't add extra newlines
return match.group(1)
# Replace opening tag with proper spacing
text = re.sub(f"\\s*<{self.reasoning_tag_name}>\\s*", f"\n{REASONING_START}\n\n", text)
# Replace closing tag with proper spacing
text = re.sub(f"\\s*</{self.reasoning_tag_name}>\\s*", f"\n\n{REASONING_END}\n\n", text)
# Clean up any excessive newlines (more than 2 consecutive)
text = re.sub(r"\n{3,}", "\n\n", text)
return text
# Moved to aider/reasoning_tags.py
def show_send_output(self, completion):
if self.verbose:
@ -1774,12 +1742,7 @@ class Coder:
show_resp = self.render_incremental_response(True)
if reasoning_content:
formatted_reasoning = (
f"<{self.reasoning_tag_name}>\n\n"
+ reasoning_content
+ f"\n\n</{self.reasoning_tag_name}>"
)
formatted_reasoning = self.replace_reasoning_tags(formatted_reasoning) + "\n\n"
formatted_reasoning = format_reasoning_content(reasoning_content, self.reasoning_tag_name) + "\n\n"
show_resp = formatted_reasoning + show_resp
self.io.assistant_output(show_resp, pretty=self.show_pretty())

66
aider/reasoning_tags.py Normal file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env python
import re
# Standard tag identifier
REASONING_TAG = "thinking-content-" + "7bbeb8e1441453ad999a0bbba8a46d4b"
# Output formatting
REASONING_START = "> Thinking ..."
REASONING_END = "> ... done thinking.\n\n------"
def replace_reasoning_tags(text, tag_name):
"""
Replace opening and closing reasoning tags with standard formatting.
Ensures exactly one blank line before START and END markers.
Args:
text (str): The text containing the tags
tag_name (str): The name of the tag to replace
Returns:
str: Text with reasoning tags replaced with standard format
"""
if not text:
return text
# Replace opening tag with proper spacing
text = re.sub(f"\\s*<{tag_name}>\\s*", f"\n{REASONING_START}\n\n", text)
# Replace closing tag with proper spacing
text = re.sub(f"\\s*</{tag_name}>\\s*", f"\n\n{REASONING_END}\n\n", text)
# Clean up any excessive newlines (more than 2 consecutive)
text = re.sub(r"\n{3,}", "\n\n", text)
return text
def format_reasoning_content(reasoning_content, tag_name):
"""
Format reasoning content with appropriate tags.
Args:
reasoning_content (str): The content to format
tag_name (str): The tag name to use
Returns:
str: Formatted reasoning content with tags
"""
if not reasoning_content:
return ""
formatted = f"<{tag_name}>\n\n{reasoning_content}\n\n</{tag_name}>"
return replace_reasoning_tags(formatted, tag_name)
def detect_reasoning_tag(text, tag_name):
"""
Detect if text contains reasoning tags.
Args:
text (str): The text to check
tag_name (str): The tag name to look for
Returns:
bool: True if text contains reasoning tags
"""
opening_pattern = f"<{tag_name}>"
return opening_pattern in text