Skip to content

fix: Various Agent Stream Reasoning Bugs & Presentation Refactor#5677

Open
angelplusultra wants to merge 8 commits into
feat-reasoning-controlfrom
5676-bug-non-ollama-agent-providers-do-not-parse-and-present-reasoning-content
Open

fix: Various Agent Stream Reasoning Bugs & Presentation Refactor#5677
angelplusultra wants to merge 8 commits into
feat-reasoning-controlfrom
5676-bug-non-ollama-agent-providers-do-not-parse-and-present-reasoning-content

Conversation

@angelplusultra

@angelplusultra angelplusultra commented May 21, 2026

Copy link
Copy Markdown
Contributor

Pull Request Type

  • ✨ feat (New feature)
  • 🐛 fix (Bug fix)
  • ♻️ refactor (Code refactoring without changing behavior)
  • 💄 style (UI style changes)
  • 🔨 chore (Build, CI, maintenance)
  • 📝 docs (Documentation updates)

Relevant Issues

resolves #5676
UPSTREAM PR: #5972

TLDR

This PR fixes a few things:

  • Surfaces thinking tokens in agent mode for both the tooledStream and untooled stream pathways (any provider emitting reasoning_content), instead of only Ollama.
  • Unifies how reasoning is presented: it now streams live into the collapsible StatusResponse bubble (same place tool activity shows) and is ephemeral — a reloaded conversation shows only tool status + the final answer. This replaces the old <think> thought-pane that stacked a separate pane per step.
  • Ollama stream refactor: Ollama does not use tooled or untooledstream pathways, instead its own bespoke implementation using </think> tags. It has been refactored to match the behavior in tooled and untooled
  • Fixes the pulsing head icon. It used to stop pulsing the moment a tool call or the answer rendered, and on untooled reasoning turns it could get stuck pulsing forever. It now pulses through the whole agent status stream (thinking + tool calls) and settles to static once the final response streams or the agent goes idle.
  • Fixes untooled streams where the thoughts would vanish the instant the final response started streaming — they now persist in the collapsible status bubble, matching tooled.

Description

In agent mode, only ollama surfaced reasoning — and it did so as a standalone <think> thought pane that was persisted into the saved message. Every other provider routing through the shared OpenAI-compatible helpers (tooled.js / untooled.js) dropped reasoning_content entirely, so users got a long pause then only the final answer.

This PR does two things:

  • Surfaces reasoning for all providers routing through the shared helpers (any model emitting reasoning_content).
  • Unifies how reasoning is presented. It now streams live into the collapsible StatusResponse bubble (the same place tool activity shows), framed with "Thinking:", and is no longer persisted — a reloaded conversation shows only tool status + the final answer. This replaces the old <think> thought-pane approach, which stacked a separate pane per step in multi-step runs (per review feedback).

This is a behavior change for Ollama too: its agent reasoning used to render as a thought pane and survive reload, and is now ephemeral.

Scope:

  • Covered: the streaming paths (tooledStream, UnTooled.stream / streamingFunctionCall) plus Ollama.
  • Out of scope (native, non-OpenAI-shaped reasoning streams, follow-up?): anthropic.js, openai.js, gemini.js, bedrock.js.
  • Regular (non-agent) chat is unchanged — it still persists reasoning as <think> panes. The agent/regular asymmetry is noted for a separate discussion.

Implementation notes:

  • Reasoning streams under its own event id (${msgUUID}:reasoning) so the answer's textResponseChunk doesn't merge into the reasoning bubble and render thoughts as visible text.
  • Frontend: the TTS/copy action bar is now gated on a visibleResponseContent() helper so reasoning-only bubbles don't get a copy button that copies an empty string.

Agent head animation (pulse) fix:

The animated agent head is meant to pulse while the agent is actively thinking or invoking a tool, and go static once the final answer is streaming or the agent is idle. It was instead deriving "is the agent working" from the chat tail — pulse while a session is open and the last message is a status bubble. In an interactive @agent session the socket stays open between turns, so any turn that ended on a status bubble (e.g. an untooled reasoning turn that finished on a thought with no trailing answer) left the head pulsing forever.

