fix: surface assistant stream errors
This commit is contained in:
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 },
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user