(props)
| 26 | } |
| 27 | |
| 28 | const StreamErrorMessageBase: React.FC<StreamErrorMessageBaseProps> = (props) => { |
| 29 | const message = props.message; |
| 30 | const className = props.className; |
| 31 | const compactRetryAction = props.compactRetryAction; |
| 32 | const compactionDetails = props.compactionDetails; |
| 33 | |
| 34 | const debugAction = ( |
| 35 | <Tooltip> |
| 36 | <TooltipTrigger asChild> |
| 37 | <Button |
| 38 | variant="ghost" |
| 39 | size="icon" |
| 40 | onClick={() => { |
| 41 | window.dispatchEvent(createCustomEvent(CUSTOM_EVENTS.OPEN_DEBUG_LLM_REQUEST)); |
| 42 | }} |
| 43 | aria-label="Open last LLM request debug modal" |
| 44 | className="text-error/80 hover:text-error h-6 w-6" |
| 45 | > |
| 46 | <Bug className="h-3.5 w-3.5" /> |
| 47 | </Button> |
| 48 | </TooltipTrigger> |
| 49 | <TooltipContent align="center"> |
| 50 | <div className="flex items-center gap-2"> |
| 51 | <span>Debug last LLM request</span> |
| 52 | <code className="bg-foreground/5 text-foreground/80 border-foreground/10 rounded-sm border px-1.5 py-0.5 font-mono text-[10px]"> |
| 53 | /debug-llm-request |
| 54 | </code> |
| 55 | </div> |
| 56 | </TooltipContent> |
| 57 | </Tooltip> |
| 58 | ); |
| 59 | |
| 60 | // Runtime unavailable gets a distinct, friendlier presentation. |
| 61 | // This is a permanent failure (container/runtime doesn't exist), not a transient stream error. |
| 62 | // The backend sends "Container unavailable..." for Docker or "Runtime unavailable..." for others. |
| 63 | if (message.errorType === "runtime_not_ready") { |
| 64 | // Extract title from error message (e.g., "Container unavailable" or "Runtime unavailable") |
| 65 | const title = message.error?.split(".")[0] ?? "Runtime Unavailable"; |
| 66 | return ( |
| 67 | <div className={cn("bg-error-bg border border-error rounded px-5 py-4 my-3", className)}> |
| 68 | <div className="font-primary text-error mb-2 flex items-center gap-2 text-[13px] font-semibold"> |
| 69 | <AlertTriangle aria-hidden="true" className="h-4 w-4" /> |
| 70 | <span>{title}</span> |
| 71 | <div className="ml-auto flex items-center">{debugAction}</div> |
| 72 | </div> |
| 73 | <div className="text-foreground/80 text-[13px] leading-relaxed">{message.error}</div> |
| 74 | </div> |
| 75 | ); |
| 76 | } |
| 77 | |
| 78 | const provider = message.model ? getModelProvider(message.model) : ""; |
| 79 | const isAnthropicOverloaded = |
| 80 | provider === "anthropic" && |
| 81 | message.errorType === "server_error" && |
| 82 | /\bHTTP\s*529\b|overloaded/i.test(message.error); |
| 83 | const isEmptyOutputError = message.errorType === "empty_output"; |
| 84 | const isMaxOutputTokensError = message.errorType === "max_output_tokens"; |
| 85 | // Terminal refusal: distinct from network/empty-output messaging because retrying |
nothing calls this directly
no test coverage detected