Skip to content

Commit 52e60e8

Browse files
authored
Delay sending of chat UI messages until reactive graph is flushed (#1593)
1 parent 32b9761 commit 52e60e8

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
* A handful of fixes for `ui.Chat()`, including:
2323
* A fix for use inside Shiny modules. (#1582)
2424
* `.messages(format="google")` now returns the correct role. (#1622)
25+
* `ui.Chat(messages)` are no longer dropped when dynamically rendered. (#1593)
2526
* `transform_assistant_response` can now return `None` and correctly handles change of content on the last chunk. (#1641)
2627

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

shiny/ui/_chat.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ async def append_message_stream(self, message: Iterable[Any] | AsyncIterable[Any
564564
async def _stream_task():
565565
await self._append_message_stream(message)
566566

567-
_stream_task()
567+
self._session.on_flushed(_stream_task, once=True)
568568

569569
# Since the task runs in the background (outside/beyond the current context,
570570
# if any), we need to manually raise any exceptions that occur
@@ -642,7 +642,9 @@ async def _send_append_message(
642642

643643
# print(msg)
644644

645-
await self._send_custom_message(msg_type, msg)
645+
# When streaming (i.e., chunk is truthy), we can send messages immediately
646+
# since we already waited for the flush in order to start the stream
647+
await self._send_custom_message(msg_type, msg, on_flushed=chunk is False)
646648
# TODO: Joe said it's a good idea to yield here, but I'm not sure why?
647649
# await asyncio.sleep(0)
648650

@@ -994,15 +996,23 @@ def destroy(self):
994996
async def _remove_loading_message(self):
995997
await self._send_custom_message("shiny-chat-remove-loading-message", None)
996998

997-
async def _send_custom_message(self, handler: str, obj: ClientMessage | None):
998-
await self._session.send_custom_message(
999-
"shinyChatMessage",
1000-
{
1001-
"id": self.id,
1002-
"handler": handler,
1003-
"obj": obj,
1004-
},
1005-
)
999+
async def _send_custom_message(
1000+
self, handler: str, obj: ClientMessage | None, on_flushed: bool = True
1001+
):
1002+
async def _do_send():
1003+
await self._session.send_custom_message(
1004+
"shinyChatMessage",
1005+
{
1006+
"id": self.id,
1007+
"handler": handler,
1008+
"obj": obj,
1009+
},
1010+
)
1011+
1012+
if on_flushed:
1013+
self._session.on_flushed(_do_send, once=True)
1014+
else:
1015+
await _do_send()
10061016

10071017

10081018
@add_example(ex_dir="../api-examples/chat")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from shiny.express import render, ui
2+
3+
chat = ui.Chat(id="chat", messages=["A starting message"])
4+
5+
6+
@render.ui
7+
def chat_output():
8+
return chat.ui()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from playwright.sync_api import Page, expect
2+
from utils.deploy_utils import skip_on_webkit
3+
4+
from shiny.playwright import controller
5+
from shiny.run import ShinyAppProc
6+
7+
8+
@skip_on_webkit
9+
def test_validate_chat_basic(page: Page, local_app: ShinyAppProc) -> None:
10+
page.goto(local_app.url)
11+
12+
chat = controller.Chat(page, "chat")
13+
14+
expect(chat.loc).to_be_visible(timeout=30 * 1000)
15+
chat.expect_latest_message("A starting message", timeout=30 * 1000)

tests/playwright/shiny/components/chat/stream/test_chat_stream.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ def test_validate_chat(page: Page, local_app: ShinyAppProc) -> None:
2121
expect(chat.loc_input_button).to_be_disabled()
2222

2323
messages = [
24-
"FIRST FIRST FIRST",
2524
"SECOND SECOND SECOND",
26-
"THIRD THIRD THIRD",
2725
"FOURTH FOURTH FOURTH",
26+
"FIRST FIRST FIRST",
27+
"THIRD THIRD THIRD",
2828
"FIFTH FIFTH FIFTH",
2929
]
3030
# Allow for any whitespace between messages

0 commit comments

Comments
 (0)