### Python Setup Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Installs Python dependencies using uv. Ensure uv is installed and configured.
```bash
uv sync --all-extras
```
--------------------------------
### JavaScript Setup Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Installs JavaScript dependencies for the js/ package. Run this command in the js/ directory.
```bash
cd js && npm install
```
--------------------------------
### R Package Setup Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Installs development dependencies for the R package using pak.
```bash
cd pkg-r && Rscript -e "pak::local_install_dev_deps()"
```
--------------------------------
### Preview Python Documentation
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Starts a local server to preview the Python package documentation using Quarto.
```bash
make py-docs-preview
```
--------------------------------
### Install and Enable Pre-commit Hooks
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/tests/playwright/MarkdownStream/basic/README.md
Installs pre-commit hooks that automatically format and lint code upon committing. This helps maintain code quality and consistency.
```sh
pre-commit install
```
--------------------------------
### Build a Chatbot with shinychat and ellmer
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/README.md
Example of creating a chatbot application using shinychat and ellmer. Requires an OpenAI API key set as an environment variable and the ellmer package installed. The chat interface is built with bslib::page_fillable and chat_ui, while the server logic uses ellmer::chat_openai for response generation and streams responses asynchronously.
```r
library(shiny)
library(shinychat)
ui <- bslib::page_fillable(
chat_ui(
id = "chat",
messages = "**Hello!** How can I help you today?"
),
fillable_mobile = TRUE
)
server <- function(input, output, session) {
chat <-
ellmer::chat_openai(
system_prompt = "Respond to the user as succinctly as possible."
)
observeEvent(input$chat_user_input, {
stream <- chat$stream_async(input$chat_user_input)
chat_append("chat", stream)
})
}
shinyApp(ui, server)
```
--------------------------------
### Install Shiny from PyPI
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/tests/playwright/MarkdownStream/basic/README.md
Installs the latest stable version of the Shiny package from the Python Package Index.
```sh
pip install shiny
```
--------------------------------
### Install Shinychat with uv pip
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/README.md
Install the shinychat package using uv pip. This command is used for general installation from PyPI.
```bash
uv pip install shinychat
```
--------------------------------
### Preview R Package Documentation
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Starts a local server to preview the R package's pkgdown site. Run from the pkg-r/ directory.
```bash
cd pkg-r && Rscript -e "pkgdown::preview_site()"
```
--------------------------------
### Install Shiny with Development and Test Dependencies
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/tests/playwright/MarkdownStream/basic/README.md
Installs Shiny in editable mode along with development and testing dependencies. This is recommended for developers who plan to contribute to the Shiny for Python project.
```sh
pip install -e ".[dev,test]"
```
--------------------------------
### Chat Class - Core Component (Python)
Source: https://context7.com/posit-dev/shinychat/llms.txt
Manages message state, user callbacks, and streaming responses. Use `chat_ui()` to place the UI element. This example shows basic setup with a chatlas client for handling user input and streaming responses.
```python
from shiny import App, ui, reactive
from shinychat import Chat, chat_ui
import chatlas
def app_ui(request):
return ui.page_fillable(
chat_ui(
"chat",
messages=[{"role": "assistant", "content": "Hello! How can I help?"}],
placeholder="Type your message...",
)
)
def server(input, output, session):
chat = Chat(id="chat", on_error="actual")
# One chatlas client per user session preserves conversation history
client = chatlas.ChatOpenAI(system_prompt="Be concise.")
@chat.on_user_submit
async def handle_input(user_input: str):
# stream=True returns an async iterable of string chunks
response = await client.chat_async(user_input, stream=True)
await chat.append_message_stream(response)
app = App(app_ui, server)
```
--------------------------------
### Install shinychat from CRAN
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/README.md
Install the shinychat package from the Comprehensive R Archive Network (CRAN).
```r
install.packages("shinychat")
```
--------------------------------
### Install Development Version of Shiny
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/tests/playwright/MarkdownStream/basic/README.md
Installs the latest development versions of htmltools and Shiny directly from their GitHub repositories. This is useful for testing the newest features or contributing to the project.
```sh
# First install htmltools, then shiny
pip install git+https://github.com/posit-dev/py-htmltools.git#egg=htmltools
pip install git+https://github.com/posit-dev/py-shiny.git#egg=shiny
```
--------------------------------
### Install shinychat Development Version
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/README.md
Install the development version of the shinychat package from GitHub using the pak package manager.
```r
# install.packages("pak")
pak::pak("posit-dev/shinychat/pkg-r")
```
--------------------------------
### Install Shinychat Development Version from GitHub
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/README.md
Install the development version of shinychat directly from its GitHub repository using uv pip. This is useful for testing the latest features or contributing to the project.
```bash
uv pip install git+https://github.com/posit-dev/shinychat.git
```
--------------------------------
### Python Playwright Installation
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Installs Playwright browsers required for running Python's Playwright tests.
```bash
uv run playwright install
```
--------------------------------
### Basic Shinychat Express App Example
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/README.md
A minimal example of a Shiny for Python Express app using the shinychat component. It sets page options, creates a chat interface with an initial message, and defines a callback to handle user input and display responses.
```python
from shiny.express import render, ui
from shinychat.express import Chat
# Set some Shiny page options
ui.page_opts(title="Hello Chat")
# Create a chat component, with an initial message
chat = Chat(
id="chat",
messages=[
{"content": "Hello! How can I help you today?", "role": "assistant"},
],
)
# Display the chat
chat.ui()
# Define a callback to run when the user submits a message
@chat.on_user_submit
async def handle_user_input(user_input: str):
await chat.append_message(f"You said: {user_input}")
"Message state:"
@render.code
def message_state():
return str(chat.messages())
```
--------------------------------
### Python Test Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Runs Python tests using pytest. Requires Playwright browser tests to be installed first.
```bash
uv run pytest
```
--------------------------------
### Chat.append_message() - Append Single Message (Python)
Source: https://context7.com/posit-dev/shinychat/llms.txt
Appends a complete message to the chat. Accepts markdown strings, HTML objects, or dictionaries with 'role' and 'content'. This example shows appending both a markdown string and raw HTML.
```python
from shiny.express import ui, render
from shinychat.express import Chat
from htmltools import HTML
chat = Chat(id="chat")
chat.ui()
@chat.on_user_submit
async def _(user_input: str):
# Plain markdown string
await chat.append_message(f"**Echo:** {user_input}")
# Force raw HTML (no Markdown parsing)
await chat.append_message(
{"role": "assistant", "content": HTML("Italic HTML response")}
)
```
--------------------------------
### Clear All Chat Messages with clear_messages()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Removes all messages from both the reactive state and the UI element. Use this to start a new conversation.
```Python
from shiny.express import ui, input
from shiny import reactive
from shinychat.express import Chat
chat = Chat(id="chat")
chat.ui()
uI.input_action_button("reset", "New conversation")
@reactive.effect
@reactive.event(input.reset)
async def _():
await chat.clear_messages()
```
--------------------------------
### Build R Package Documentation
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Builds the pkgdown site for the R package. Run from the pkg-r/ directory.
```bash
cd pkg-r && Rscript -e "pkgdown::build_site()"
```
--------------------------------
### JavaScript Build Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Builds the JavaScript components, including linting and bundling. Execute from the js/ directory.
```bash
cd js && npm run build
```
--------------------------------
### Python API Docs Build Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Builds the API documentation for the Python package using Quarto and quartodoc. Run from the Python docs directory.
```bash
cd pkg-py/docs && uv run quartodoc build
```
--------------------------------
### Chat UI with Simple Messages
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Initializes a chat UI and pre-populates it with simple string messages. These messages are rendered as assistant messages by default.
```R
chat_ui("chat", messages = list("Foo", "Bar"))
```
--------------------------------
### Makefile Help Target
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Displays all available targets in the Makefile for managing build tasks across different languages.
```bash
make help
```
--------------------------------
### Render Python Documentation
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Renders all documentation for the Python package using the Makefile.
```bash
make py-docs-render
```
--------------------------------
### R Package Documentation Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Generates R package documentation using devtools::document(). Run from the pkg-r/ directory.
```bash
cd pkg-r && Rscript -e "devtools::document()"
```
--------------------------------
### Chat UI with HTML and Dependencies
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Renders a chat UI with HTML content that includes a custom HTML dependency. The output includes the necessary dependencies and the rendered HTML.
```R
render_tags(chat_ui("chat", messages = list(div("Hello", htmlDependency("foo",
"1.0.0", "")),
span("world"))))
```
--------------------------------
### Basic Chat UI Initialization
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Initializes a chat UI with a given ID. This is the most basic usage.
```R
chat_ui("chat")
```
--------------------------------
### Python Build Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Builds the Python package.
```bash
uv build
```
--------------------------------
### Initialize Plausible Analytics
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/docs/_plausible.html
This snippet shows the basic JavaScript code to initialize Plausible Analytics. It ensures the plausible object and its methods are available, and then calls the init function.
```javascript
window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; plausible.init()
```
--------------------------------
### Chat UI with React-like Tag
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Initializes a chat UI with a message containing a special 'data-shinychat-react' tag, demonstrating how to embed custom interactive elements.
```R
react_tag <- tags$div("react", `data-shinychat-react` = NA)
chat_ui("chat", messages = list(tagList(tags$div("before"), react_tag, tags$div(
"after"))))
```
--------------------------------
### chat_ui()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Creates a chat UI element with a message list and input field. User input is captured via `input$_user_input` on the server.
```APIDOC
## chat_ui()
### Description
Inserts a `` web component into the Shiny UI. Provides a scrollable message list and a text input field. Listen to `input$_user_input` on the server to react to submitted messages.
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```r
library(shiny)
library(bslib)
library(shinychat)
ui <- page_fillable(
chat_ui(
id = "chat",
messages = list(
"**Hello!** How can I help you today?", # assistant greeting (markdown)
list(role = "user", content = "Tell me about R.") # pre-populated user message
),
placeholder = "Ask me anything...",
width = "min(680px, 100%)",
height = "auto",
fill = TRUE,
icon_assistant = tags$img(src = "robot.png", height = "24px")
),
fillable_mobile = TRUE
)
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
response <- paste0("Echo: ", input$chat_user_input)
chat_append("chat", response)
})
}
shinyApp(ui, server)
```
### Response
#### Success Response (200)
None
#### Response Example
None
```
--------------------------------
### Create a Chat UI Element with R
Source: https://context7.com/posit-dev/shinychat/llms.txt
Use `chat_ui()` to insert a chat interface into your Shiny app. Listen to `input$_user_input` to capture user messages.
```r
library(shiny)
library(bslib)
library(shinychat)
ui <- page_fillable(
chat_ui(
id = "chat",
messages = list(
"**Hello!** How can I help you today?", # assistant greeting (markdown)
list(role = "user", content = "Tell me about R.") # pre-populated user message
),
placeholder = "Ask me anything...",
width = "min(680px, 100%)",
height = "auto",
fill = TRUE,
icon_assistant = tags$img(src = "robot.png", height = "24px")
),
fillable_mobile = TRUE
)
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
response <- paste0("Echo: ", input$chat_user_input)
chat_append("chat", response)
})
}
shinyApp(ui, server)
```
--------------------------------
### Control Streaming with message_stream_context()
Source: https://context7.com/posit-dev/shinychat/llms.txt
An async context manager for fine-grained streaming control, supporting nesting for progress updates and injecting ephemeral content like tool-call indicators.
```Python
import asyncio
from shiny import reactive
from shiny.express import ui
from shinychat.express import Chat
chat = Chat(id="chat")
chat.ui()
@reactive.effect
async def show_progress():
async with chat.message_stream_context() as msg:
await msg.append("Starting analysis...\n\n**Progress:**")
async with chat.message_stream_context() as progress:
for pct in [0, 25, 50, 75, 100]:
await progress.append(f" {pct}%")
await asyncio.sleep(0.5)
if pct < 100:
await progress.clear() # resets to checkpoint
await msg.clear()
await msg.append("✅ Analysis complete!")
```
--------------------------------
### Chat UI with HTML Content
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Initializes a chat UI and includes messages containing HTML elements like div and span. These are rendered within a raw HTML tag.
```R
chat_ui("chat", messages = list(div("Hello"), span("world")))
```
--------------------------------
### Chat UI with Explicit Roles
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/chat.md
Initializes a chat UI with messages that have explicitly defined content and roles (e.g., 'assistant' or 'user').
```R
chat_ui("chat", messages = list(list(content = "Assistant", role = "assistant"),
list(content = "User", role = "user")))
```
--------------------------------
### Client-Side Unified Processors
Source: https://github.com/posit-dev/shinychat/blob/main/memory-bank/content-rendering.md
Illustrates the different unified processors available on the client-side for handling markdown, raw HTML, and semi-markdown content.
```typescript
// markdownProcessor - for LLM-generated markdown
// htmlProcessor - for raw HTML content
// semiMarkdownProcessor - for user input
```
--------------------------------
### R Package Test Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Runs tests for the R package using devtools::test(). Execute from the pkg-r/ directory.
```bash
cd pkg-r && Rscript -e "devtools::test()"
```
--------------------------------
### Python Coverage Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Runs tests with coverage and reports the results.
```bash
uv run coverage run -m pytest && uv run coverage report
```
--------------------------------
### Chat.enable_bookmarking()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Registers Shiny bookmark hooks to save and restore the chat messages and the underlying LLM client state. It automatically updates the URL query string after each assistant response.
```APIDOC
## Chat.enable_bookmarking()
### Description
Registers Shiny bookmark hooks to save and restore the chat messages and the underlying LLM client state. Automatically updates the URL query string after each assistant response.
### Method
`cancel = chat.enable_bookmarking(client, bookmark_on='response')`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- **client** (LLMClient) - Required - The LLM client instance to manage state for.
- **bookmark_on** (str, optional) - When to trigger bookmarking. Defaults to 'response'.
### Request Example
```python
from shiny import App
from shiny.express import app_opts, ui
from shinychat.express import Chat
import chatlas
app_opts(bookmark_store="url")
chat = Chat(id="chat")
chat.ui(messages=[{"role": "assistant", "content": "Chat history is preserved in the URL!"}])
client = chatlas.ChatAnthropic()
# Bookmarking saves both the UI messages and the client's turn history
cancel = chat.enable_bookmarking(client, bookmark_on="response")
@chat.on_user_submit
async def _(user_input: str):
stream = await client.stream_async(user_input)
await chat.append_message_stream(stream)
```
### Response
#### Success Response (200)
- **cancel** (Callable) - A function to call to disable bookmarking.
```
--------------------------------
### R Package Check Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Performs R package checks using devtools::check(). Documentation is not regenerated before checking.
```bash
cd pkg-r && Rscript -e "devtools::check(document = FALSE)"
```
--------------------------------
### Simulated Streaming Markdown Generator (R)
Source: https://context7.com/posit-dev/shinychat/llms.txt
Generates a markdown report in chunks with a delay between each chunk. Use this for simulating real-time content updates in a chat interface.
```r
ai_generator <- async_generator(function() {
lines <- c("## Report\n\n", "- Item one\n", "- Item two\n", "**Done.**")
for (chunk in lines) {
yield(chunk)
await(async_sleep(0.3))
}
})
ui <- page_fillable(
actionButton("run", "Generate report"),
output_markdown_stream("report", content_type = "markdown")
)
server <- function(input, output, session) {
observeEvent(input$run, {
markdown_stream("report", ai_generator(), operation = "replace")
})
}
shinyApp(ui, server)
```
```
--------------------------------
### Chat State Bookmarking with `chat_restore()`
Source: https://context7.com/posit-dev/shinychat/llms.txt
Enable Shiny bookmarking for chat state using `chat_restore()`. This function registers `onBookmark` and `onRestore` hooks to save and automatically restore the conversation history via URL or server-side bookmark stores. The UI function must accept a `request` argument.
```r
library(shiny)
library(bslib)
library(shinychat)
library(ellmer)
# UI must accept a `request` argument for bookmarking to work
ui <- function(request) {
page_fillable(chat_ui("chat", fill = TRUE))
}
server <- function(input, output, session) {
client <- chat_ollama(
model = "llama3.2",
system_prompt = "Always respond in exactly one sentence."
)
# Registers bookmark hooks; URL is updated automatically after each turn
chat_restore("chat", client,
bookmark_on_input = TRUE,
bookmark_on_response = TRUE
)
observeEvent(input$chat_user_input, {
chat_append("chat", client$stream_async(input$chat_user_input))
})
}
shinyApp(ui, server, enableBookmarking = "url")
```
--------------------------------
### R Code Formatting Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Formats R code using 'air'. Use '--check' to verify formatting without applying changes.
```bash
air format pkg-r/
```
--------------------------------
### JavaScript Watch Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Continuously rebuilds JavaScript components on file changes. Use this for active development in the js/ directory.
```bash
cd js && npm run watch
```
--------------------------------
### One-liner Chat App with `chat_app()`
Source: https://context7.com/posit-dev/shinychat/llms.txt
Quickly launch an interactive chat application using `chat_app()`. This function wraps an ellmer client in a minimal Shiny app, suitable for console use or single-user deployment. Ensure necessary environment variables (e.g., ANTHROPIC_API_KEY) are set.
```r
library(ellmer)
library(shinychat)
# Requires ANTHROPIC_API_KEY environment variable
client <- chat_anthropic(
system_prompt = "You are a pirate. Respond only in pirate speak."
)
# Opens browser immediately; press the × button or Escape to close
chat_app(client, bookmark_store = "url")
```
--------------------------------
### Persist Chat State with enable_bookmarking()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Registers Shiny bookmark hooks to save and restore chat messages and LLM client state. Automatically updates the URL query string after each assistant response.
```Python
from shiny import App
from shiny.express import app_opts, ui
from shinychat.express import Chat
import chatlas
app_opts(bookmark_store="url")
chat = Chat(id="chat")
chat.ui(messages=[{"role": "assistant", "content": "Chat history is preserved in the URL!"}])
client = chatlas.ChatAnthropic()
# Bookmarking saves both the UI messages and the client's turn history
cancel = chat.enable_bookmarking(client, bookmark_on="response")
@chat.on_user_submit
async def _(user_input: str):
stream = await client.stream_async(user_input)
await chat.append_message_stream(stream)
```
--------------------------------
### Render Markdown Stream with Content
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/markdown-stream.md
Render a markdown stream with specific text content. Newlines in the content are preserved and rendered correctly.
```R
output_markdown_stream("stream", content = "Foo\nBar")
```
--------------------------------
### Render Empty Markdown Stream
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/markdown-stream.md
Use `output_markdown_stream` to create an empty markdown stream component. This is useful for initializing the component before content is added.
```R
output_markdown_stream("stream")
```
--------------------------------
### chat_app()
Source: https://context7.com/posit-dev/shinychat/llms.txt
A convenient one-liner function to create an interactive chat application, wrapping an ellmer client for console use or single-user deployment.
```APIDOC
## chat_app()
### Description
One-liner interactive chat app. Wraps an ellmer client in a minimal Shiny app for interactive console use or single-user deployment.
### Usage
```r
chat_app(client, ...)
```
### Parameters
* **client** (ellmer client object) - An initialized ellmer client (e.g., `chat_anthropic()`).
* **...** - Additional arguments passed to `shiny::shinyApp()`.
```
--------------------------------
### Render Markdown Stream with HTML Dependencies
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-r/tests/testthat/_snaps/markdown-stream.md
Render markdown content that includes HTML and custom HTML dependencies. The `render_tags` function is used to process and include these dependencies.
```R
render_tags(output_markdown_stream("stream", content = div("Hello",
htmlDependency("foo", "1.0.0", ""))))
```
--------------------------------
### Python Type Check Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Performs static type checking on Python code using Pyright.
```bash
uv run pyright
```
--------------------------------
### Python Lint Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Checks Python code for style and potential errors using Ruff. Configured via pyproject.toml.
```bash
uv run ruff check pkg-py --config pyproject.toml
```
--------------------------------
### Reusable Chat Module with `chat_mod_ui()` / `chat_mod_server()`
Source: https://context7.com/posit-dev/shinychat/llms.txt
Encapsulate chat functionality as a Shiny module for multi-user or multi-chat applications. `chat_mod_server()` provides reactive handles for tracking conversation state and includes helper functions for appending, clearing, and updating user input.
```r
library(shiny)
library(bslib)
library(shinychat)
library(ellmer)
ui <- page_fillable(
titlePanel("Dual-model chat"),
layout_columns(
card(
card_header("Claude"),
chat_mod_ui("claude", messages = list("Hello from Claude!"))
),
card(
card_header("GPT-4o"),
chat_mod_ui("openai", messages = list("Hello from GPT-4o!"))
)
)
)
server <- function(input, output, session) {
# Create one client per user session (required for multi-user apps)
claude <- chat_anthropic(model = "claude-3-5-sonnet-latest")
gpt <- chat_openai(model = "gpt-4o")
mod_claude <- chat_mod_server("claude", claude)
mod_gpt <- chat_mod_server("openai", gpt)
# React to the last assistant turn for claude
observe({
turn <- mod_claude$last_turn()
if (!is.null(turn)) message("Claude last turn: ", turn@text[[1]])
})
}
shinyApp(ui, server)
```
--------------------------------
### Python Tox Multi-Version Testing
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Runs Python tests across multiple Python versions (3.9-3.13) using Tox.
```bash
uv run tox run-parallel
```
--------------------------------
### Chat.on_user_submit() - React to User Messages (Python)
Source: https://context7.com/posit-dev/shinychat/llms.txt
Registers an async callback for user message submissions. The callback receives the user's input string and can stream responses using `append_message_stream()`.
```python
from shiny.express import ui
from shinychat.express import Chat
import chatlas
chat = Chat(id="my_chat")
chat.ui(messages=[{"role": "assistant", "content": "Ask me anything!"}])
client = chatlas.ChatAnthropic(model="claude-3-5-sonnet-latest")
@chat.on_user_submit
async def _(user_input: str):
stream = await client.chat_async(user_input, stream=True)
await chat.append_message_stream(stream)
```
--------------------------------
### Update Python Distribution Assets
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Copies built JavaScript assets to the Python package directory after changes in js/. Requires a prior 'npm run build' in js/.
```bash
make py-update-dist
```
--------------------------------
### Append Streaming Responses to Chat UI with R
Source: https://context7.com/posit-dev/shinychat/llms.txt
Use `chat_append()` to send responses to the chat UI, supporting live streaming from async generators or promises. Includes basic error handling for streams.
```r
library(shiny)
library(bslib)
library(shinychat)
library(ellmer)
ui <- page_fillable(chat_ui("chat", fill = TRUE))
server <- function(input, output, session) {
chat_client <- chat_openai(
system_prompt = "You are a helpful assistant. Be concise.",
model = "gpt-4o"
)
observeEvent(input$chat_user_input, {
# stream_async() returns a streaming async generator
stream <- chat_client$stream_async(input$chat_user_input)
# chat_append handles chunked streaming automatically;
# the returned promise rejects if the stream errors
chat_append("chat", stream) |>
promises::catch(function(err) {
message("Stream error: ", conditionMessage(err))
})
})
}
shinyApp(ui, server)
```
--------------------------------
### chat_restore()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Enables Shiny bookmarking for chat state, automatically saving and restoring conversation history to URL or server-side bookmark stores.
```APIDOC
## chat_restore()
### Description
Shiny bookmarking for chat state. Registers `onBookmark` / `onRestore` hooks so the ellmer client's conversation history is saved to a URL or server-side bookmark store and automatically restored when the user revisits the URL.
### Usage
```r
chat_restore("chat_id", client, bookmark_on_input = TRUE, bookmark_on_response = TRUE)
```
### Parameters
* **chat_id** (character) - The ID of the chat output element.
* **client** (ellmer client object) - The ellmer client whose history will be bookmarked.
* **bookmark_on_input** (logical, optional) - Whether to bookmark on user input. Defaults to TRUE.
* **bookmark_on_response** (logical, optional) - Whether to bookmark on assistant response. Defaults to TRUE.
```
--------------------------------
### Update Chat Input Field with `update_chat_user_input()`
Source: https://context7.com/posit-dev/shinychat/llms.txt
Programmatically set the chat input field's value and placeholder, and optionally focus it or auto-submit the message. Useful for suggesting questions or pre-filling input.
```r
library(shiny)
library(bslib)
library(shinychat)
ui <- page_fillable(
chat_ui("chat"),
layout_columns(
fill = FALSE,
actionButton("suggest", "Suggest a question"),
actionButton("autosend", "Auto-send a question")
)
)
server <- function(input, output, session) {
observeEvent(input$suggest, {
update_chat_user_input("chat", value = "What is the meaning of life?", focus = TRUE)
})
observeEvent(input$autosend, {
update_chat_user_input("chat", value = "Summarize today's news.", submit = TRUE)
})
observeEvent(input$chat_user_input, {
chat_append("chat", paste0("You asked: ", input$chat_user_input))
})
}
shinyApp(ui, server)
```
--------------------------------
### Python Format Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Formats Python code using Ruff. This command applies automatic code style fixes.
```bash
uv run ruff check --fix pkg-py --config pyproject.toml && uv run ruff format pkg-py --config pyproject.toml
```
--------------------------------
### HTML Island Separation Logic
Source: https://github.com/posit-dev/shinychat/blob/main/memory-bank/content-rendering.md
Demonstrates how server-side code separates React-native elements from opaque HTML, wrapping the latter in tags.
```html
Some widget output
More widget output
```
```html
Some widget output
More widget output
```
--------------------------------
### Programmatically Control User Input Field
Source: https://context7.com/posit-dev/shinychat/llms.txt
Sets the text, placeholder, focus state, or auto-submits a message from the server side using `update_user_input()`.
```Python
from shiny.express import ui, input, render
from shinychat.express import Chat
chat = Chat(id="chat")
chat.ui()
ui.input_action_button("suggest", "Suggest a question")
ui.input_action_button("autosend", "Auto-send")
@reactive.effect
@reactive.event(input.suggest)
def _():
chat.update_user_input(value="What is Shiny?", focus=True)
@reactive.effect
@reactive.event(input.autosend)
def _():
chat.update_user_input(value="Explain async programming.", submit=True)
```
--------------------------------
### chat_mod_ui() / chat_mod_server()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Provides a reusable Shiny module for chat interfaces, suitable for multi-user or multi-chat applications. The server function returns reactive handles for input, turns, and helper functions.
```APIDOC
## chat_mod_ui() / chat_mod_server()
### Description
Reusable chat Shiny module. Encapsulates the full chat pattern as a Shiny module for multi-user or multi-chat apps. `chat_mod_server()` returns reactive handles for `last_input`, `last_turn`, and helpers `append()`, `clear()`, `update_user_input()`.
### Usage
```r
# UI
chat_mod_ui("module_id", messages = list(...))
# Server
chat_mod_server("module_id", client, ...)
```
### Parameters
* **module_id** (character) - The ID for the Shiny module.
* **client** (ellmer client object, for `chat_mod_server`) - An initialized ellmer client.
* **messages** (list, optional, for `chat_mod_ui`) - Initial messages to display in the chat.
### Returns (`chat_mod_server`)
* **last_input**: Reactive value of the last user input.
* **last_turn**: Reactive value of the last assistant turn.
* **append**: Function to append messages.
* **clear**: Function to clear messages.
* **update_user_input**: Function to update the user input field.
```
--------------------------------
### Update R Distribution Assets
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Copies built JavaScript assets to the R package directory after changes in js/. Requires a prior 'npm run build' in js/.
```bash
make r-update-dist
```
--------------------------------
### JavaScript Lint Command
Source: https://github.com/posit-dev/shinychat/blob/main/CLAUDE.md
Performs TypeScript checks and ESLint for code quality. Run within the js/ directory.
```bash
cd js && npm run lint
```
--------------------------------
### MarkdownStream class / output_markdown_stream()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Provides a standalone streaming output element for Markdown, HTML, or plain text. This is useful for non-conversational streaming use cases like AI report generation or live log display.
```APIDOC
## MarkdownStream class / output_markdown_stream()
### Description
Streams Markdown, HTML, or plain text into a standalone output element outside of a chat interface. Useful for AI report generation, live log display, and similar non-conversational streaming use cases.
### Method
`await stream.stream(response, clear=True)`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
- **response** (AsyncIterable[str]) - Required - An asynchronous iterable yielding string chunks to stream.
- **clear** (bool, optional) - Whether to clear the previous content before streaming. Defaults to True.
### Request Example
```python
from shiny import App, ui, reactive
from shinychat import MarkdownStream, output_markdown_stream
import chatlas
def app_ui(request):
return ui.page_fillable(
ui.input_action_button("generate", "Generate report"),
output_markdown_stream("report", content_type="markdown"),
)
def server(input, output, session):
stream = MarkdownStream(id="report", on_error="actual")
@reactive.effect
@reactive.event(input.generate)
async def _():
client = chatlas.ChatOpenAI()
response = await client.stream_async(
"Write a brief markdown report on Python async programming."
)
await stream.stream(response, clear=True)
app = App(app_ui, server)
```
### Response
None (This is a command to update the UI).
```
--------------------------------
### Chat.message_stream_context()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Provides an asynchronous context manager for fine-grained control over streaming messages. It supports nesting for checkpointed progress updates and can be used to inject ephemeral content like tool-call progress indicators.
```APIDOC
## Chat.message_stream_context()
### Description
An async context manager for fine-grained streaming control. Supports nesting (for checkpointed progress updates) and can be used from within `append_message_stream()` to inject ephemeral content such as tool-call progress indicators.
### Method
`async with chat.message_stream_context() as msg:`
### Parameters
None
### Request Example
```python
import asyncio
from shiny import reactive
from shiny.express import ui
from shinychat.express import Chat
chat = Chat(id="chat")
chat.ui()
@reactive.effect
async def show_progress():
async with chat.message_stream_context() as msg:
await msg.append("Starting analysis...\n\n**Progress:**")
async with chat.message_stream_context() as progress:
for pct in [0, 25, 50, 75, 100]:
await progress.append(f" {pct}%")
await asyncio.sleep(0.5)
if pct < 100:
await progress.clear() # resets to checkpoint
await msg.clear()
await msg.append("✅ Analysis complete!")
```
### Response
#### Success Response (200)
- **msg** (MessageStreamContext) - An object with methods like `append()` and `clear()` to control the streamed message.
```
--------------------------------
### output_markdown_stream() / markdown_stream()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Functions for displaying streaming Markdown, HTML, or text content in a standalone output element, useful for AI reports or log feeds.
```APIDOC
## output_markdown_stream() / markdown_stream()
### Description
Streaming markdown outside a chat. Displays streaming Markdown/HTML/text content in a standalone output element—useful for AI-generated reports, log feeds, or any non-chat streaming content.
### Usage
```r
# UI
output_markdown_stream("output_id")
# Server
markdown_stream("output_id", content_generator())
```
### Parameters
* **output_id** (character) - The ID of the output element.
* **content_generator** (coroutine or future) - A generator that yields chunks of Markdown/HTML/text content.
```
--------------------------------
### Custom Tool Result Rendering with S7 (R)
Source: https://context7.com/posit-dev/shinychat/llms.txt
Extends shinychat's `contents_shinychat` generic to render custom tool results. Define a new class inheriting from `ContentToolResult` and register a method to customize its display within `chat_ui()`.
```r
library(ellmer)
library(shinychat)
library(S7)
# 1. Define a custom ContentToolResult subclass
WeatherResult <- new_class(
"WeatherResult",
parent = ContentToolResult,
properties = list(location = class_character)
)
# 2. Register a method to render it in shinychat
contents_shinychat <- new_external_generic("shinychat", "contents_shinychat", "content")
method(contents_shinychat, WeatherResult) <- function(content) {
# Build on the default shinychat card via super()
res <- contents_shinychat(super(content, ContentToolResult))
res$title <- paste("Weather for", content@location)
res$value <- paste(content@value, collapse = "\n")
res$value_type <- "text"
res
}
# 3. Use as normal — shinychat auto-dispatches when the tool result arrives
get_weather <- tool(
function(lat, lon, location) WeatherResult(paste("22°C, sunny"), location = location),
name = "get_weather",
description = "Get the weather at a location.",
arguments = list(
lat = type_number("Latitude"),
lon = type_number("Longitude"),
location = type_string("Display name of the location")
)
)
```
--------------------------------
### Standalone Markdown Streaming with MarkdownStream
Source: https://context7.com/posit-dev/shinychat/llms.txt
Streams Markdown, HTML, or plain text into a standalone output element outside of a chat interface. Useful for AI report generation or live log display.
```Python
from shiny import App, ui, reactive
from shinychat import MarkdownStream, output_markdown_stream
import chatlas
def app_ui(request):
return ui.page_fillable(
ui.input_action_button("generate", "Generate report"),
output_markdown_stream("report", content_type="markdown"),
)
def server(input, output, session):
stream = MarkdownStream(id="report", on_error="actual")
@reactive.effect
@reactive.event(input.generate)
async def _():
client = chatlas.ChatOpenAI()
# chatlas async generator yields string chunks
response = await client.stream_async(
"Write a brief markdown report on Python async programming."
)
await stream.stream(response, clear=True)
app = App(app_ui, server)
```
--------------------------------
### Chat.on_user_submit() — React to user messages
Source: https://context7.com/posit-dev/shinychat/llms.txt
Decorator that registers an async callback invoked each time the user submits a message. The callback optionally receives the user input string.
```APIDOC
## Chat.on_user_submit()
### Description
Decorator that registers an async callback invoked each time the user submits a message. The callback optionally receives the user input string.
### Usage
```python
@chat.on_user_submit
async def _(user_input: str):
# Callback logic
pass
```
### Arguments
* `user_input` (str): The message submitted by the user.
```
--------------------------------
### chat_append()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Appends a message or a live-streaming response to an existing chat UI. It accepts strings, async generators, promises, or specific streams and returns a promise that resolves to the streamed content.
```APIDOC
## chat_append()
### Description
Appends a message or a live-streaming response to an existing `chat_ui()`. The `response` argument accepts a plain string, an async generator (`coro::async_generator()`), a promise, or an ellmer stream returned by `chat$stream_async()`. Returns a promise that resolves to the streamed content string.
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```r
library(shiny)
library(bslib)
library(shinychat)
library(ellmer)
ui <- page_fillable(chat_ui("chat", fill = TRUE))
server <- function(input, output, session) {
chat_client <- chat_openai(
system_prompt = "You are a helpful assistant. Be concise.",
model = "gpt-4o"
)
observeEvent(input$chat_user_input, {
# stream_async() returns a streaming async generator
stream <- chat_client$stream_async(input$chat_user_input)
# chat_append handles chunked streaming automatically;
# the returned promise rejects if the stream errors
chat_append("chat", stream) |>
promises::catch(function(err) {
message("Stream error: ", conditionMessage(err))
})
})
}
shinyApp(ui, server)
```
### Response
#### Success Response (200)
None
#### Response Example
None
```
--------------------------------
### Clear Chat Messages with `chat_clear()`
Source: https://context7.com/posit-dev/shinychat/llms.txt
Use `chat_clear()` to remove all messages from a chat UI. Remember to also reset the LLM history using the client's `set_turns()` method.
```r
library(shiny)
library(bslib)
library(shinychat)
ui <- page_fillable(
chat_ui("chat", fill = TRUE),
actionButton("clear_btn", "New conversation", class = "btn-secondary m-2")
)
server <- function(input, output, session) {
chat_client <- ellmer::chat_anthropic()
observeEvent(input$clear_btn, {
chat_clear("chat") # clears the UI
chat_client$set_turns(list()) # also reset the LLM history
})
observeEvent(input$chat_user_input, {
chat_append("chat", chat_client$stream_async(input$chat_user_input))
})
}
shinyApp(ui, server)
```
--------------------------------
### Handle Navigation and Animation Speed
Source: https://github.com/posit-dev/shinychat/blob/main/docs/404.html
This script detects internal navigation and back button presses to adjust animation speed. It also sets up listeners for internal link clicks to mark navigation as internal.
```javascript
window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; plausible.init() // Handle navigation detection including back button presses document.addEventListener('DOMContentLoaded', function () { const referrer = document.referrer; const internalNavigation = sessionStorage.getItem('shinychat-internal-navigation'); // Check if coming from within the site or using back button if (referrer && referrer.includes('posit-dev.github.io/shinychat') || internalNavigation === 'true') { // Set a faster animation speed document.documentElement.style.setProperty('--animation-speed', '0.2'); // 5x faster } // Clear the session storage after we've used it sessionStorage.removeItem('shinychat-internal-navigation'); // Add click listeners to all internal links document.addEventListener('click', function (e) { // Find the closest anchor tag if the click was on a child element const link = e.target.closest('a'); if (link && link.href && link.href.includes('posit-dev.github.io/shinychat')) { // Mark that we're navigating internally sessionStorage.setItem('shinychat-internal-navigation', 'true'); } }); });
```
--------------------------------
### contents_shinychat() — Custom tool result rendering
Source: https://context7.com/posit-dev/shinychat/llms.txt
An S7 generic that converts `ellmer::Content` objects into Shiny UI for display inside `chat_ui()`. Extend it to create custom rich displays for domain-specific tool results.
```APIDOC
## contents_shinychat()
### Description
An S7 generic that converts `ellmer::Content` objects into Shiny UI for display inside `chat_ui()`. Extend it to create custom rich displays for domain-specific tool results.
### Usage
```r
contents_shinychat(content)
```
### Arguments
* `content` (ellmer::Content): The content object to render.
```
--------------------------------
### RawHTML Component for InnerHTML Injection
Source: https://github.com/posit-dev/shinychat/blob/main/memory-bank/content-rendering.md
This snippet demonstrates the core logic of the RawHTML component, using a ref to inject HTML directly and manage Shiny bindings. It ensures Shiny bindings are preserved across React re-renders by opting out of React's DOM management for the injected content.
```tsx
const ref = useRef(null)
const shiny = useContext(ShinyLifecycleContext)
useEffect(() => {
const el = ref.current
if (!el) return
el.innerHTML = html
if (shiny && html) shiny.bindAll(el)
return () => { if (shiny && el) shiny.unbindAll(el) }
}, [html, shiny])
```
--------------------------------
### Low-Level Chunked Message Control in R
Source: https://context7.com/posit-dev/shinychat/llms.txt
Utilize `chat_append_message()` for granular control over message chunks, enabling patterns like temporary "Thinking…" indicators before replacing with the final response.
```r
library(shiny)
library(bslib)
library(coro)
library(shinychat)
fake_bot <- async_generator(function(id) {
# Show a temporary "thinking" indicator
chat_append_message(id, list(role = "assistant", content = "_Thinking…_ "), chunk = "start")
await(async_sleep(1))
# Replace with actual response
chat_append_message(id, list(role = "assistant", content = "Here is my answer!"), operation = "replace")
chat_append_message(id, list(role = "assistant", content = ""), chunk = "end")
})
ui <- page_fillable(chat_ui("chat", fill = TRUE))
server <- function(input, output, session) {
observeEvent(input$chat_user_input, {
fake_bot("chat")
})
}
shinyApp(ui, server)
```
--------------------------------
### HAST to React Component Mapping
Source: https://github.com/posit-dev/shinychat/blob/main/memory-bank/content-rendering.md
Shows how the hastToReact function maps HTML tags to specific React components, including custom elements and raw HTML wrappers.
```javascript
{
pre: CopyableCodeBlock,
table: BootstrapTable,
'shinychat-raw-html': RawHTML,
// ... other mappings
}
```
--------------------------------
### Disable Pre-commit Hooks
Source: https://github.com/posit-dev/shinychat/blob/main/pkg-py/tests/playwright/MarkdownStream/basic/README.md
Uninstalls the pre-commit hooks, disabling the automatic code formatting and linting on commit.
```sh
# pre-commit uninstall
```
--------------------------------
### Stream Chat Response with append_message_stream()
Source: https://context7.com/posit-dev/shinychat/llms.txt
Appends an async iterable of message chunks to the chat UI as they arrive. Runs in a background task and returns the task for status monitoring.
```Python
from shiny.express import ui
from shinychat.express import Chat
import chatlas
chat = Chat(id="chat")
chat.ui()
client = chatlas.ChatOpenAI(model="gpt-4o-mini")
@chat.on_user_submit
async def _(user_input: str):
# chatlas stream is an AsyncIterable[str]
response_stream = await client.stream_async(user_input)
task = await chat.append_message_stream(response_stream)
# Optionally react to stream completion in a separate effect
# task.result() is reactive and resolves to the full response string
```