Cherry-pick 6 bug fixes from PR #76 and update documentation
Code fixes (run_agent.py): - Fix off-by-one in _flush_messages_to_session_db skipping one message per flush - Add clear_interrupt() to 3 early-return paths preventing stale interrupt state - Wrap handle_function_call in try/except so tool crashes don't kill the conversation - Replace fragile `is` identity check with _flush_sentinel marker for memory flush cleanup - Fix retry loop off-by-one (6 attempts not 7) - Remove redundant inline `import re`
This commit is contained in:
parent
c104647450
commit
c77f3da0ce
2 changed files with 69 additions and 37 deletions
30
run_agent.py
30
run_agent.py
|
|
@ -596,7 +596,7 @@ class AIAgent:
|
|||
if not self._session_db:
|
||||
return
|
||||
try:
|
||||
start_idx = (len(conversation_history) if conversation_history else 0) + 1
|
||||
start_idx = len(conversation_history) if conversation_history else 0
|
||||
for msg in messages[start_idx:]:
|
||||
role = msg.get("role", "unknown")
|
||||
content = msg.get("content")
|
||||
|
|
@ -943,8 +943,6 @@ class AIAgent:
|
|||
if not content:
|
||||
return content
|
||||
content = convert_scratchpad_to_think(content)
|
||||
# Strip extra newlines before/after think blocks
|
||||
import re
|
||||
content = re.sub(r'\n+(<think>)', r'\n\1', content)
|
||||
content = re.sub(r'(</think>)\n+', r'\1\n', content)
|
||||
return content.strip()
|
||||
|
|
@ -1305,7 +1303,8 @@ class AIAgent:
|
|||
"[System: The session is being compressed. "
|
||||
"Please save anything worth remembering to your memories.]"
|
||||
)
|
||||
flush_msg = {"role": "user", "content": flush_content}
|
||||
_sentinel = f"__flush_{id(self)}_{time.monotonic()}"
|
||||
flush_msg = {"role": "user", "content": flush_content, "_flush_sentinel": _sentinel}
|
||||
messages.append(flush_msg)
|
||||
|
||||
try:
|
||||
|
|
@ -1367,10 +1366,13 @@ class AIAgent:
|
|||
except Exception as e:
|
||||
logger.debug("Memory flush API call failed: %s", e)
|
||||
finally:
|
||||
# Strip flush artifacts: remove everything from the flush message onward
|
||||
while messages and messages[-1] is not flush_msg and len(messages) > 0:
|
||||
# Strip flush artifacts: remove everything from the flush message onward.
|
||||
# Use sentinel marker instead of identity check for robustness.
|
||||
while messages and messages[-1].get("_flush_sentinel") != _sentinel:
|
||||
messages.pop()
|
||||
if messages and messages[-1] is flush_msg:
|
||||
if not messages:
|
||||
break
|
||||
if messages and messages[-1].get("_flush_sentinel") == _sentinel:
|
||||
messages.pop()
|
||||
|
||||
def _compress_context(self, messages: list, system_message: str, *, approx_tokens: int = None) -> tuple:
|
||||
|
|
@ -1565,12 +1567,19 @@ class AIAgent:
|
|||
try:
|
||||
function_result = handle_function_call(function_name, function_args, effective_task_id)
|
||||
_spinner_result = function_result
|
||||
except Exception as tool_error:
|
||||
function_result = f"Error executing tool '{function_name}': {tool_error}"
|
||||
logger.error("handle_function_call raised for %s: %s", function_name, tool_error)
|
||||
finally:
|
||||
tool_duration = time.time() - tool_start_time
|
||||
cute_msg = _get_cute_tool_message_impl(function_name, function_args, tool_duration, result=_spinner_result)
|
||||
spinner.stop(cute_msg)
|
||||
else:
|
||||
function_result = handle_function_call(function_name, function_args, effective_task_id)
|
||||
try:
|
||||
function_result = handle_function_call(function_name, function_args, effective_task_id)
|
||||
except Exception as tool_error:
|
||||
function_result = f"Error executing tool '{function_name}': {tool_error}"
|
||||
logger.error("handle_function_call raised for %s: %s", function_name, tool_error)
|
||||
tool_duration = time.time() - tool_start_time
|
||||
|
||||
result_preview = function_result[:200] if len(function_result) > 200 else function_result
|
||||
|
|
@ -1877,7 +1886,7 @@ class AIAgent:
|
|||
retry_count = 0
|
||||
max_retries = 6 # Increased to allow longer backoff periods
|
||||
|
||||
while retry_count <= max_retries:
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
api_kwargs = self._build_api_kwargs(api_messages)
|
||||
|
||||
|
|
@ -1971,6 +1980,7 @@ class AIAgent:
|
|||
if self._interrupt_requested:
|
||||
print(f"{self.log_prefix}⚡ Interrupt detected during retry wait, aborting.")
|
||||
self._persist_session(messages, conversation_history)
|
||||
self.clear_interrupt()
|
||||
return {
|
||||
"final_response": "Operation interrupted.",
|
||||
"messages": messages,
|
||||
|
|
@ -2073,6 +2083,7 @@ class AIAgent:
|
|||
if self._interrupt_requested:
|
||||
print(f"{self.log_prefix}⚡ Interrupt detected during error handling, aborting retries.")
|
||||
self._persist_session(messages, conversation_history)
|
||||
self.clear_interrupt()
|
||||
return {
|
||||
"final_response": "Operation interrupted.",
|
||||
"messages": messages,
|
||||
|
|
@ -2160,6 +2171,7 @@ class AIAgent:
|
|||
if self._interrupt_requested:
|
||||
print(f"{self.log_prefix}⚡ Interrupt detected during retry wait, aborting.")
|
||||
self._persist_session(messages, conversation_history)
|
||||
self.clear_interrupt()
|
||||
return {
|
||||
"final_response": "Operation interrupted.",
|
||||
"messages": messages,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue