Skip to contents

Streams markdown content into a output_markdown_stream() UI element. A markdown stream can be useful for displaying generative AI responses (outside of a chat interface), streaming logs, or other use cases where chunks of content are generated over time.

Usage

markdown_stream(
  id,
  content_stream,
  operation = c("replace", "append"),
  session = getDefaultReactiveDomain()
)

Arguments

id

The ID of the markdown stream to stream content to.

content_stream

A string generator (e.g., coro::generator() or coro::async_generator()), a string promise (e.g., promises::promise()), or a string promise generator.

An item may also be a structured content block (a shinychat_block such as a web_search, web_search_results, or web_fetch block of the kind ellmer content normalization produces for chat_append()). Each block is sent as one complete, append-only structured block message. The client validates, groups, and renders it. Only the block types the stream client supports are accepted. html_block and the web_* family. Any other block type (e.g. a tool block, which the client would drop with a warning) raises an error.

operation

The operation to perform on the markdown stream. The default, "replace", will replace the current content with the new content stream. The other option, "append", will append the new content stream to the existing content.

session

The Shiny session object.

Value

A promise that resolves to the accumulated stream content as a single string. Structured blocks contribute nothing to the string.

Examples

if (FALSE) { # interactive()

library(shiny)
library(coro)
library(bslib)
library(shinychat)

# Define a generator that yields a random response
# (imagine this is a more sophisticated AI generator)
random_response_generator <- async_generator(function() {
  responses <- c(
    "What does that suggest to you?",
    "I see.",
    "I'm not sure I understand you fully.",
    "What do you think?",
    "Can you elaborate on that?",
    "Interesting question! Let's examine thi... **See more**"
  )

  await(async_sleep(1))
  for (chunk in strsplit(sample(responses, 1), "")[[1]]) {
    yield(chunk)
    await(async_sleep(0.02))
  }
})

ui <- page_fillable(
  actionButton("generate", "Generate response"),
  output_markdown_stream("stream")
)

server <- function(input, output, session) {
  observeEvent(input$generate, {
    markdown_stream("stream", random_response_generator())
  })
}

shinyApp(ui, server)
}