fix: handle webchat image outputs without streaming

This commit is contained in:
邹永赫
2026-03-15 16:13:13 +09:00
parent da1565ee81
commit d7457f38d4
8 changed files with 398 additions and 7 deletions

View File

@@ -11,6 +11,9 @@ from astrbot.core.agent.hooks import BaseAgentRunHooks
from astrbot.core.agent.run_context import ContextWrapper
from astrbot.core.agent.runners.tool_loop_agent_runner import ToolLoopAgentRunner
from astrbot.core.agent.tool import FunctionTool, ToolSet
from astrbot.core.astr_agent_run_util import run_agent
from astrbot.core.message.components import Image
from astrbot.core.message.message_event_result import MessageChain
from astrbot.core.provider.entities import LLMResponse, ProviderRequest, TokenUsage
from astrbot.core.provider.provider import Provider
@@ -127,6 +130,26 @@ class MockAbortableStreamProvider(MockProvider):
)
class MockFinalMediaStreamProvider(MockProvider):
async def text_chat_stream(self, **kwargs):
yield LLMResponse(
role="assistant",
is_chunk=True,
result_chain=MessageChain().message("draft "),
)
yield LLMResponse(
role="assistant",
is_chunk=False,
result_chain=MessageChain(
chain=[
Image.fromBase64(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a7d4AAAAASUVORK5CYII="
)
]
),
)
class MockHooks(BaseAgentRunHooks):
"""模拟钩子函数"""
@@ -466,6 +489,41 @@ async def test_stop_signal_returns_aborted_and_persists_partial_message(
assert runner.run_context.messages[-1].role == "assistant"
@pytest.mark.asyncio
async def test_run_agent_emits_final_media_chain_in_streaming_mode(
runner, provider_request, mock_tool_executor, mock_hooks
):
provider = MockFinalMediaStreamProvider()
mock_event = MockEvent("test:FriendMessage:stream_media", "u1")
mock_event.is_stopped = lambda: False
mock_event.get_extra = lambda *args, **kwargs: None
mock_event.set_extra = lambda *args, **kwargs: None
mock_event.get_platform_id = lambda: "webchat"
mock_event.get_platform_name = lambda: "webchat"
mock_event.send = AsyncMock()
mock_event.trace = AsyncMock()
mock_event.trace.record = lambda *args, **kwargs: None
run_context = ContextWrapper(context=MockAgentContext(mock_event))
await runner.reset(
provider=provider,
request=provider_request,
run_context=run_context,
tool_executor=mock_tool_executor,
agent_hooks=mock_hooks,
streaming=True,
)
emitted = []
async for chain in run_agent(runner, max_step=5):
if chain is not None:
emitted.append(chain)
assert any(
any(isinstance(comp, Image) for comp in chain.chain) for chain in emitted
)
@pytest.mark.asyncio
async def test_tool_result_injects_follow_up_notice(
runner, mock_provider, provider_request, mock_tool_executor, mock_hooks