ResponseView
A model's response as it arrives: Markdown with a quiet caret, reasoning in a Thinking disclosure, and calm stopped and failed states.
Subject: Your order #4471 is on its way — and we’re sorry it’s late
Hi Maya,
Your order #4471 left our warehouse on time, but a delay with our carrier held it up for three days. That’s not the experience we want for you, and I’m sorry.
It’s moving again and should arrive by Thursday. As a thank-you for your patience, your next order ships free — the code is applied to your account automatically.
Warmly,
The Northwind team
- First token
- 1.18 s
- Total
- 6.12 s
function Stream() {
const [state, setState] = useState<{ status: ResponseStatus; text: string; reasoning: string; took?: number; started?: number; total?: number }>({
status: "done",
text: gpt.text,
reasoning: gpt.reasoning!,
took: 3800,
total: 6120,
});
const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
useEffect(() => () => clearTimeout(timer.current), []);
const play = () => {
clearTimeout(timer.current);
const started = Date.now();
let r = 0;
let t = 0;
let took: number | undefined;
setState({ status: "pending", text: "", reasoning: "", started });
const tick = () => {
if (r < gpt.reasoning!.length) r += 8;
else {
took ??= Date.now() - started;
t += 6;
}
const done = t >= gpt.text.length;
setState({
status: done ? "done" : "streaming",
reasoning: gpt.reasoning!.slice(0, r),
text: gpt.text.slice(0, t),
started,
...(took !== undefined ? { took } : {}),
...(done ? { total: Date.now() - started } : {}),
});
if (!done) timer.current = setTimeout(tick, 24);
};
timer.current = setTimeout(tick, 700);
};
return (
<div className="flex w-full max-w-560 flex-col gap-16">
<div>
<Button size="sm" onClick={play} disabled={state.status === "pending" || state.status === "streaming"}>
Stream it again
</Button>
</div>
<ResponseView
aria-label="GPT-5 response"
status={state.status}
text={state.text}
reasoning={state.reasoning}
{...(state.took !== undefined ? { reasoningDuration: state.took } : {})}
footer={
<ResponseMetrics
reasoning
live={state.status === "pending" || state.status === "streaming"}
startedAt={state.started}
duration={state.total}
timeToFirstToken={state.status === "pending" ? undefined : 1180}
/>
}
/>
</div>
);
}import { ResponseView, UserMessage, Thread } from "@logicplanes/kaeso-ui/playground";Props
aria-labelstringThe response's accessible name ("GPT-5 response" in a comparison). Defaults to "Response" in the provider's language.
defaultReasoningOpenbooleanWhether the reasoning starts expanded.
footerReactNodeUnder the response: its metrics, actions.
onReasoningOpenChange((open: boolean) => void)Called when the reasoning is expanded or collapsed.
onRetry(() => void)Sends the turn again; shown with an error or a stop.
reasoningstringThe reasoning summary so far, as Markdown. A "Thinking" disclosure shows it, closed by default.
reasoningDurationnumberHow long the model reasoned, in milliseconds: "Thought for 4.2 s".
reasoningOpenbooleanWhether the reasoning is expanded, when you control it.
status"done" | "error" | "streaming" | "pending" | "stopped" = doneWhere the reply stands: waiting, streaming, done, stopped or failed.
textstringThe output text so far, as Markdown.