### 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 ```