diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 4b8ba8d7a..9887ce2fc 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -501,6 +501,7 @@ [data-slot="session-turn-collapsible-trigger-content"] { max-width: 100%; + min-width: 0; display: flex; align-items: center; gap: 8px; @@ -525,6 +526,10 @@ [data-slot="session-turn-retry-message"] { font-weight: 500; color: var(--syntax-critical); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } [data-slot="session-turn-retry-seconds"] { @@ -549,6 +554,9 @@ .error-card { color: var(--text-on-critical-base); max-height: 240px; + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; overflow-y: auto; } diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 5ea9f64bb..c2e26b9c7 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -27,6 +27,59 @@ import { createResizeObserver } from "@solid-primitives/resize-observer" type Translator = (key: UiI18nKey, params?: UiI18nParams) => string +function record(value: unknown): value is Record { + return !!value && typeof value === "object" && !Array.isArray(value) +} + +function unwrap(message: string) { + const text = message.replace(/^Error:\s*/, "").trim() + + const parse = (value: string) => { + try { + return JSON.parse(value) as unknown + } catch { + return undefined + } + } + + const read = (value: string) => { + const first = parse(value) + if (typeof first !== "string") return first + return parse(first.trim()) + } + + let json = read(text) + + if (json === undefined) { + const start = text.indexOf("{") + const end = text.lastIndexOf("}") + if (start !== -1 && end > start) { + json = read(text.slice(start, end + 1)) + } + } + + if (!record(json)) return message + + const err = record(json.error) ? json.error : undefined + if (err) { + const type = typeof err.type === "string" ? err.type : undefined + const msg = typeof err.message === "string" ? err.message : undefined + if (type && msg) return `${type}: ${msg}` + if (msg) return msg + if (type) return type + const code = typeof err.code === "string" ? err.code : undefined + if (code) return code + } + + const msg = typeof json.message === "string" ? json.message : undefined + if (msg) return msg + + const reason = typeof json.error === "string" ? json.error : undefined + if (reason) return reason + + return message +} + function computeStatusFromPart(part: PartType | undefined, t: Translator): string | undefined { if (!part) return undefined @@ -236,6 +289,12 @@ export function SessionTurn( const lastAssistantMessage = createMemo(() => assistantMessages().at(-1)) const error = createMemo(() => assistantMessages().find((m) => m.error)?.error) + const errorText = createMemo(() => { + const msg = error()?.data?.message + if (typeof msg === "string") return unwrap(msg) + if (msg === undefined || msg === null) return "" + return unwrap(String(msg)) + }) const lastTextPart = createMemo(() => { const msgs = assistantMessages() @@ -463,6 +522,39 @@ export function SessionTurn( onCleanup(() => clearInterval(timer)) }) + let retryLog = "" + createEffect(() => { + const r = retry() + if (!r) return + const key = `${r.attempt}:${r.next}:${r.message}` + if (key === retryLog) return + retryLog = key + console.warn("[session-turn] retry", { + sessionID: props.sessionID, + messageID: props.messageID, + attempt: r.attempt, + next: r.next, + raw: r.message, + parsed: unwrap(r.message), + }) + }) + + let errorLog = "" + createEffect(() => { + const value = error()?.data?.message + if (value === undefined || value === null) return + const raw = typeof value === "string" ? value : String(value) + if (!raw) return + if (raw === errorLog) return + errorLog = raw + console.warn("[session-turn] assistant-error", { + sessionID: props.sessionID, + messageID: props.messageID, + raw, + parsed: unwrap(raw), + }) + }) + createEffect(() => { const update = () => { setStore("duration", duration()) @@ -595,7 +687,8 @@ export function SessionTurn( {(() => { const r = retry() if (!r) return "" - return r.message.length > 60 ? r.message.slice(0, 60) + "..." : r.message + const msg = unwrap(r.message) + return msg.length > 60 ? msg.slice(0, 60) + "..." : msg })()} @@ -640,7 +733,7 @@ export function SessionTurn( - {error()?.data?.message as string} + {errorText()} @@ -696,7 +789,7 @@ export function SessionTurn( - {error()?.data?.message as string} + {errorText()}