Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions open_strix/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
SEND_MESSAGE_LOOP_HARD_LIMIT,
SEND_MESSAGE_LOOP_SIMILARITY_THRESHOLD,
SEND_MESSAGE_LOOP_SOFT_LIMIT,
SEND_MESSAGE_LOOP_WARN_LIMIT,
SendMessageCircuitBreakerStop,
ToolsMixin,
)
Expand Down Expand Up @@ -362,6 +363,7 @@ def __init__(self, home: Path) -> None:
self.worker_task: asyncio.Task[Any] | None = None
self._current_turn_sent_messages: list[tuple[str, str]] | None = None
self.send_message_loop_soft_limit = SEND_MESSAGE_LOOP_SOFT_LIMIT
self.send_message_loop_warn_limit = SEND_MESSAGE_LOOP_WARN_LIMIT
self.send_message_loop_hard_limit = SEND_MESSAGE_LOOP_HARD_LIMIT
self.send_message_loop_similarity_threshold = SEND_MESSAGE_LOOP_SIMILARITY_THRESHOLD
self._send_message_last_text_normalized: str | None = None
Expand Down
32 changes: 28 additions & 4 deletions open_strix/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
DEFAULT_TAVILY_SEARCH_URL = "https://api.tavily.com/search"
SHELL_OUTPUT_LIMIT_CHARS = 12_000
SEND_MESSAGE_LOOP_SOFT_LIMIT = 3
SEND_MESSAGE_LOOP_WARN_LIMIT = 7
SEND_MESSAGE_LOOP_HARD_LIMIT = 10
SEND_MESSAGE_LOOP_SIMILARITY_THRESHOLD = 0.98

Expand Down Expand Up @@ -407,8 +408,32 @@ async def send_message(
reacted=hard_stop_reacted,
)
raise SendMessageCircuitBreakerStop(
"send_message hard stop: detected repeated near-duplicate loop. "
"Turn terminated at streak=10 for safety.",
"send_message hard stop: repeated near-duplicate loop at streak=10. "
"This turn is terminated for safety. Next turn: reflect on what went "
"wrong before resuming — consider using 5 Whys or a completely "
"different approach instead of retrying.",
)

if streak >= self.send_message_loop_warn_limit:
self.log_event(
"send_message_loop_warning",
tool="send_message",
channel_id=target_channel_id,
streak=streak,
similarity_ratio=round(similarity_ratio, 6),
reacted_warning=warning_reacted,
)
return (
f"WARNING: You have sent {streak} near-duplicate messages. "
f"Hard stop at {self.send_message_loop_hard_limit} — after that, "
"this turn will be terminated.\n\n"
"Before that happens, stop and reflect:\n"
"1. What were you trying to accomplish? Did the approach work?\n"
"2. What is a COMPLETELY DIFFERENT way to achieve this?\n"
"3. If you have a 5 Whys or root-cause analysis skill, use it on why "
"this approach failed before trying again.\n\n"
"Do not retry the same action. Think creatively about an alternative, "
"or finish the turn safely."
)

self.log_event(
Expand All @@ -421,8 +446,7 @@ async def send_message(
)
return (
"Loop detected in send_message calls. Message delivery is paused for this turn "
"to prevent an infinite output loop. Stop repeating similar messages immediately, "
"change strategy, and finish the turn safely."
"to prevent an infinite output loop."
)

sent, sent_message_id, sent_chunks = await self._send_channel_message(
Expand Down