It now drives the animation off the live stream events instead of the rendered tail:

  • A statusResponse (thinking) or toolCallInvocation event starts the pulse.
  • The final answer (textResponseChunk / fullTextResponse) or the end-of-turn markers (usageMetrics / chatId, which the agent backend emits at the end of every turn even when the answer is empty) settle it to static, as does a session start/end.

This is tracked with a small useIsAgentThinking() hook (mirroring useIsAgentSessionActive) fed by AGENT_THINKING_START / AGENT_THINKING_STOP events dispatched from the websocket handler.

Untooled reasoning persistence fix:

In tooled mode the reasoning bubble persists after the answer lands (collapsible), but in untooled mode the thoughts disappeared the moment the response finished. streamingFunctionCall was streaming both the reasoning and the tentative tool-call/answer preview under the same msgUUID, then calling removeStatusResponse(msgUUID) to clear the preview once the turn resolved — which wiped the thoughts along with it. Reasoning now streams under its own ${msgUUID}:reasoning bubble so the cleanup only removes the preview, matching tooled.js and the existing second-stream path.

Visuals (if applicable)

image

Additional Information

  • Tested across tooled and untooled paths with reasoning models (LM Studio, Koboldcpp, DeepSeek, Lemonade, Moonshot) in both agent and regular chat.

Developer Validations

  • I ran yarn lint from the root of the repo & committed changes
  • Relevant documentation has been updated (if applicable)
  • I have tested my code functionality
  • Docker build succeeds locally

@angelplusultra
angelplusultra marked this pull request as draft May 21, 2026 21:23
@angelplusultra
angelplusultra requested a review from shatfield4 May 21, 2026 23:58
@angelplusultra angelplusultra added the PR:needs review Needs review by core team label May 21, 2026
@angelplusultra
angelplusultra marked this pull request as ready for review May 21, 2026 23:58

@shatfield4 shatfield4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to work just fine for me on the backend using tooled and untooled providers with reasoning enabled. Tested both Deepseek and Lemonade providers with thinking models in both regular chat and agent modes. Some small UI issues here with the copy button appearing under the thoughts UI (when pressing copy nothing is copied so we need to make sure we hide this in the UI or make it functional).

Image Image

@angelplusultra

Copy link
Copy Markdown
Contributor Author

Seems to work just fine for me on the backend using tooled and untooled providers with reasoning enabled. Tested both Deepseek and Lemonade providers with thinking models in both regular chat and agent modes. Some small UI issues here with the copy button appearing under the thoughts UI (when pressing copy nothing is copied so we need to make sure we hide this in the UI or make it functional).
Image Image

@shatfield4 I was able to fix this in bcfad04

image

@shatfield4 shatfield4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested again and UI LGTM now.

@timothycarambat

Copy link
Copy Markdown
Member

@angelplusultra this seems to work well - however my only feedback is about the UI - I can understand wanting to break out thoughts but the very nature of thoughts is more like a debug log - they are ephemeral to understanding reasoning but we should collapse these into the StatusResponses we get from agent tools.

Otherwise, worse case on multi-step is we get a list of thoughts/status alternating until finally the user gets an answer - which is a lot of waste screenspace when all the user wants is the real output!

@timothycarambat timothycarambat removed the PR:needs review Needs review by core team label May 29, 2026

@shatfield4 shatfield4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is fully functional and now stacks all thoughts/agent calls into the single thoughts/agent call container.

I am noticing we are not seeing the animation being played though when thinking is in progress. Can you trigger the animation so it plays the animation while thinking is in progress?

@shatfield4 shatfield4 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@angelplusultra angelplusultra changed the title fix: parse reasoning_content in agent tool-calling helpers fix: Various Agent Stream Reasoning Bugs Jun 3, 2026
@angelplusultra angelplusultra changed the title fix: Various Agent Stream Reasoning Bugs fix: Various Agent Stream Reasoning Bugs & Presentation Refactor Jun 3, 2026

@timothycarambat timothycarambat left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's look at the frontend changes here - I think we can simplify the diff here a lot. Feels like we are doing a lot of change for something that should, ideally, be smaller footprint

@timothycarambat
timothycarambat changed the base branch from feat-reasoning-control to master June 3, 2026 21:22
@timothycarambat
timothycarambat changed the base branch from master to feat-reasoning-control June 3, 2026 21:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants