studio/frontend: surface tool-call activity during Generating phase
Until now, the assistant message rendered a static `Generating...` label whenever `message.content.length === 0 && status === "running"`. That covers the moment between Send and the first chunk landing, but also covers the much-longer window where the model is mid-way through emitting a `<tool_call>...</tool_call>` block: the closed pair gets stripped by the backend / chat-adapter before reaching the message parts, so content stays empty for the full tool-call duration. Users see the static label for many seconds and assume the model is stuck. Two changes: 1. Animated activity. The `<span>` now wraps a pulsing dot (same dot style as `RunningToolIndicator`) so even when the label can't say anything more specific, the user sees the bubble is alive. 2. Tool-call probe. If any text / reasoning part has been emitted and it contains the opening of any known tool-call shape (`<tool_call>`, `<function=`, `<|tool_call>`, `<|python_tag|>`, `[TOOL_CALLS]`), switch the label from `Generating...` to `Calling tool...`. The probe runs in the existing useAuiState selector so memo invalidation only happens when content actually changes. The `RunningToolIndicator` path (which renders once a real tool-call part has landed) is unchanged. The two indicators are mutually exclusive because RunningToolIndicator requires a tool-call part and GeneratingIndicator bails out the moment any non-text part shows up.
This commit is contained in:
parent
e379958b78
commit
c52bd6c57e
1 changed files with 51 additions and 7 deletions
|
|
@ -987,15 +987,59 @@ const MessageError: FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
// Heuristic: text the model emits before a tool actually starts often
|
||||
// includes the opening of one of the tool-call markup shapes the
|
||||
// backend strips. When that shape appears in the assistant's
|
||||
// content but no tool-call PART has landed yet (because the backend
|
||||
// only emits tool_start after the markup parses cleanly), the user
|
||||
// otherwise stares at a frozen "Generating..." for the whole time it
|
||||
// takes the model to finish writing the tool call.
|
||||
const TOOL_CALL_PROBE = /<tool_call>|<function=|<\|tool_call>|<\|python_tag\|>|\[TOOL_CALLS\]/i;
|
||||
|
||||
const GeneratingIndicator: FC = () => {
|
||||
const show = useAuiState(
|
||||
({ message }) =>
|
||||
message.content.length === 0 && message.status?.type === "running",
|
||||
);
|
||||
if (!show) {
|
||||
const phase = useAuiState(({ message }) => {
|
||||
if (message.status?.type !== "running") return null;
|
||||
const content = message.content;
|
||||
if (!content || content.length === 0) return "generating";
|
||||
// If every part is empty (placeholder text added but no chars yet)
|
||||
// we still want to surface generating activity.
|
||||
let allEmpty = true;
|
||||
let sawToolSignal = false;
|
||||
for (const p of content) {
|
||||
const t = (p as { type?: string }).type;
|
||||
if (t === "text" || t === "reasoning") {
|
||||
const text = ((p as { text?: string }).text ?? "");
|
||||
if (text.length > 0) {
|
||||
allEmpty = false;
|
||||
if (TOOL_CALL_PROBE.test(text)) {
|
||||
sawToolSignal = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// tool-call / source / etc. mean a real part exists; the
|
||||
// RunningToolIndicator or message body handles those.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (sawToolSignal) return "calling-tool";
|
||||
if (allEmpty) return "generating";
|
||||
return null;
|
||||
}
|
||||
return <span className="text-sm text-muted-foreground">Generating...</span>;
|
||||
});
|
||||
if (!phase) return null;
|
||||
return (
|
||||
<span
|
||||
data-slot="generating-indicator"
|
||||
data-phase={phase}
|
||||
className="aui-generating-indicator inline-flex items-center gap-2 text-sm text-muted-foreground"
|
||||
aria-live="polite"
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className="aui-generating-indicator-dot inline-block size-2 animate-pulse rounded-full bg-muted-foreground/60"
|
||||
/>
|
||||
<span>{phase === "calling-tool" ? "Calling tool..." : "Generating..."}</span>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// Placeholder when stop fires before any visible content (e.g. mid-think).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue