Skip to content
Merged
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
23 changes: 22 additions & 1 deletion astrbot/core/astr_main_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@
)
from astrbot.core.utils.string_utils import normalize_and_dedupe_strings

LLM_ERROR_MESSAGE_EXTRA_KEY = "_llm_error_message"


@dataclass(slots=True)
class MainAgentBuildConfig:
Expand Down Expand Up @@ -183,25 +185,39 @@ class MainAgentBuildResult:
reset_coro: Coroutine | None = None


def _set_llm_error_message(event: AstrMessageEvent, message: str) -> None:
event.set_extra(LLM_ERROR_MESSAGE_EXTRA_KEY, message)


def _select_provider(
event: AstrMessageEvent, plugin_context: Context
) -> Provider | None:
"""Select chat provider for the event."""
sel_provider = event.get_extra("selected_provider")
if sel_provider and isinstance(sel_provider, str):
provider = plugin_context.get_provider_by_id(sel_provider)
if not provider:
if provider is None:
logger.error("未找到指定的提供商: %s。", sel_provider)
_set_llm_error_message(
event,
f"LLM 请求失败:未找到指定的提供商 `{sel_provider}`。请检查提供商配置或重新选择可用模型。",
)
return None
if not isinstance(provider, Provider):
logger.error(
"选择的提供商类型无效(%s),跳过 LLM 请求处理。", type(provider)
)
_set_llm_error_message(
event,
f"LLM 请求失败:选择的提供商类型无效({type(provider).__name__}),已跳过本次请求。",
)
return None
return provider
try:
return plugin_context.get_using_provider(umo=event.unified_msg_origin)
except ValueError as exc:
logger.error("Error occurred while selecting provider: %s", exc)
_set_llm_error_message(event, f"LLM 请求失败:{exc}")
return None


Expand Down Expand Up @@ -1192,6 +1208,11 @@ async def build_main_agent(
provider = provider or _select_provider(event, plugin_context)
if provider is None:
logger.info("未找到任何对话模型(提供商),跳过 LLM 请求处理。")
if not event.get_extra(LLM_ERROR_MESSAGE_EXTRA_KEY):
_set_llm_error_message(
event,
"LLM 请求失败:未找到任何可用的对话模型(提供商)。请先在 WebUI 中配置并启用可用模型。",
)
return None

if req is None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
)
from astrbot.core.agent.response import AgentStats
from astrbot.core.astr_main_agent import (
LLM_ERROR_MESSAGE_EXTRA_KEY,
MainAgentBuildConfig,
MainAgentBuildResult,
build_main_agent,
Expand Down Expand Up @@ -151,6 +152,11 @@ async def initialize(self, ctx: PipelineContext) -> None:
max_quoted_fallback_images=settings.get("max_quoted_fallback_images", 20),
)

async def _send_llm_error_message(
self, event: AstrMessageEvent, message: object
) -> None:
await event.send(MessageChain().message(str(message)))

async def process(
self, event: AstrMessageEvent, provider_wake_prefix: str
) -> AsyncGenerator[None, None]:
Expand Down Expand Up @@ -219,6 +225,13 @@ async def process(
)

if build_result is None:
if llm_error_message := event.get_extra(
LLM_ERROR_MESSAGE_EXTRA_KEY
):
await self._send_llm_error_message(
event,
llm_error_message,
)
return

agent_runner = build_result.agent_runner
Expand All @@ -229,10 +242,12 @@ async def process(
api_base = provider.provider_config.get("api_base", "")
for host in decoded_blocked:
if host in api_base:
logger.error(
"Provider API base %s is blocked due to security reasons. Please use another ai provider.",
api_base,
error_message = (
f"LLM 请求失败:Provider API base `{api_base}` "
"因安全原因被拦截,请更换可用的 AI 提供商。"
)
logger.error(error_message)
await self._send_llm_error_message(event, error_message)
return

stream_to_general = (
Expand Down
26 changes: 24 additions & 2 deletions dashboard/src/components/chat/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ async function startNewChat() {
replyTarget.value = null;
newChat();
closeMobileSidebar();
await focusChatInput();
}

function openCreateProjectDialog() {
Expand Down Expand Up @@ -975,6 +976,7 @@ async function selectSession(sessionId: string, pushRoute = true) {
}
scrollToBottom();
closeMobileSidebar();
await focusChatInput();
}

async function sendCurrentMessage() {
Expand Down Expand Up @@ -1032,6 +1034,7 @@ async function sendCurrentMessage() {
console.error("Failed to send message:", error);
} finally {
sending.value = false;
await focusChatInput();
}
}

Expand Down Expand Up @@ -1326,6 +1329,13 @@ function scrollToBottom() {
});
}

async function focusChatInput() {
await nextTick();
window.requestAnimationFrame(() => {
inputRef.value?.focusInput();
});
}

async function stopCurrentSession() {
if (!currSessionId.value) return;
try {
Expand Down Expand Up @@ -1487,6 +1497,9 @@ function toggleTheme() {
align-items: center;
gap: 8px;
padding: 8px 12px;
padding-right: 68px;
position: relative;
box-sizing: border-box;
cursor: pointer;
text-align: left;
}
Expand All @@ -1511,15 +1524,24 @@ function toggleTheme() {
}

.session-actions {
display: none;
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
opacity: 0;
pointer-events: none;
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
visibility: hidden;
}

.session-item:hover .session-actions,
.session-item:focus-within .session-actions {
display: flex;
opacity: 1;
pointer-events: auto;
visibility: visible;
}

.session-action-btn {
Expand Down
Loading
Loading