feat(webui): track optimistic message delivery status (#5162)
This commit is contained in:
@@ -11,9 +11,8 @@ interface StreamErrorNoticeProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismissible banner that surfaces transport-level faults the user needs to
|
||||
* know about. Rendered above the composer so the message the fault referred
|
||||
* to remains in view just above. ``role="alert"`` + ``aria-live="assertive"``
|
||||
* Fallback banner for transport-level faults that cannot be attached to a
|
||||
* visible failed message. ``role="alert"`` + ``aria-live="assertive"``
|
||||
* ensures screen readers announce the failure.
|
||||
*/
|
||||
export function StreamErrorNotice({ error, onDismiss }: StreamErrorNoticeProps) {
|
||||
|
||||
@@ -107,7 +107,11 @@ export function ThreadMessages({
|
||||
unit.type === "message" && unit.message.role === "assistant" && forkFlags[index]
|
||||
? nextUserIndex
|
||||
: undefined;
|
||||
if (unit.type === "message" && unit.message.role === "user") nextUserIndex += 1;
|
||||
if (
|
||||
unit.type === "message"
|
||||
&& unit.message.role === "user"
|
||||
&& unit.message.deliveryStatus !== "failed"
|
||||
) nextUserIndex += 1;
|
||||
|
||||
return (
|
||||
<ThreadDisplayUnit
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
installedMcpPresetsFromPayload,
|
||||
isMcpPresetsPayload,
|
||||
} from "@/lib/mcp-preset-events";
|
||||
import type { CanonicalRunSnapshot } from "@/lib/nanobot-client";
|
||||
import type { CanonicalRunSnapshot, StreamError } from "@/lib/nanobot-client";
|
||||
import { inferProviderFromModelName, providerDisplayLabel } from "@/lib/provider-brand";
|
||||
import type {
|
||||
ChatSummary,
|
||||
@@ -222,11 +222,28 @@ function latestActiveTurnId(messages: UIMessage[]): string | null {
|
||||
}
|
||||
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
||||
const message = messages[index];
|
||||
if (message.role === "user" && message.turnId) return message.turnId;
|
||||
if (
|
||||
message.role === "user"
|
||||
&& message.deliveryStatus !== "failed"
|
||||
&& message.turnId
|
||||
) return message.turnId;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasInlineDeliveryError(
|
||||
messages: UIMessage[],
|
||||
error: StreamError | null,
|
||||
): boolean {
|
||||
if (!error?.turnId) return false;
|
||||
return messages.some((message) => (
|
||||
message.role === "user"
|
||||
&& message.turnId === error.turnId
|
||||
&& message.deliveryStatus === "failed"
|
||||
&& message.deliveryErrorKind === error.kind
|
||||
));
|
||||
}
|
||||
|
||||
function completedAssistantTurnIds(messages: UIMessage[]): string[] {
|
||||
return Array.from(new Set(
|
||||
messages
|
||||
@@ -1283,7 +1300,7 @@ export function ThreadShell({
|
||||
|
||||
const composer = (
|
||||
<>
|
||||
{streamError ? (
|
||||
{streamError && !hasInlineDeliveryError(messages, streamError) ? (
|
||||
<StreamErrorNotice
|
||||
error={streamError}
|
||||
onDismiss={dismissStreamError}
|
||||
|
||||
@@ -250,7 +250,9 @@ export const ThreadViewport = forwardRef<ThreadViewportHandle, ThreadViewportPro
|
||||
const hiddenUserMessageCount =
|
||||
userMessageOffset
|
||||
+ (hiddenMessageCount > 0
|
||||
? messages.slice(0, hiddenMessageCount).filter((message) => message.role === "user").length
|
||||
? messages.slice(0, hiddenMessageCount).filter(
|
||||
(message) => message.role === "user" && message.deliveryStatus !== "failed",
|
||||
).length
|
||||
: 0);
|
||||
const visibleForkBoundaryMessageCount =
|
||||
forkBoundaryMessageCount !== null && forkBoundaryMessageCount > hiddenMessageCount
|
||||
|
||||
Reference in New Issue
Block a user