fix(channel): avoid replaying thinking after resume (#154)

* fix(channel): avoid replaying thinking after resume

* fix(channel): relay fresh thinking after resume

* Fix

* Fix

---------

Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
This commit is contained in:
Ziheng Zhang
2026-04-15 21:05:47 +08:00
committed by GitHub
parent 2961e5ee88
commit a2d2ddc5a2
4 changed files with 294 additions and 40 deletions
+96
View File
@@ -188,6 +188,102 @@ class TestMultipleStreamingCalls:
# Cleanup
loop2.close()
def test_recursive_streaming_does_not_resend_same_thinking(self):
"""Resumed runs should not replay the original thinking to channels."""
from EvoScientist.stream.display import _run_streaming
mock_agent = Mock()
thinking = "Initial plan. " * 20
stream_calls = 0
async def mock_stream(*args, **kwargs):
nonlocal stream_calls
stream_calls += 1
if stream_calls == 1:
yield {"type": "thinking", "content": thinking}
yield {
"type": "ask_user",
"interrupt_id": "ask-1",
"tool_call_id": "tc-1",
"questions": [{"question": "Continue?"}],
}
return
yield {"type": "text", "content": "final answer"}
yield {"type": "done", "response": "final answer"}
sent_thinking: list[str] = []
with patch(
"EvoScientist.stream.display.stream_agent_events",
side_effect=mock_stream,
):
with patch("EvoScientist.stream.display.Live"):
result = _run_streaming(
agent=mock_agent,
message="test message",
thread_id="thread1",
show_thinking=False,
interactive=True,
on_thinking=sent_thinking.append,
ask_user_prompt_fn=lambda _data: {
"answers": ["yes"],
"status": "answered",
},
)
assert result == "final answer"
assert sent_thinking == [thinking.rstrip()]
def test_recursive_streaming_sends_new_thinking_after_resume(self):
"""Genuinely new thinking in resumed rounds should be relayed."""
from EvoScientist.stream.display import _run_streaming
mock_agent = Mock()
thinking_r1 = "Initial plan. " * 20
thinking_r2 = "Revised plan. " * 20
stream_calls = 0
async def mock_stream(*args, **kwargs):
nonlocal stream_calls
stream_calls += 1
if stream_calls == 1:
yield {"type": "thinking", "content": thinking_r1}
yield {
"type": "ask_user",
"interrupt_id": "ask-1",
"tool_call_id": "tc-1",
"questions": [{"question": "Continue?"}],
}
return
yield {"type": "thinking", "content": thinking_r2}
yield {"type": "text", "content": "final answer"}
yield {"type": "done", "response": "final answer"}
sent_thinking: list[str] = []
with patch(
"EvoScientist.stream.display.stream_agent_events",
side_effect=mock_stream,
):
with patch("EvoScientist.stream.display.Live"):
result = _run_streaming(
agent=mock_agent,
message="test message",
thread_id="thread1",
show_thinking=False,
interactive=True,
on_thinking=sent_thinking.append,
ask_user_prompt_fn=lambda _data: {
"answers": ["yes"],
"status": "answered",
},
)
assert result == "final answer"
assert sent_thinking == [thinking_r1.rstrip(), thinking_r2.rstrip()]
class TestEventLoopThreadSafety:
"""Tests for thread safety edge cases."""