Skip to content

feat(pkg-r): Support streaming ContentThinking#167

Open
simonpcouch wants to merge 6 commits intoposit-dev:mainfrom
simonpcouch:thinking
Open

feat(pkg-r): Support streaming ContentThinking#167
simonpcouch wants to merge 6 commits intoposit-dev:mainfrom
simonpcouch:thinking

Conversation

@simonpcouch
Copy link
Contributor

Addresses #76, but only on the R side. Adds support for displaying ContentThinking during streaming and on reload.

Without corresponding changes in ellmer, this PR doesn't do anything; with main ellmer, shinychat behaves as it currently does, just streaming the thinking content as if it were normal assistant text content. With an incoming ellmer PR (that adds a Remotes on this PR), this is how the content streams + reloads:

ellmer-shinychat-thinking.mp4

Here's the script I was using the test in that video:

# ...load dev versions of both packages

# Anthropic with thinking ------------------------------------------
chat_anthropic <- chat_claude(
  model = "claude-sonnet-4-5-20250929",
  params = params(reasoning_tokens = 1024)
)

clipr::write_clip(
  "Write a limerick about the tidyverse, returning only the limerick."
)
chat_app(chat_anthropic)

# OpenAI with reasoning ------------------------------------------
chat_openai <- chat_openai(
  model = "gpt-5.1",
  params = params(reasoning_effort = "high")
)

chat_app(chat_openai)

# Gemini with thinking ------------------------------------------
chat_gemini <- chat_google_gemini(
  model = "gemini-3-flash-preview",
  params = params(reasoning_tokens = 1024)
)

chat_app(chat_gemini)

compact(map(content@contents, contents_shinychat))
contents <- content@contents

# Consolidate adjacent ContentThinking into single blocks
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gemini likes to provide multiple sequential thinking blocks, otherwise resulting in UI like this without this change:

adjacent-thinking-blocks

```
  - pkg-r/inst/lib/shiny/thinking/thinking.js:68-75 – Map entries aren’t cleared when a thinking block completes
    (only deleted for empty text). Each streamed thinking block leaves a live entry holding DOM nodes, so a long
    session will leak memory. Delete the entry on done regardless of content.
  - pkg-r/inst/lib/shiny/thinking/thinking.js:39-45 – The retry loop (setTimeout) runs forever if a start
    arrives after the streaming message has disappeared (e.g., chat cleared or race on disconnect). Add a stop
    condition (max retries / bail when the chat container is gone) or drop the message when no streaming target
    exists.
```
@simonpcouch
Copy link
Contributor Author

I'm unable to request reviews on this repo, but cc @gadenbuie :)

@bianchenhao
Copy link

When will these codes be merged, I'm suffering this problem that each thinking content wraps lines. >_<

other attached packages:
[1] shiny_1.11.1 shinychat_0.3.0.9000 ellmer_0.4.0.9000

企业微信截图_17683051243913

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants