From 290f2be2fd3e48a043f41f1d26733c4b7e845259 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Tue, 7 Apr 2026 13:09:16 +0000 Subject: [PATCH] fix: resolve "sorry something went wrong" errors and show images in chat Three fixes from beta tester session feedback: 1. MCP error handling (backend/app/services/assistant_chat_service.py) - The MCP Microsoft Learn integration was catching only BadRequestError. Any other error type (APIStatusError, APIConnectionError, timeout) from the external MCP server propagated as a 502, causing the generic error. - Now catches all Exception types when MCP is active and retries without MCP using the stable client.messages.create endpoint. 2. Frontend error UX (frontend/src/pages/AssistantChatPage.tsx) - catch {} was silently swallowing all errors and inserting a generic assistant message. Now: differentiates 429 (rate limit) vs 502/503 (AI unavailable), removes the optimistic user message on failure, restores the failed message to the input so users can retry without retyping, and logs errors to console for debugging. 3. Image attachments visible in chat (frontend/src/components/assistant/ChatMessage.tsx) - Uploaded images were sent to the AI correctly but never shown in the chat thread. Now captures preview URLs before clearing pendingUploads and renders thumbnails above the user bubble, clickable to full size. Co-Authored-By: Claude Sonnet 4.6 --- .../app/services/assistant_chat_service.py | 24 +++++++--- .../src/components/assistant/ChatMessage.tsx | 18 +++++++- frontend/src/pages/AssistantChatPage.tsx | 45 +++++++++++++------ 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/backend/app/services/assistant_chat_service.py b/backend/app/services/assistant_chat_service.py index 6e17b8e3..184fd744 100644 --- a/backend/app/services/assistant_chat_service.py +++ b/backend/app/services/assistant_chat_service.py @@ -303,6 +303,8 @@ async def _call_anthropic_cached( } ] + _mcp_active = mcp_servers is not anthropic.NOT_GIVEN + try: response = await client.beta.messages.create( model=settings.AI_MODEL_ANTHROPIC, @@ -313,12 +315,22 @@ async def _call_anthropic_cached( tools=tools, betas=["mcp-client-2025-11-20"], ) - except anthropic.BadRequestError as e: - # MCP server failures (rate limits, connection errors) should not - # block the assistant entirely — retry without MCP tools. - if "MCP server" in str(e) and mcp_servers is not anthropic.NOT_GIVEN: - logger.warning("MCP server error, retrying without MCP: %s", e) - response = await client.beta.messages.create( + except Exception as e: + # MCP server failures surface as many error types — BadRequestError, + # APIStatusError, APIConnectionError, APITimeoutError. Always retry + # without MCP when MCP was active, so a flaky external server never + # blocks the assistant entirely. + _is_mcp_error = _mcp_active and ( + "MCP server" in str(e) + or "mcp" in type(e).__name__.lower() + or isinstance(e, (anthropic.BadRequestError, anthropic.APIStatusError)) + ) + if _is_mcp_error: + logger.warning( + "MCP server error (%s), retrying without MCP: %s", + type(e).__name__, e, + ) + response = await client.messages.create( model=settings.AI_MODEL_ANTHROPIC, max_tokens=max_tokens, system=system_blocks, diff --git a/frontend/src/components/assistant/ChatMessage.tsx b/frontend/src/components/assistant/ChatMessage.tsx index d905aada..c9a214d2 100644 --- a/frontend/src/components/assistant/ChatMessage.tsx +++ b/frontend/src/components/assistant/ChatMessage.tsx @@ -7,9 +7,10 @@ interface ChatMessageProps { role: 'user' | 'assistant' content: string suggestedFlows?: SuggestedFlow[] + imageUrls?: string[] } -export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps) { +export function ChatMessage({ role, content, suggestedFlows, imageUrls }: ChatMessageProps) { return (
{/* Avatar */} @@ -25,6 +26,21 @@ export function ChatMessage({ role, content, suggestedFlows }: ChatMessageProps) {/* Content */}
+ {/* Image attachments (user messages only) */} + {role === 'user' && imageUrls && imageUrls.length > 0 && ( +
+ {imageUrls.map((url, i) => ( + + {`Attachment + + ))} +
+ )} +
u.status === 'done' && u.result?.id) - .map((u) => u.result!.id) + const completedUploads = pendingUploads.filter((u) => u.status === 'done' && u.result?.id) + const completedUploadIds = completedUploads.map((u) => u.result!.id) + const imageUrls = completedUploads + .filter((u) => u.preview) + .map((u) => u.preview) setInput('') setPendingUploads([]) - setMessages(prev => [...prev, { role: 'user', content: userMessage }]) + setMessages(prev => [...prev, { role: 'user', content: userMessage, imageUrls: imageUrls.length > 0 ? imageUrls : undefined }]) setLoading(true) const sentForChatId = activeChatId @@ -363,11 +366,21 @@ export default function AssistantChatPage() { setActiveActions(response.actions || []) setShowTaskLane(true) } - } catch { - setMessages(prev => [ - ...prev, - { role: 'assistant', content: 'Sorry, something went wrong. Please try again.' }, - ]) + } catch (err: unknown) { + console.error('[AssistantChat] sendChatMessage failed:', err) + const status = (err as { response?: { status?: number } })?.response?.status + let errorMsg: string + if (status === 429) { + errorMsg = "You're sending messages too quickly. Wait a moment and try again." + } else if (status === 502 || status === 503) { + errorMsg = "The AI is temporarily unavailable. Please try again in a few seconds." + } else { + errorMsg = "Something went wrong sending your message. Please try again." + } + // Remove the optimistic user message and restore it to the input so they can retry + setMessages(prev => prev.slice(0, -1)) + setInput(userMessage) + toast.error(errorMsg) } finally { setLoading(false) requestAnimationFrame(() => inputRef.current?.focus()) @@ -421,11 +434,14 @@ export default function AssistantChatPage() { setActiveQuestions([]) setActiveActions([]) } - } catch { - setMessages(prev => [ - ...prev, - { role: 'assistant', content: 'Sorry, something went wrong processing your responses. Please try again.' }, - ]) + } catch (err: unknown) { + console.error('[AssistantChat] handleTaskSubmit failed:', err) + const status = (err as { response?: { status?: number } })?.response?.status + const errorMsg = status === 429 + ? "You're sending messages too quickly. Wait a moment and try again." + : "Something went wrong submitting your responses. Please try again." + setMessages(prev => prev.slice(0, -1)) + toast.error(errorMsg) } finally { setLoading(false) } @@ -832,6 +848,7 @@ export default function AssistantChatPage() { role={msg.role} content={msg.content} suggestedFlows={msg.suggestedFlows} + imageUrls={msg.imageUrls} /> ))} {loading && (