Skip to content

Commit

Permalink
Get ui.Chat() working inside Shiny modules (#1582)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpsievert authored Jul 29, 2024
1 parent de17aa3 commit 24fb5f9
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

* An empty `ui.input_date()` value no longer crashes Shiny. (#1528)

* `ui.Chat()` now works as expected inside Shiny modules. (#1582)

* Fixed bug where calling `.update_filter(None)` on a data frame renderer did not visually reset non-numeric column filters. (It did reset the column's filtering, just not the label). Now it resets filter's label. (#1557)

* Require shinyswatch >= 0.7.0 and updated examples accordingly. (#1558)
Expand Down
8 changes: 5 additions & 3 deletions shiny/ui/_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from .. import _utils, reactive
from .._docstring import add_example
from .._namespaces import resolve_id
from .._namespaces import ResolvedId, resolve_id
from ..session import require_active_session, session_context
from ..types import MISSING, MISSING_TYPE, NotifyException
from ..ui.css import CssUnit, as_css_unit
Expand Down Expand Up @@ -147,9 +147,11 @@ def __init__(
on_error: Literal["auto", "actual", "sanitize", "unhandled"] = "auto",
tokenizer: TokenEncoding | MISSING_TYPE | None = MISSING,
):
if not isinstance(id, str):
raise TypeError("`id` must be a string.")

self.id = id
self.user_input_id = f"{id}_user_input"
self.id = resolve_id(id)
self.user_input_id = ResolvedId(f"{self.id}_user_input")
self._transform_user: TransformUserInputAsync | None = None
self._transform_assistant: TransformAssistantResponseChunkAsync | None = None
if isinstance(tokenizer, MISSING_TYPE):
Expand Down
32 changes: 32 additions & 0 deletions tests/playwright/shiny/components/chat/module/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from htmltools import Tag

from shiny import App, Inputs, Outputs, Session, module, ui


@module.ui
def chat_mod_ui() -> Tag:
return ui.chat_ui(id="chat")


@module.server
def chat_mod_server(input: Inputs, output: Outputs, session: Session):
chat = ui.Chat(id="chat")

@chat.on_user_submit
async def _():
user = chat.user_input()
await chat.append_message(f"You said: {user}")


app_ui = ui.page_fillable(
ui.panel_title("Hello Shiny Chat"),
chat_mod_ui("foo"),
fillable_mobile=True,
)


def server(input: Inputs, output: Outputs, session: Session):
chat_mod_server("foo")


app = App(app_ui, server)
18 changes: 18 additions & 0 deletions tests/playwright/shiny/components/chat/module/test_chat_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from playwright.sync_api import Page, expect
from utils.deploy_utils import skip_on_webkit

from shiny.playwright import controller
from shiny.run import ShinyAppProc


@skip_on_webkit
def test_validate_chat_append_user_message(page: Page, local_app: ShinyAppProc) -> None:
page.goto(local_app.url)

chat = controller.Chat(page, "foo-chat")

# Verify starting state
expect(chat.loc).to_be_visible(timeout=30 * 1000)
chat.set_user_input("A user message")
chat.send_user_input()
chat.expect_latest_message("You said: A user message", timeout=30 * 1000)

0 comments on commit 24fb5f9

Please sign in to comment.