Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)
from astrbot.core.agent.runners.deerflow.deerflow_api_client import DeerFlowAPIClient
from astrbot.core.db.po import ProviderStat
from astrbot.core.pipeline.process_stage.follow_up import request_active_runner_stop
from astrbot.core.utils.active_event_registry import active_event_registry

from .utils.rst_scene import RstScene
Expand Down Expand Up @@ -206,6 +207,8 @@ async def stop(self, message: AstrMessageEvent) -> None:
umo,
exclude=message,
)
if request_active_runner_stop(umo) and stopped_count == 0:
stopped_count = 1

if stopped_count > 0:
message.set_result(
Expand Down
401 changes: 328 additions & 73 deletions astrbot/core/agent/runners/tool_loop_agent_runner.py

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions astrbot/core/agent/stop_policy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Any

AGENT_OUTPUT_DELIVERY_CONFIRMED_KEY = "_agent_output_delivery_confirmed"


class AgentOutputStopped(Exception):
"""Unwind streaming adapters without triggering broad error handlers."""


def event_requests_agent_stop(event: Any) -> bool:
"""Return whether an event requests the current agent run to stop.

Args:
event: Event-like object that may expose stop state and extras.

Returns:
Whether hard stop, soft stop, or user-aborted state is active.
"""
if event is None:
return False

is_stopped = getattr(event, "is_stopped", None)
if callable(is_stopped) and is_stopped():
return True

get_extra = getattr(event, "get_extra", None)
if not callable(get_extra):
return False
return bool(get_extra("agent_stop_requested")) or bool(
get_extra("agent_user_aborted")
)
34 changes: 22 additions & 12 deletions astrbot/core/astr_agent_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

from astrbot.core.agent.hooks import BaseAgentRunHooks
from astrbot.core.agent.run_context import ContextWrapper
from astrbot.core.agent.stop_policy import event_requests_agent_stop
from astrbot.core.agent.tool import FunctionTool
from astrbot.core.astr_agent_context import AstrAgentContext
from astrbot.core.pipeline.context_utils import call_event_hook
from astrbot.core.pipeline.context_utils import call_agent_done_hook, call_event_hook
from astrbot.core.star.star_handler import EventType


Expand All @@ -22,20 +23,29 @@ async def on_agent_begin(

async def on_agent_done(self, run_context, llm_response) -> None:
# 执行事件钩子
if llm_response and llm_response.reasoning_content:
event = run_context.context.event
suppress_llm_response = event_requests_agent_stop(event)

if (
not suppress_llm_response
and llm_response
and llm_response.reasoning_content
):
# we will use this in result_decorate stage to inject reasoning content to chain
run_context.context.event.set_extra(
"_llm_reasoning_content", llm_response.reasoning_content
event.set_extra("_llm_reasoning_content", llm_response.reasoning_content)

if not suppress_llm_response:
await call_event_hook(
event,
EventType.OnLLMResponseEvent,
llm_response,
)

await call_event_hook(
run_context.context.event,
EventType.OnLLMResponseEvent,
llm_response,
)
await call_event_hook(
run_context.context.event,
EventType.OnAgentDoneEvent,
if event_requests_agent_stop(event):
event.set_extra("_llm_reasoning_content", None)

await call_agent_done_hook(
event,
run_context,
llm_response,
)
Expand Down
Loading
Loading