fix: surface assistant stream errors

This commit is contained in:
ajnart committed 2026-08-14 11:32:10 +02:00
1 parent 8a8e222da3
commit 27448868dd
5 files changed
+58 -5

No files matched your search

@@ -27,7 +27,8 @@ const getToolApprovalSecret = () =>
Buffer.from(
hkdfSync("sha256", Buffer.from(env.SECRET_ENCRYPTION_KEY, "hex"), "", "assistant-tool-approval", 32),
).toString("base64url");
const safeStreamError = "The model endpoint stopped the response. Check its URL, model, and credentials.";
const safeStreamError =
"The model endpoint stopped the response. Try again, or ask an administrator to verify its URL, model, and credentials.";
const requestSchema = z.object({
id: z.string().min(1).max(64),
@@ -267,9 +268,15 @@ export async function POST(request: Request) {
});
return result.toUIMessageStreamResponse({ onError: () => safeStreamError });
} catch {
} catch (error) {
logger.error("Assistant response could not start", {
error: error instanceof Error ? error.message : String(error),
});
return Response.json(
{ error: "The configured model endpoint could not start this response. Check its URL, model, and credentials." },
{
error:
"The configured model endpoint could not start this response. Try again, or ask an administrator to verify its URL, model, and credentials.",
},
{ status: 502 },
);
}
@@ -85,7 +85,12 @@
border-radius: var(--mantine-radius-md);
color: var(--mantine-color-red-text);
background: var(--mantine-color-red-light);
}
.messageErrorText {
color: var(--mantine-color-text);
font-size: var(--mantine-font-size-sm);
line-height: 1.45;
}
.tool {
@@ -5,6 +5,7 @@ import type { ToolCallMessagePartProps } from "@assistant-ui/react";
import {
ActionBarPrimitive,
ComposerPrimitive,
ErrorPrimitive,
MessagePartPrimitive,
MessagePrimitive,
ThreadListItemPrimitive,
@@ -16,6 +17,7 @@ import { ActionIcon, Box, Button, Drawer, Group, ScrollArea, Stack, Text, ThemeI
import { useMediaQuery } from "@mantine/hooks";
import {
IconActivityHeartbeat,
IconAlertTriangle,
IconApps,
IconArchive,
IconArrowUp,
@@ -151,7 +153,30 @@ const RuntimeError = () => {
const t = useScopedI18n("common.assistant");
return (
<MessagePrimitive.Error>
<Box className={classes.messageError}>{t("responseError")}</Box>
<ErrorPrimitive.Root className={classes.messageError}>
<Group align="flex-start" wrap="nowrap">
<ThemeIcon color="red" variant="light" radius="xl" size="md">
<IconAlertTriangle size={17} />
</ThemeIcon>
<Stack gap="xs" flex={1}>
<Text size="sm" fw={700}>
{t("responseError.title")}
</Text>
<ErrorPrimitive.Message className={classes.messageErrorText} />
<ActionBarPrimitive.Reload asChild>
<Button
variant="light"
color="red"
size="compact-sm"
w="fit-content"
leftSection={<IconRefresh size={14} />}
>
{t("responseError.retry")}
</Button>
</ActionBarPrimitive.Reload>
</Stack>
</Group>
</ErrorPrimitive.Root>
</MessagePrimitive.Error>
);
};
@@ -21,6 +21,7 @@ import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai";
import { clientApi, fetchApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { hotkeys } from "@homarr/definitions";
import { showErrorNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
import {
openMediaRequestSearch,
@@ -177,12 +178,23 @@ const createHistoryAdapter = (threadId: string | undefined): ThreadHistoryAdapte
});
const AssistantThreadRuntime = () => {
const t = useScopedI18n("common.assistant");
const threadId = useAuiState((state) => state.threadListItem.remoteId);
const transport = useMemo(() => new AssistantChatTransport({ api: "/api/assistant/chat" }), []);
const history = useMemo(() => createHistoryAdapter(threadId), [threadId]);
const onError = useCallback(
() =>
showErrorNotification({
title: t("responseError.title"),
message: t("responseError.description"),
autoClose: 10_000,
}),
[t],
);
return useChatRuntime<UIMessage>({
transport,
onError,
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
adapters: { history },
});
+5 -1
View File
@@ -2024,7 +2024,11 @@
"send": "Send message",
"copy": "Copy response",
"regenerate": "Regenerate response",
"responseError": "The response stopped unexpectedly. Try again, or ask an administrator to verify the model endpoint.",
"responseError": {
"title": "The assistant could not finish",
"description": "Try again. If the problem continues, ask an administrator to verify the configured model endpoint.",
"retry": "Try again"
},
"scrollToLatest": "Scroll to latest",
"approvalRequired": "Approval required",
"complete": "Complete",