Skip to content

Reasoning ​

Chat UI shows the model's reasoning in a collapsible Thinking block above the answer, and can send a reasoning effort with each message.

Available from @cognipeer/chat-ui 0.6.0

Needs agent-server 0.6.0 or newer for stream.thinking frames and the reasoningEffort request field. Against an older server the block simply never appears and the effort is ignored.

The Thinking block ​

When the server streams stream.thinking frames, Chat UI keeps that text apart from the answer:

  • Each assistant message carries reasoning. The answer bubble and the stored answer text never contain it.
  • The block sits above the answer. It is open while the model is still reasoning and folds once the answer starts. The person can open and close it at any time, with the keyboard too.
  • The text is plain text and follows the theme tokens.
  • Stored messages that carry metadata.reasoning or metadata.thinking show the block too, so a conversation reloaded from the server keeps what the server stored.
  • The strings are chat.reasoning.* (English and Turkish). Override them with customMessages like any other string, see Internationalization.

Reasoning is usually a summary of the model's thinking, not its raw chain of thought. Providers decide what they return, and the agent behind the server decides whether the text is shown at all.

Hiding it ​

tsx
<Chat baseUrl="/api/agents" agentId="support" showReasoning={false} />
SwitchEffect
showReasoning prop on Chat / ChatMessageList (default true)false never renders the block
AgentInfo.showReasoning === false (set on the server)The block is never shown for that agent, even with showReasoning true. A block the client already holds for that agent is hidden too

The server-side switch is the one that matters for confidentiality: with it off the server does not send the text at all. The showReasoning prop only decides what this page renders.

Reasoning effort ​

Send a reasoning effort with every message the chat sends, including regenerate and edit:

tsx
<Chat baseUrl="/api/agents" agentId="support" reasoningEffort="high" />

reasoningEffort is one of none, minimal, low, medium, high (type ReasoningEffort). The server answers 400 for anything else, and leaves the agent's own setting in force when the prop is absent. There is no effort picker in the UI yet: the value is whatever your page passes.

With useChat ​

tsx
const chat = useChat({
  baseUrl: '/api/agents',
  agentId: 'support',
  getReasoningEffort: () => 'low',   // read when each message is sent
});

chat.streamingReasoning;             // the reasoning of the turn that is running
  • streamingReasoning holds the reasoning of the running turn. Finished messages keep theirs in Message.reasoning.
  • getReasoningEffort is a function, so the effort can follow a control of yours.
  • SendMessageOptions.reasoningEffort sets it for one send, regenerate or edit call.

Using the pieces yourself ​

tsx
import { ThinkingBlock } from '@cognipeer/chat-ui';

<ThinkingBlock reasoning={text} streaming={false} answering />
PropMeaning
reasoningThe text. An empty string renders nothing
streamingThe reasoning is still arriving: the block is open and its title reads "Thinking"
answeringThe answer has started: the block folds
classNameExtra class on the container

A custom StreamCallbacks receives onThinking(text, fullThinking), next to onText. See Streaming.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub