Skip to content
Closed
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
15 changes: 6 additions & 9 deletions astrbot/core/agent/runners/tool_loop_agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
MessageChain,
)
from astrbot.core.persona_error_reply import (
extract_persona_custom_error_message_from_event,
get_user_facing_error_message,
)
from astrbot.core.provider.entities import (
LLMResponse,
Expand Down Expand Up @@ -106,11 +106,6 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
EMPTY_OUTPUT_RETRY_WAIT_MIN_S = 1
EMPTY_OUTPUT_RETRY_WAIT_MAX_S = 4

def _get_persona_custom_error_message(self) -> str | None:
"""Read persona-level custom error message from event extras when available."""
event = getattr(self.run_context.context, "event", None)
return extract_persona_custom_error_message_from_event(event)

async def _complete_with_assistant_response(self, llm_resp: LLMResponse) -> None:
"""Finalize the current step as a plain assistant response with no tool calls."""
self.final_llm_resp = llm_resp
Expand Down Expand Up @@ -517,10 +512,12 @@ async def step(self):
self.stats.end_time = time.time()
self._transition_state(AgentState.ERROR)
self._resolve_unconsumed_follow_ups()
custom_error_message = self._get_persona_custom_error_message()
error_text = custom_error_message or (
f"LLM 响应错误: {llm_resp.completion_text or '未知错误'}"
logger.error(
"LLM responded with error role: %s",
llm_resp.completion_text or "unknown",
)
event = getattr(self.run_context.context, "event", None)
error_text = get_user_facing_error_message(event)
yield AgentResponse(
type="err",
data=AgentResponseData(
Expand Down
21 changes: 5 additions & 16 deletions astrbot/core/astr_agent_run_util.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import re
import time
import traceback
from collections.abc import AsyncGenerator

from astrbot.core import logger
Expand All @@ -15,7 +14,7 @@
ResultContentType,
)
from astrbot.core.persona_error_reply import (
extract_persona_custom_error_message_from_event,
get_user_facing_error_message,
)
from astrbot.core.provider.entities import LLMResponse
from astrbot.core.provider.provider import TTSProvider
Expand Down Expand Up @@ -234,26 +233,15 @@ async def run_agent(

break

except Exception as e:
except Exception:
if "stop_watcher" in locals() and not stop_watcher.done():
stop_watcher.cancel()
try:
await stop_watcher
except asyncio.CancelledError:
pass
logger.error(traceback.format_exc())

custom_error_message = extract_persona_custom_error_message_from_event(
astr_event
)
if custom_error_message:
err_msg = custom_error_message
else:
err_msg = (
f"Error occurred during AI execution.\n"
f"Error Type: {type(e).__name__}\n"
f"Error Message: {str(e)}"
)
logger.error("Error occurred during AI execution", exc_info=True)
err_msg = get_user_facing_error_message(astr_event)

error_llm_response = LLMResponse(
role="err",
Expand All @@ -270,6 +258,7 @@ async def run_agent(
yield MessageChain().message(err_msg)
else:
astr_event.set_result(MessageEventResult().message(err_msg))
yield
return


Expand Down
17 changes: 17 additions & 0 deletions astrbot/core/persona_error_reply.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any

PERSONA_CUSTOM_ERROR_MESSAGE_EXTRA_KEY = "persona_custom_error_message"
DEFAULT_USER_FACING_ERROR_MESSAGE = "处理请求时出现异常,请稍后再试。"


def normalize_persona_custom_error_message(value: object) -> str | None:
Expand Down Expand Up @@ -34,6 +35,22 @@ def extract_persona_custom_error_message_from_event(event: Any) -> str | None:
return None


def get_user_facing_error_message(
event: Any,
fallback_message: object = DEFAULT_USER_FACING_ERROR_MESSAGE,
) -> str:
"""Resolve the user-facing error message with persona override support."""
custom_error_message = extract_persona_custom_error_message_from_event(event)
if custom_error_message:
return custom_error_message

normalized_fallback = normalize_persona_custom_error_message(fallback_message)
if normalized_fallback:
return normalized_fallback

return DEFAULT_USER_FACING_ERROR_MESSAGE


def set_persona_custom_error_message_on_event(
event: Any, message: object
) -> str | None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
)
from astrbot.core.persona_error_reply import (
extract_persona_custom_error_message_from_event,
get_user_facing_error_message,
resolve_event_conversation_persona_id,
resolve_persona_custom_error_message,
set_persona_custom_error_message_on_event,
)
from astrbot.core.pipeline.stage import Stage
from astrbot.core.platform.astr_message_event import AstrMessageEvent
Expand Down Expand Up @@ -138,6 +142,39 @@ async def initialize(self, ctx: PipelineContext) -> None:
max_quoted_fallback_images=settings.get("max_quoted_fallback_images", 20),
)

async def _prime_persona_custom_error_message(
self, event: AstrMessageEvent
) -> None:
if extract_persona_custom_error_message_from_event(event) is not None:
return

try:
conversation_persona_id = None
provider_request = event.get_extra("provider_request")
if (
isinstance(provider_request, ProviderRequest)
and provider_request.conversation is not None
):
conversation_persona_id = provider_request.conversation.persona_id

if conversation_persona_id is None:
conversation_persona_id = await resolve_event_conversation_persona_id(
event,
self.conv_manager,
)

custom_error_message = await resolve_persona_custom_error_message(
event=event,
persona_manager=self.ctx.plugin_manager.context.persona_manager,
provider_settings=self.main_agent_cfg.provider_settings,
conversation_persona_id=conversation_persona_id,
)
except Exception as exc:
logger.debug("Failed to prime persona custom error message: %s", exc)
return

set_persona_custom_error_message_on_event(event, custom_error_message)

async def process(
self, event: AstrMessageEvent, provider_wake_prefix: str
) -> AsyncGenerator[None, None]:
Expand All @@ -164,6 +201,7 @@ async def process(
logger.debug("skip llm request: empty message and no provider_request")
return

await self._prime_persona_custom_error_message(event)
logger.debug("ready to request llm provider")
follow_up_capture = try_capture_follow_up(event)
if follow_up_capture:
Expand Down Expand Up @@ -381,14 +419,9 @@ async def process(
if runner_registered and agent_runner is not None:
unregister_active_runner(event.unified_msg_origin, agent_runner)

except Exception as e:
logger.error(f"Error occurred while processing agent: {e}")
custom_error_message = extract_persona_custom_error_message_from_event(
event
)
error_text = custom_error_message or (
f"Error occurred while processing agent request: {e}"
)
except Exception:
logger.error("Error occurred while processing agent", exc_info=True)
error_text = get_user_facing_error_message(event)
await event.send(MessageChain().message(error_text))
finally:
if typing_requested:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ResultContentType,
)
from astrbot.core.persona_error_reply import (
get_user_facing_error_message,
resolve_event_conversation_persona_id,
resolve_persona_custom_error_message,
set_persona_custom_error_message_on_event,
Expand Down Expand Up @@ -79,15 +80,9 @@ async def run_third_party_agent(
yield resp.data["chain"], False
elif resp.type == "err":
yield resp.data["chain"], True
except Exception as e:
logger.error(f"Third party agent runner error: {e}")
err_msg = custom_error_message
if not err_msg:
err_msg = (
f"Error occurred during AI execution.\n"
f"Error Type: {type(e).__name__} (3rd party)\n"
f"Error Message: {str(e)}"
)
except Exception:
logger.error("Third party agent runner error", exc_info=True)
err_msg = get_user_facing_error_message(None, custom_error_message)
yield MessageChain().message(err_msg), True


Expand Down
Loading