Chat
Chat(id, *, messages=(), on_error='auto', tokenizer=None)Create a chat interface.
A UI component for building conversational interfaces. With it, end users can submit messages, which will cause a .on_user_submit() callback to run. That callback gets passed the user input message, which can be used to generate a response. The response can then be appended to the chat using .append_message() or .append_message_stream().
Here’s a rough outline for how to implement a Chat:
from shiny.express import ui
# Create and display chat instance
chat = ui.Chat(id="my_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):
# Create a response message stream
response = await my_model.generate_response(user_input, stream=True)
# Append the response into the chat
await chat.append_message_stream(response)In the outline above, my_model.generate_response() is a placeholder for the function that generates a response based on the chat’s messages. This function will look different depending on the model you’re using, but it will generally involve passing the messages to the model and getting a response back. Also, you’ll typically have a choice to stream=True the response generation, and in that case, you’ll use .append_message_stream() instead of .append_message() to append the response to the chat. Streaming is preferrable when available since it allows for more responsive and scalable chat interfaces.
It is also highly recommended to use a package like chatlas to generate responses, especially when responses should be aware of the chat history, support tool calls, etc. See this article to learn more.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| id | str | A unique identifier for the chat session. In Shiny Core, make sure this id matches a corresponding :func:~shiny.ui.chat_ui call in the UI. |
required |
| messages | Sequence[Any] | Deprecated. Use chat.ui(messages=...) instead. |
() |
| on_error | Literal['auto', 'actual', 'sanitize', 'unhandled'] | How to handle errors that occur in response to user input. When "unhandled", the app will stop running when an error occurs. Otherwise, a notification is displayed to the user and the app continues to run. * "auto": Sanitize the error message if the app is set to sanitize errors, otherwise display the actual error message. * "actual": Display the actual error message to the user. * "sanitize": Sanitize the error message before displaying it to the user. * "unhandled": Do not display any error message to the user. |
'auto' |
| tokenizer | TokenEncoding | None | Deprecated. Token counting and message trimming features will be removed in a future version. | None |
Attributes
| Name | Description |
|---|---|
| latest_message_stream | React to changes in the latest message stream. |
Methods
| Name | Description |
|---|---|
| append_message | Append a message to the chat. |
| append_message_stream | Append a message as a stream of message chunks. |
| clear_messages | Clear all chat messages. |
| destroy | Destroy the chat instance. |
| enable_bookmarking | Enable bookmarking for the chat instance. |
| message_stream_context | Message stream context manager. |
| messages | Reactively read chat messages |
| on_user_submit | Define a function to invoke when user input is submitted. |
| set_user_message | Deprecated. Use update_user_input(value=value) instead. |
| transform_assistant_response | Deprecated. Assistant response transformation features will be removed in a future version. |
| transform_user_input | Deprecated. User input transformation features will be removed in a future version. |
| update_user_input | Update the user input. |
| user_input | Reactively read the user’s message. |
append_message
Chat.append_message(message, *, icon=None)Append a message to the chat.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| message | Any | A given message can be one of the following: * A string, which is interpreted as markdown and rendered to HTML on the client. * To prevent interpreting as markdown, mark the string as :class:~shiny.ui.HTML. * A UI element (specifically, a :class:~shiny.ui.TagChild). * This includes :class:~shiny.ui.TagList, which take UI elements (including strings) as children. In this case, strings are still interpreted as markdown as long as they’re not inside HTML. * A dictionary with content and role keys. The content key can contain content as described above, and the role key can be “assistant” or “user”. * More generally, any type registered with :func:shinychat.message_content. NOTE: content may include specially formatted input suggestion links (see note below). |
required |
| icon | HTML | Tag | TagList | None | An optional icon to display next to the message, currently only used for assistant messages. The icon can be any HTML element (e.g., an :func:~shiny.ui.img tag) or a string of HTML. |
None |
Note
Input suggestions are special links that send text to the user input box when clicked (or accessed via keyboard). They can be created in the following ways:
<span class='suggestion'>Suggestion text</span>: An inline text link that places ‘Suggestion text’ in the user input box when clicked.<img data-suggestion='Suggestion text' src='image.jpg'>: An image link with the same functionality as above.<span data-suggestion='Suggestion text'>Actual text</span>: An inline text link that places ‘Suggestion text’ in the user input box when clicked.
A suggestion can also be submitted automatically by doing one of the following:
- Adding a
submitCSS class or adata-suggestion-submit="true"attribute to the suggestion element. - Holding the
Ctrl/Cmdkey while clicking the suggestion link.
Note that a user may also opt-out of submitting a suggestion by holding the Alt/Option key while clicking the suggestion link.
Use .append_message_stream() instead of this method when stream=True (or similar) is specified in model’s completion method.
append_message_stream
Chat.append_message_stream(message, *, icon=None)Append a message as a stream of message chunks.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| message | Iterable[Any] | AsyncIterable[Any] | An (async) iterable of message chunks. Each chunk can be one of the following: * A string, which is interpreted as markdown and rendered to HTML on the client. * To prevent interpreting as markdown, mark the string as :class:~shiny.ui.HTML. * A UI element (specifically, a :class:~shiny.ui.TagChild). * This includes :class:~shiny.ui.TagList, which take UI elements (including strings) as children. In this case, strings are still interpreted as markdown as long as they’re not inside HTML. * A dictionary with content and role keys. The content key can contain content as described above, and the role key can be “assistant” or “user”. * More generally, any type registered with :func:shinychat.message_content_chunk. NOTE: content may include specially formatted input suggestion links (see note below). |
required |
| icon | HTML | Tag | None | An optional icon to display next to the message, currently only used for assistant messages. The icon can be any HTML element (e.g., an :func:~shiny.ui.img tag) or a string of HTML. |
None |
Note
Input suggestions are special links that send text to the user input box when
clicked (or accessed via keyboard). They can be created in the following ways:
* `<span class='suggestion'>Suggestion text</span>`: An inline text link that
places 'Suggestion text' in the user input box when clicked.
* `<img data-suggestion='Suggestion text' src='image.jpg'>`: An image link with
the same functionality as above.
* `<span data-suggestion='Suggestion text'>Actual text</span>`: An inline text
link that places 'Suggestion text' in the user input box when clicked.
A suggestion can also be submitted automatically by doing one of the following:
* Adding a `submit` CSS class or a `data-suggestion-submit="true"` attribute to
the suggestion element.
* Holding the `Ctrl/Cmd` key while clicking the suggestion link.
Note that a user may also opt-out of submitting a suggestion by holding the
`Alt/Option` key while clicking the suggestion link.
Use this method (over `.append_message()`) when `stream=True` (or similar) is
specified in model's completion method.
Returns
| Name | Type | Description |
|---|---|---|
An extended task that represents the streaming task. The .result() method of the task can be called in a reactive context to get the final state of the stream. |
clear_messages
Chat.clear_messages()Clear all chat messages.
destroy
Chat.destroy()Destroy the chat instance.
enable_bookmarking
Chat.enable_bookmarking(client, /, *, bookmark_on='response')Enable bookmarking for the chat instance.
This method registers on_bookmark and on_restore hooks on session.bookmark (:class:shiny.bookmark.Bookmark) to save/restore chat state on both the Chat and client= instances. In order for this method to actually work correctly, a bookmark_store= must be specified in shiny.App().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| client | 'ClientWithState | chatlas.Chat[Any, Any]' | The chat client instance to use for bookmarking. This can be a Chat model provider from chatlas, or more generally, an instance following the ClientWithState protocol. |
required |
| bookmark_on | Optional[Literal['response']] | The event to trigger the bookmarking on. Supported values include: - "response" (the default): a bookmark is triggered when the assistant is done responding. - None: no bookmark is triggered When this method triggers a bookmark, it also updates the URL query string to reflect the bookmarked state. |
'response' |
Raises
| Name | Type | Description |
|---|---|---|
| ValueError | If the Shiny App does have bookmarking enabled. |
Returns
| Name | Type | Description |
|---|---|---|
| CancelCallback | A callback to cancel the bookmarking hooks. |
message_stream_context
Chat.message_stream_context() Message stream context manager.
A context manager for appending streaming messages into the chat. This context
manager can:
1. Be used in isolation to append a new streaming message to the chat.
* Compared to `.append_message_stream()` this method is more flexible but
isn't non-blocking by default (i.e., it doesn't launch an extended task).
2. Be nested within itself
* Nesting is primarily useful for making checkpoints to `.clear()` back
to (see the example below).
3. Be used from within a `.append_message_stream()`
* Useful for inserting additional content from another context into the
stream (e.g., see the note about tool calls below).
Yields
:
A `MessageStream` class instance, which has a method for `.append()`ing
message content chunks to as well as way to `.clear()` the stream back to
it's initial state. Note that `.append()` supports the same message content
types as `.append_message()`.
Example
```python
import asyncio
from shiny import reactive
from shiny.express import ui
chat = ui.Chat(id="my_chat")
chat.ui()
@reactive.effect
async def _():
async with chat.message_stream_context() as msg:
await msg.append("Starting stream...
Progress:“) async with chat.message_stream_context() as progress: for x in [0, 50, 100]: await progress.append(f” {x}%“) await asyncio.sleep(1) await progress.clear() await msg.clear() await msg.append(”Completed stream”) ```
Note
A useful pattern for displaying tool calls in a chatbot is for the tool to
display using `.message_stream_context()` while the the response generation is
happening through `.append_message_stream()`. This allows the tool to display
things like progress updates (or other "ephemeral" content) and optionally
`.clear()` the stream back to it's initial state when ready to display the
"final" content.
messages
Chat.messages(
format=MISSING,
token_limits=None,
transform_user='all',
transform_assistant=False,
)Reactively read chat messages
Obtain chat messages within a reactive context.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| format | 'MISSING_TYPE | ProviderMessageFormat' | Deprecated. Provider-specific message formatting will be removed in a future version. | MISSING |
| token_limits | tuple[int, int] | None | Deprecated. Token counting and message trimming features will be removed in a future version. | None |
| transform_user | Literal['all', 'last', 'none'] | Deprecated. Message transformation features will be removed in a future version. | 'all' |
| transform_assistant | bool | Deprecated. Message transformation features will be removed in a future version. | False |
Note
Messages are listed in the order they were added. As a result, when this method is called in a .on_user_submit() callback (as it most often is), the last message will be the most recent one submitted by the user.
Returns
| Name | Type | Description |
|---|---|---|
| tuple[ChatMessage, …] | A tuple of chat messages. |
on_user_submit
Chat.on_user_submit(fn=None)Define a function to invoke when user input is submitted.
Apply this method as a decorator to a function (fn) that should be invoked when the user submits a message. This function can take an optional argument, which will be the user input message.
In many cases, the implementation of fn should also do the following:
- Generate a response based on the user input.
- If the response should be aware of chat history, use a package like chatlas to manage the chat state, or use the
.messages()method to get the chat history.
- Append that response to the chat component using
.append_message()( or.append_message_stream()if the response is streamed).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| fn | UserSubmitFunction | None | A function to invoke when user input is submitted. | None |
Note
This method creates a reactive effect that only gets invalidated when the user submits a message. Thus, the function fn can read other reactive dependencies, but it will only be re-invoked when the user submits a message.
set_user_message
Chat.set_user_message(value)Deprecated. Use update_user_input(value=value) instead.
transform_assistant_response
Chat.transform_assistant_response(fn=None)Deprecated. Assistant response transformation features will be removed in a future version.
transform_user_input
Chat.transform_user_input(fn=None)Deprecated. User input transformation features will be removed in a future version.
update_user_input
Chat.update_user_input(value=None, placeholder=None, submit=False, focus=False)Update the user input.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| value | str | None | The value to set the user input to. | None |
| placeholder | str | None | The placeholder text for the user input. | None |
| submit | bool | Whether to automatically submit the text for the user. Requires value. |
False |
| focus | bool | Whether to move focus to the input element. Requires value. |
False |
user_input
Chat.user_input(transform=False)Reactively read the user’s message.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| transform | bool | Whether to apply the user input transformation function (if one was provided). | False |
Returns
| Name | Type | Description |
|---|---|---|
| str | None | The user input message (before any transformation). |
Note
Most users shouldn’t need to use this method directly since the last item in .messages() contains the most recent user input. It can be useful for:
- Taking a reactive dependency on the user’s input outside of a
.on_user_submit()callback. - Maintaining message state separately from
.messages().