From f33867bb128e45705abb8b5c4fb9c61ca067f73c Mon Sep 17 00:00:00 2001 From: retoor Date: Mon, 10 Nov 2025 10:29:27 +0100 Subject: [PATCH] feat: bump version to 1.51.0 feat: add reasoning extraction and cleaning feat: update autonomous mode to handle reasoning and task completion markers refactor: extract reasoning and clean content in assistant docs: update system message to include reasoning and task completion instructions fix: improve task completion detection by lowercasing content --- rp/autonomous/detection.py | 13 +++++++++---- rp/autonomous/mode.py | 31 ++++++++++++++++++++++++++++++- rp/core/assistant.py | 11 +++++++++-- rp/core/context.py | 8 ++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/rp/autonomous/detection.py b/rp/autonomous/detection.py index ce953be..c04c695 100644 --- a/rp/autonomous/detection.py +++ b/rp/autonomous/detection.py @@ -8,7 +8,12 @@ def is_task_complete(response, iteration): if "choices" not in response or not response["choices"]: return True message = response["choices"][0]["message"] - content = message.get("content", "").lower() + content = message.get("content", "") + + if "[TASK_COMPLETE]" in content: + return True + + content_lower = content.lower() completion_keywords = [ "task complete", "task is complete", @@ -36,9 +41,9 @@ def is_task_complete(response, iteration): "what can i do for you", ] has_tool_calls = "tool_calls" in message and message["tool_calls"] - mentions_completion = any((keyword in content for keyword in completion_keywords)) - mentions_error = any((keyword in content for keyword in error_keywords)) - is_simple_response = any((keyword in content for keyword in simple_response_keywords)) + mentions_completion = any((keyword in content_lower for keyword in completion_keywords)) + mentions_error = any((keyword in content_lower for keyword in error_keywords)) + is_simple_response = any((keyword in content_lower for keyword in simple_response_keywords)) if mentions_error: return True if mentions_completion and (not has_tool_calls): diff --git a/rp/autonomous/mode.py b/rp/autonomous/mode.py index 64e01b0..b7c0adc 100644 --- a/rp/autonomous/mode.py +++ b/rp/autonomous/mode.py @@ -13,6 +13,29 @@ from rp.ui.progress import ProgressIndicator logger = logging.getLogger("rp") +def extract_reasoning_and_clean_content(content): + """ + Extract reasoning from content and strip the [TASK_COMPLETE] marker. + + Returns: + tuple: (reasoning, cleaned_content) + """ + reasoning = None + lines = content.split('\n') + cleaned_lines = [] + + for line in lines: + if line.strip().startswith('REASONING:'): + reasoning = line.strip()[10:].strip() + else: + cleaned_lines.append(line) + + cleaned_content = '\n'.join(cleaned_lines) + cleaned_content = cleaned_content.replace('[TASK_COMPLETE]', '').strip() + + return reasoning, cleaned_content + + def sanitize_for_json(obj): if isinstance(obj, bytes): return base64.b64encode(obj).decode("utf-8") @@ -131,9 +154,15 @@ def process_response_autonomous(assistant, response): print(f"{Colors.YELLOW}💰 Cost: ${cost:.4f} | Total: ${total_cost:.4f}{Colors.RESET}") return process_response_autonomous(assistant, follow_up) content = message.get("content", "") + + reasoning, cleaned_content = extract_reasoning_and_clean_content(content) + + if reasoning: + print(f"{Colors.BLUE}💭 Reasoning: {reasoning}{Colors.RESET}") + from rp.ui import render_markdown - return render_markdown(content, assistant.syntax_highlighting) + return render_markdown(cleaned_content, assistant.syntax_highlighting) def execute_single_tool(assistant, func_name, arguments): diff --git a/rp/core/assistant.py b/rp/core/assistant.py index 73dc9eb..633beba 100644 --- a/rp/core/assistant.py +++ b/rp/core/assistant.py @@ -336,9 +336,16 @@ class Assistant: ) return self.process_response(follow_up) content = message.get("content", "") + + from rp.autonomous.mode import extract_reasoning_and_clean_content + reasoning, cleaned_content = extract_reasoning_and_clean_content(content) + + if reasoning: + print(f"{Colors.BLUE}💭 Reasoning: {reasoning}{Colors.RESET}") + with ProgressIndicator("Updating memory..."): - self.graph_memory.populate_from_text(content) - return render_markdown(content, self.syntax_highlighting) + self.graph_memory.populate_from_text(cleaned_content) + return render_markdown(cleaned_content, self.syntax_highlighting) def signal_handler(self, signum, frame): if self.autonomous_mode: diff --git a/rp/core/context.py b/rp/core/context.py index 5e392c1..2d58f75 100644 --- a/rp/core/context.py +++ b/rp/core/context.py @@ -87,6 +87,14 @@ def init_system_message(args): "Prefer standard Unix utilities over complex scripts.", "Use run_command_interactive for commands requiring user input (vim, nano, etc.).", "Use the knowledge base to answer questions and store important user preferences or information when relevant. Avoid storing simple greetings or casual conversation.", + "", + "IMPORTANT RESPONSE FORMAT:", + "When you have completed a task or answered a question, include [TASK_COMPLETE] at the end of your response.", + "The [TASK_COMPLETE] marker will not be shown to the user, so include it only when the task is truly finished.", + "Before your main response, include your reasoning on a separate line prefixed with 'REASONING: '.", + "Example format:", + "REASONING: The user asked about their favorite beer. I found 'westmalle' in the knowledge base.", + "Your favorite beer is Westmalle. [TASK_COMPLETE]", ] max_context_size = 10000