fix(agent): deliver non-streamed finalization responses

This commit is contained in:
chengyongru
2026-07-23 13:16:47 +08:00
committed by chengyongru
parent 536e8db324
commit 5f054c0e74
3 changed files with 85 additions and 5 deletions
+49
View File
@@ -534,6 +534,55 @@ class TestToolEventProgress:
assert turn_end_msgs[0].content == ""
provider.chat_with_retry.assert_not_awaited()
@pytest.mark.asyncio
async def test_non_streamed_finalization_is_delivered_as_regular_message(
self,
tmp_path: Path,
) -> None:
"""A no-tools finalization must not be dropped after empty stream retries."""
bus = MessageBus()
provider = MagicMock()
provider.supports_progress_deltas = True
provider.get_default_model.return_value = "openai-codex/gpt-5.5"
provider.chat_stream_with_retry = AsyncMock(side_effect=[
LLMResponse(content=None, tool_calls=[]),
LLMResponse(content=None, tool_calls=[]),
])
provider.chat_with_retry = AsyncMock(
return_value=LLMResponse(content="final answer", tool_calls=[]),
)
loop = AgentLoop(
bus=bus,
provider=provider,
workspace=tmp_path,
model="openai-codex/gpt-5.5",
)
_attach_webui_runtime_events(loop, bus)
loop.tools.get_definitions = MagicMock(return_value=[])
loop.consolidator.maybe_consolidate_by_tokens = AsyncMock(return_value=False) # type: ignore[method-assign]
await loop._dispatch(InboundMessage(
channel="websocket",
sender_id="u1",
chat_id="chat1",
content="say hello",
metadata={"_wants_stream": True},
))
outbound = []
while bus.outbound_size > 0:
outbound.append(await bus.consume_outbound())
assert not any(isinstance(message.event, StreamDeltaEvent) for message in outbound)
assert len([
message for message in outbound if isinstance(message.event, StreamEndEvent)
]) == 3
final = [message for message in outbound if message.content == "final answer"]
assert len(final) == 1
assert final[0].event is None
provider.chat_stream_with_retry.assert_awaited()
provider.chat_with_retry.assert_awaited_once()
@pytest.mark.asyncio
async def test_independent_late_subagent_result_gets_complete_webui_turn(
self,
+8 -1
View File
@@ -512,7 +512,7 @@ async def test_ssrf_soft_block_can_finalize_after_streamed_tool_call(tmp_path):
)],
usage={},
)
provider.chat_stream_with_retry = AsyncMock(side_effect=[
responses = iter([
tool_call_resp,
LLMResponse(
content="I cannot access private URLs. Please share the local file.",
@@ -521,6 +521,13 @@ async def test_ssrf_soft_block_can_finalize_after_streamed_tool_call(tmp_path):
),
])
async def chat_stream_with_retry(*, on_content_delta, **kwargs):
response = next(responses)
await on_content_delta(response.content)
return response
provider.chat_stream_with_retry = AsyncMock(side_effect=chat_stream_with_retry)
loop = AgentLoop(bus=bus, provider=provider, workspace=tmp_path, model="test-model")
loop.tools.get_definitions = MagicMock(return_value=[])
loop.tools.prepare_call = MagicMock(return_value=(None, {}, None))