Browser freezes when streaming (very) long markdown responses
When streaming long markdown content, the browser can freeze as markdown is re-parsed on every incoming chunk. This is especially noticeable when using LLMs with tool calls, where each tool result can add substantial content to the stream.
<details>
<summary>Reprex</summary>
```r
library(shiny)
library(bslib)
library(shinychat)
library(coro)
# Simulate a long streaming response with "tool call" outputs
fake_stream <- async_generator(function(input) {
yield("I'll run several analyses for you.\n\n")
await(async_sleep(0.2))
for (i in 1:8) {
yield(sprintf("**Running tool %d: analyze_data**\n\n", i))
await(async_sleep(0.05))
# Large code block (simulates tool output)
code <- paste0("```r\n# Results for dataset ", i, "\n",
paste(sprintf("row_%02d <- c(%s)\n", 1:40,
sapply(1:40, function(x) paste(sample(1:99, 8), collapse = ", "))),
collapse = ""), "```\n\n")
for (chunk in strsplit(code, "(?<=.{50})", perl = TRUE)[[1]]) {
yield(chunk)
await(async_sleep(0.008))
}
# Markdown table
table <- paste0(
"| Metric | Value | SE | CI Low | CI High | p |\n",
"|--------|-------|-----|--------|---------|------|\n",
paste(sprintf("| %s | %.2f | %.2f | %.2f | %.2f | %.4f |\n",
c("Mean","Median","SD","Var","Skew","Kurt","Min","Max","Range","IQR"),
runif(10,0,100), runif(10,0,5), runif(10,-5,0),
runif(10,0,5), runif(10,0,0.1)), collapse = ""), "\n")
for (char in strsplit(table, "")[[1]]) {
yield(char)
await(async_sleep(0.003))
}
}
yield("All analyses complete!")
})
ui <- page_fillable(
p("Send any message to trigger a long streaming response."),
chat_ui("chat", fill = TRUE)
)
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
chat_append("chat", fake_stream(input$chat_user_input))
})
}
shinyApp(ui, server)
```
</details>
The browser becomes increasingly unresponsive as the response grows, eventually freezing for several seconds. In extreme cases, it completely freezes that app.
## Proposed solution
I have a local fix that implements incremental markdown rendering. It uses block-based rendering and throttling based on content size.
Would a PR be welcome? If so, should I only modify the `.ts` or should I also build and copy to both packages?
## Abbreviated Session info
```
shiny 1.12.1.9000
bslib 0.9.0.9002
coro 1.1.0
shinychat 0.3.0.9000
```
1 条评论