fix(frontend): update TimelineToolGroup to visually propagate error status with red color and X icon
This commit is contained in:
@@ -65,7 +65,12 @@ interface Message {
|
|||||||
|
|
||||||
type TimelineEntry =
|
type TimelineEntry =
|
||||||
| { kind: "thought"; text: string }
|
| { kind: "thought"; text: string }
|
||||||
| { kind: "tool"; name: string; status: "running" | "done"; result?: string }
|
| {
|
||||||
|
kind: "tool";
|
||||||
|
name: string;
|
||||||
|
status: "running" | "done" | "error";
|
||||||
|
result?: string;
|
||||||
|
}
|
||||||
// A text segment from one round of the assistant's tool loop.
|
// A text segment from one round of the assistant's tool loop.
|
||||||
// Each text SSE event from the server starts a new entry; subsequent
|
// Each text SSE event from the server starts a new entry; subsequent
|
||||||
// streaming chunks for that same round append to the most-recent
|
// streaming chunks for that same round append to the most-recent
|
||||||
@@ -589,6 +594,7 @@ function TimelineToolGroup({
|
|||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const count = entries.length;
|
const count = entries.length;
|
||||||
const allDone = entries.every((e) => e.status === "done");
|
const allDone = entries.every((e) => e.status === "done");
|
||||||
|
const hasError = entries.some((e) => e.status === "error");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -616,7 +622,9 @@ function TimelineToolGroup({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ width: 14, display: "flex", justifyContent: "center" }}>
|
<span style={{ width: 14, display: "flex", justifyContent: "center" }}>
|
||||||
{!allDone ? (
|
{hasError ? (
|
||||||
|
<span style={{ color: "#ef4444", fontWeight: "bold" }}>✗</span>
|
||||||
|
) : !allDone ? (
|
||||||
<Loader2
|
<Loader2
|
||||||
style={{ width: 12, height: 12 }}
|
style={{ width: 12, height: 12 }}
|
||||||
className="animate-spin"
|
className="animate-spin"
|
||||||
@@ -625,8 +633,9 @@ function TimelineToolGroup({
|
|||||||
<Wrench style={{ width: 12, height: 12, color: "#2e7d32" }} />
|
<Wrench style={{ width: 12, height: 12, color: "#2e7d32" }} />
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span style={{ flex: 1 }}>
|
<span style={{ flex: 1, color: hasError ? "#ef4444" : undefined }}>
|
||||||
{category} {count > 1 ? `(x${count})` : ""} {!allDone ? "..." : " ✓"}
|
{category} {count > 1 ? `(x${count})` : ""}
|
||||||
|
{hasError ? " ✗" : !allDone ? "..." : " ✓"}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
@@ -1294,6 +1303,14 @@ export function ChatPanel({
|
|||||||
// earlier same-named entries.
|
// earlier same-named entries.
|
||||||
let updated = false;
|
let updated = false;
|
||||||
const newTl: TimelineEntry[] = [];
|
const newTl: TimelineEntry[] = [];
|
||||||
|
|
||||||
|
const isToolErr =
|
||||||
|
typeof ev.result === "string" &&
|
||||||
|
(ev.result.toLowerCase().includes("error") ||
|
||||||
|
ev.result.toLowerCase().includes("failed") ||
|
||||||
|
ev.result.toLowerCase().includes("unexpected") ||
|
||||||
|
ev.result.toLowerCase().includes("not found"));
|
||||||
|
|
||||||
for (let i = tl.length - 1; i >= 0; i--) {
|
for (let i = tl.length - 1; i >= 0; i--) {
|
||||||
const e = tl[i];
|
const e = tl[i];
|
||||||
if (
|
if (
|
||||||
@@ -1304,7 +1321,7 @@ export function ChatPanel({
|
|||||||
) {
|
) {
|
||||||
newTl.unshift({
|
newTl.unshift({
|
||||||
...e,
|
...e,
|
||||||
status: "done",
|
status: isToolErr ? "error" : "done",
|
||||||
result: ev.result,
|
result: ev.result,
|
||||||
});
|
});
|
||||||
updated = true;
|
updated = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user