shinychat for Python website

shinychat provides a Shiny toolkit for building generative AI applications like chatbots and streaming content. It works best with chatlas, but also works great with other LLM frameworks such as LangChain, Pydantic AI, and more.

Installation

shinychat is a dependency of the shiny package, so in most cases, you’ll want to just install shiny with:

uv pip install shiny

However, you can also install shinychat separately with:

uv pip install shinychat

Or, install the development version of shinychat from GitHub with:

uv pip install git+https://github.com/posit-dev/shinychat.git

Quick start

The fastest way to build a chatbot with shinychat is to pass a chatlas client to Chat(client=). This gives you streaming, cancellation, bookmarking, and more out of the box — no manual wiring required.

Shiny Express

from chatlas import ChatAnthropic
from shiny.express import app_opts, ui
from shinychat.express import Chat

client = ChatAnthropic(system_prompt="You are a helpful assistant.")
chat = Chat(id="chat", client=client)
chat.ui()

ui.page_opts(fillable=True)
app_opts(bookmark_store="url")

Shiny Core

from shiny import App, ui
from chatlas import ChatAnthropic
from shinychat import Chat, chat_ui

app_ui = ui.page_fillable(
    chat_ui("chat"),
)

def server(input, output, session):
    client = ChatAnthropic(system_prompt="You are a helpful assistant.")
    chat = Chat("chat", client=client)

app = App(app_ui, server, bookmark_store="url")

The chat.client property provides a .set() method for swapping models mid-session and .clear() for resetting the conversation. For lower-level chat methods such as appending messages, updating the input, or inspecting stream state, call them directly on chat. See the API reference for the full interface.

Lower-level interface

When you need full control over the server-side logic — or you’re using an LLM framework other than chatlas — omit the client= argument and wire things up manually. This means handling streaming, cancellation, and bookmarking yourself, but gives you complete flexibility over how messages are generated and displayed.

Shiny Express

from shiny import reactive
from shiny.express import input
from chatlas import ChatAnthropic, StreamController
from shinychat.express import Chat

chat = Chat(id="chat")
chat.ui(enable_cancel=True)

client = ChatAnthropic(system_prompt="You are a helpful assistant.")
ctrl = StreamController()

@chat.on_user_submit
async def handle_user_input(user_input: str):
    response = await client.stream_async(user_input, controller=ctrl)
    await chat.append_message_stream(response)

@reactive.effect
@reactive.event(input.chat_cancel)
def handle_cancel():
    ctrl.cancel()

Shiny Core

from shiny import App, reactive, ui
from chatlas import ChatAnthropic, StreamController
from shinychat import Chat, chat_ui

app_ui = ui.page_fillable(
    chat_ui("chat", enable_cancel=True),
)

def server(input, output, session):
    client = ChatAnthropic(system_prompt="You are a helpful assistant.")
    chat = Chat("chat")
    ctrl = StreamController()

    @chat.on_user_submit
    async def handle_user_input(user_input: str):
        response = await client.stream_async(user_input, controller=ctrl)
        await chat.append_message_stream(response)

    @reactive.effect
    @reactive.event(input.chat_cancel)
    def handle_cancel():
        ctrl.cancel()

app = App(app_ui, server)

Key points:

  • enable_cancel=True shows the stop button while a response is streaming.
  • StreamController() is created once and reused — it automatically resets between streams.
  • Pass the controller to stream_async(controller=ctrl) so chatlas can honour the cancellation signal.
  • The cancel input fires as input.<id>_cancel (e.g. input.chat_cancel). Observe it with @reactive.event to call ctrl.cancel(). This is needed in both Express and Core apps.
  • Partial responses are automatically preserved in chat history by chatlas when a stream is cancelled.

Learn more

The official shiny website offers the best starting point for learning about shinychat: