feat: enable autonomous mode by default
feat: improve content extraction in autonomous mode refactor: remove user message in autonomous mode refactor: display tool call arguments maintenance: update version to 1.54.0 refactor: handle tool call execution errors refactor: prevent excessive autonomous mode exits perf: display execution time in progress indicator
This commit is contained in:
parent
20668d9086
commit
8e6af2b32b
@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Version 1.53.0 - 2025-11-10
|
||||||
|
|
||||||
|
Autonomous mode is now enabled by default, streamlining workflows. We've also improved the underlying code and fixed some issues with content extraction in autonomous mode.
|
||||||
|
|
||||||
|
**Changes:** 15 files, 433 lines
|
||||||
|
**Languages:** Markdown (47 lines), Python (384 lines), TOML (2 lines)
|
||||||
|
|
||||||
## Version 1.52.0 - 2025-11-10
|
## Version 1.52.0 - 2025-11-10
|
||||||
|
|
||||||
This release updates the project version to 1.52.0. No new features or changes are introduced for users or developers.
|
This release updates the project version to 1.52.0. No new features or changes are introduced for users or developers.
|
||||||
|
|||||||
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "rp"
|
name = "rp"
|
||||||
version = "1.52.0"
|
version = "1.53.0"
|
||||||
description = "R python edition. The ultimate autonomous AI CLI."
|
description = "R python edition. The ultimate autonomous AI CLI."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
@ -99,6 +99,9 @@ def run_autonomous_mode(assistant, task):
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.debug("Autonomous mode interrupted by user")
|
logger.debug("Autonomous mode interrupted by user")
|
||||||
print(f"\n{Colors.YELLOW}Autonomous mode interrupted by user{Colors.RESET}")
|
print(f"\n{Colors.YELLOW}Autonomous mode interrupted by user{Colors.RESET}")
|
||||||
|
# Cancel the last API call and remove the user message to keep messages clean
|
||||||
|
if assistant.messages and assistant.messages[-1]["role"] == "user":
|
||||||
|
assistant.messages.pop()
|
||||||
finally:
|
finally:
|
||||||
assistant.autonomous_mode = False
|
assistant.autonomous_mode = False
|
||||||
logger.debug("=== AUTONOMOUS MODE END ===")
|
logger.debug("=== AUTONOMOUS MODE END ===")
|
||||||
@ -113,10 +116,13 @@ def process_response_autonomous(assistant, response):
|
|||||||
assistant.messages.append(message)
|
assistant.messages.append(message)
|
||||||
if "tool_calls" in message and message["tool_calls"]:
|
if "tool_calls" in message and message["tool_calls"]:
|
||||||
tool_results = []
|
tool_results = []
|
||||||
with ProgressIndicator("Executing tools..."):
|
|
||||||
for tool_call in message["tool_calls"]:
|
for tool_call in message["tool_calls"]:
|
||||||
func_name = tool_call["function"]["name"]
|
func_name = tool_call["function"]["name"]
|
||||||
arguments = json.loads(tool_call["function"]["arguments"])
|
arguments = json.loads(tool_call["function"]["arguments"])
|
||||||
|
args_str = ", ".join([f"{k}={repr(v)}" for k, v in arguments.items()])
|
||||||
|
if len(args_str) > 100:
|
||||||
|
args_str = args_str[:97] + "..."
|
||||||
|
print(f"{Colors.BLUE}⠋ Executing tools......{func_name}({args_str}){Colors.RESET}")
|
||||||
result = execute_single_tool(assistant, func_name, arguments)
|
result = execute_single_tool(assistant, func_name, arguments)
|
||||||
if isinstance(result, str):
|
if isinstance(result, str):
|
||||||
try:
|
try:
|
||||||
@ -125,7 +131,6 @@ def process_response_autonomous(assistant, response):
|
|||||||
result = {"error": str(ex)}
|
result = {"error": str(ex)}
|
||||||
status = "success" if result.get("status") == "success" else "error"
|
status = "success" if result.get("status") == "success" else "error"
|
||||||
result = truncate_tool_result(result)
|
result = truncate_tool_result(result)
|
||||||
display_tool_call(func_name, arguments, status, result)
|
|
||||||
sanitized_result = sanitize_for_json(result)
|
sanitized_result = sanitize_for_json(result)
|
||||||
tool_results.append(
|
tool_results.append(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import readline
|
|||||||
import signal
|
import signal
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ class Assistant:
|
|||||||
"MODEL_LIST_URL", MODEL_LIST_URL
|
"MODEL_LIST_URL", MODEL_LIST_URL
|
||||||
)
|
)
|
||||||
self.use_tools = os.environ.get("USE_TOOLS", "1") == "1"
|
self.use_tools = os.environ.get("USE_TOOLS", "1") == "1"
|
||||||
self.interrupt_count = 0
|
self.last_interrupt_time = 0
|
||||||
self.python_globals = {}
|
self.python_globals = {}
|
||||||
self.db_conn = None
|
self.db_conn = None
|
||||||
self.autonomous_mode = False
|
self.autonomous_mode = False
|
||||||
@ -238,6 +239,10 @@ class Assistant:
|
|||||||
func_name = tool_call["function"]["name"]
|
func_name = tool_call["function"]["name"]
|
||||||
arguments = json.loads(tool_call["function"]["arguments"])
|
arguments = json.loads(tool_call["function"]["arguments"])
|
||||||
logger.debug(f"Tool call: {func_name} with arguments: {arguments}")
|
logger.debug(f"Tool call: {func_name} with arguments: {arguments}")
|
||||||
|
args_str = ", ".join([f"{k}={repr(v)}" for k, v in arguments.items()])
|
||||||
|
if len(args_str) > 100:
|
||||||
|
args_str = args_str[:97] + "..."
|
||||||
|
print(f"{Colors.BLUE}⠋ Executing tools......{func_name}({args_str}){Colors.RESET}")
|
||||||
func_map = {
|
func_map = {
|
||||||
"http_fetch": lambda **kw: http_fetch(**kw),
|
"http_fetch": lambda **kw: http_fetch(**kw),
|
||||||
"run_command": lambda **kw: run_command(**kw),
|
"run_command": lambda **kw: run_command(**kw),
|
||||||
@ -351,22 +356,15 @@ class Assistant:
|
|||||||
return render_markdown(cleaned_content, self.syntax_highlighting)
|
return render_markdown(cleaned_content, self.syntax_highlighting)
|
||||||
|
|
||||||
def signal_handler(self, signum, frame):
|
def signal_handler(self, signum, frame):
|
||||||
if self.autonomous_mode:
|
current_time = time.time()
|
||||||
self.interrupt_count += 1
|
if current_time - self.last_interrupt_time < 1.0:
|
||||||
if self.interrupt_count >= 2:
|
print(f"\n{Colors.RED}Force exiting...{Colors.RESET}")
|
||||||
print(f"\n{Colors.RED}Force exiting autonomous mode...{Colors.RESET}")
|
|
||||||
self.autonomous_mode = False
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print(f"\n{Colors.YELLOW}Press Ctrl+C again to force exit{Colors.RESET}")
|
|
||||||
return
|
|
||||||
self.interrupt_count += 1
|
|
||||||
if self.interrupt_count >= 2:
|
|
||||||
print(f"\n{Colors.RED}Exiting...{Colors.RESET}")
|
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
print(f"\n{Colors.YELLOW}Press Ctrl+C again to exit{Colors.RESET}")
|
self.last_interrupt_time = current_time
|
||||||
|
print(f"\n{Colors.YELLOW}Interrupted{Colors.RESET}")
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
|
||||||
def setup_readline(self):
|
def setup_readline(self):
|
||||||
try:
|
try:
|
||||||
@ -446,7 +444,8 @@ class Assistant:
|
|||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.signal_handler(None, None)
|
print(f"\n{Colors.YELLOW}Interrupted, returning to prompt{Colors.RESET}")
|
||||||
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"{Colors.RED}Error: {e}{Colors.RESET}")
|
print(f"{Colors.RED}Error: {e}{Colors.RESET}")
|
||||||
logging.error(f"REPL error: {e}\n{traceback.format_exc()}")
|
logging.error(f"REPL error: {e}\n{traceback.format_exc()}")
|
||||||
|
|||||||
@ -10,6 +10,7 @@ class ProgressIndicator:
|
|||||||
self.show = show
|
self.show = show
|
||||||
self.running = False
|
self.running = False
|
||||||
self.thread = None
|
self.thread = None
|
||||||
|
self.start_time = None
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
if self.show:
|
if self.show:
|
||||||
@ -21,6 +22,7 @@ class ProgressIndicator:
|
|||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
self.start_time = time.time()
|
||||||
self.running = True
|
self.running = True
|
||||||
self.thread = threading.Thread(target=self._animate, daemon=True)
|
self.thread = threading.Thread(target=self._animate, daemon=True)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
@ -30,14 +32,15 @@ class ProgressIndicator:
|
|||||||
self.running = False
|
self.running = False
|
||||||
if self.thread:
|
if self.thread:
|
||||||
self.thread.join(timeout=1.0)
|
self.thread.join(timeout=1.0)
|
||||||
sys.stdout.write("\r" + " " * (len(self.message) + 10) + "\r")
|
sys.stdout.write("\r" + " " * (len(self.message) + 20) + "\r")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def _animate(self):
|
def _animate(self):
|
||||||
spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
spinner = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
||||||
idx = 0
|
idx = 0
|
||||||
while self.running:
|
while self.running:
|
||||||
sys.stdout.write(f"\r{spinner[idx]} {self.message}...")
|
elapsed = time.time() - self.start_time
|
||||||
|
sys.stdout.write(f"\r{spinner[idx]} {self.message}... ({elapsed:.1f}s)")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
idx = (idx + 1) % len(spinner)
|
idx = (idx + 1) % len(spinner)
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user