fix(chat): display error messages for failed assistant messages
This commit is contained in:
@@ -526,6 +526,26 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
}
|
}
|
||||||
const assistantSummaryForCopy = assistantSummaryRef.current;
|
const assistantSummaryForCopy = assistantSummaryRef.current;
|
||||||
|
|
||||||
|
const assistantErrorText = React.useMemo(() => {
|
||||||
|
if (isUser) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const errorInfo = (message.info as { error?: unknown } | undefined)?.error as
|
||||||
|
| { data?: { message?: unknown }; message?: unknown; name?: unknown }
|
||||||
|
| undefined;
|
||||||
|
if (!errorInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const dataMessage = typeof errorInfo.data?.message === 'string' ? errorInfo.data.message : undefined;
|
||||||
|
const errorMessage = typeof errorInfo.message === 'string' ? errorInfo.message : undefined;
|
||||||
|
const errorName = typeof errorInfo.name === 'string' ? errorInfo.name : undefined;
|
||||||
|
const detail = dataMessage || errorMessage || errorName;
|
||||||
|
if (!detail) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return `Opencode failed to send message with error:\n\`${detail}\``;
|
||||||
|
}, [isUser, message.info]);
|
||||||
|
|
||||||
const messageTextContent = React.useMemo(() => {
|
const messageTextContent = React.useMemo(() => {
|
||||||
if (isUser) {
|
if (isUser) {
|
||||||
const textParts = displayParts
|
const textParts = displayParts
|
||||||
@@ -540,12 +560,16 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
return combined.replace(/\n\s*\n+/g, '\n');
|
return combined.replace(/\n\s*\n+/g, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (assistantErrorText && assistantErrorText.trim().length > 0) {
|
||||||
|
return assistantErrorText;
|
||||||
|
}
|
||||||
|
|
||||||
if (assistantSummaryForCopy && assistantSummaryForCopy.trim().length > 0) {
|
if (assistantSummaryForCopy && assistantSummaryForCopy.trim().length > 0) {
|
||||||
return assistantSummaryForCopy;
|
return assistantSummaryForCopy;
|
||||||
}
|
}
|
||||||
|
|
||||||
return flattenAssistantTextParts(displayParts);
|
return flattenAssistantTextParts(displayParts);
|
||||||
}, [assistantSummaryForCopy, displayParts, isUser]);
|
}, [assistantErrorText, assistantSummaryForCopy, displayParts, isUser]);
|
||||||
|
|
||||||
const hasTextContent = messageTextContent.length > 0;
|
const hasTextContent = messageTextContent.length > 0;
|
||||||
|
|
||||||
@@ -767,6 +791,7 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
agentMention={agentMention}
|
agentMention={agentMention}
|
||||||
onRevert={handleRevert}
|
onRevert={handleRevert}
|
||||||
isFirstMessage={isFirstMessage}
|
isFirstMessage={isFirstMessage}
|
||||||
|
errorMessage={assistantErrorText}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -808,6 +833,7 @@ const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||||||
showReasoningTraces={showReasoningTraces}
|
showReasoningTraces={showReasoningTraces}
|
||||||
agentMention={agentMention}
|
agentMention={agentMention}
|
||||||
turnGroupingContext={turnGroupingContext}
|
turnGroupingContext={turnGroupingContext}
|
||||||
|
errorMessage={assistantErrorText}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ interface MessageBodyProps {
|
|||||||
turnGroupingContext?: TurnGroupingContext;
|
turnGroupingContext?: TurnGroupingContext;
|
||||||
onRevert?: () => void;
|
onRevert?: () => void;
|
||||||
isFirstMessage?: boolean;
|
isFirstMessage?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserMessageBody: React.FC<{
|
const UserMessageBody: React.FC<{
|
||||||
@@ -318,6 +319,7 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
|||||||
onAuxiliaryContentComplete,
|
onAuxiliaryContentComplete,
|
||||||
showReasoningTraces = false,
|
showReasoningTraces = false,
|
||||||
turnGroupingContext,
|
turnGroupingContext,
|
||||||
|
errorMessage,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
void _streamPhase;
|
void _streamPhase;
|
||||||
@@ -1051,7 +1053,9 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
|||||||
summaryBody &&
|
summaryBody &&
|
||||||
summaryBody.trim().length > 0;
|
summaryBody.trim().length > 0;
|
||||||
|
|
||||||
const shouldShowFooter = hasTextContent && assistantTextParts.length > 0 && hasStopFinish && isLastAssistantInTurn;
|
const showErrorMessage = Boolean(errorMessage);
|
||||||
|
|
||||||
|
const shouldShowFooter = isLastAssistantInTurn && hasTextContent && (hasStopFinish || Boolean(errorMessage));
|
||||||
const [isSummaryHovered, setIsSummaryHovered] = React.useState(false);
|
const [isSummaryHovered, setIsSummaryHovered] = React.useState(false);
|
||||||
|
|
||||||
const footerButtons = (
|
const footerButtons = (
|
||||||
@@ -1131,6 +1135,13 @@ const AssistantMessageBody: React.FC<Omit<MessageBodyProps, 'isUser'>> = ({
|
|||||||
className="leading-normal overflow-hidden text-foreground/90 [&_p:last-child]:mb-0 [&_ul:last-child]:mb-0 [&_ol:last-child]:mb-0"
|
className="leading-normal overflow-hidden text-foreground/90 [&_p:last-child]:mb-0 [&_ul:last-child]:mb-0 [&_ol:last-child]:mb-0"
|
||||||
>
|
>
|
||||||
{renderedParts}
|
{renderedParts}
|
||||||
|
{showErrorMessage && (
|
||||||
|
<FadeInOnReveal key="assistant-error">
|
||||||
|
<div className="group/assistant-text relative break-words">
|
||||||
|
<SimpleMarkdownRenderer content={errorMessage ?? ''} />
|
||||||
|
</div>
|
||||||
|
</FadeInOnReveal>
|
||||||
|
)}
|
||||||
{showSummaryBody && (
|
{showSummaryBody && (
|
||||||
<FadeInOnReveal key="summary-body">
|
<FadeInOnReveal key="summary-body">
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user