-
Notifications
You must be signed in to change notification settings - Fork 114
Description
I’m using Python Shiny’s built-in ui.chat_ui()
and chat.append_message()
to build a chat interface. I’d like to insert a Shiny input (for example, an action link or a button) directly into a new chat message so that the user can interact with it. However, when I embed an input_action_link
in the text passed to chat.append_message(...)
, Shiny displays the link visually but does not treat it as a real input (i.e., input.my_special_link()
never increments).
The rationale for this approach is linked to best practices when building AI agents with a human in the loop. A chat-based input (which could even be constructed by an LLM) is a popular approach. It can be observed in ChatGPT, when user is asked to provide feedback. It can be found in the assistant-ui
library (labeled "Generative UI"), where it is used to confirm an AI action, such transaction (https://blog.langchain.dev/assistant-ui/). Of course, one could just add such an element outside the chat, but it makes the GUI more complex and can be cumbersome when you have multiple tools and/or actions. Aligning all temporary one-off inputs with their chat context seems more proper.
Below is my minimal reproducible example. When you run it and enter a message in the chat, it appends a new chat message containing a “special link,” but that link isn’t recognized by Shiny. Uncommenting the workaround (inserting an empty <script>
or using ui.insert_ui
) does force a re-scan, making it work. But I’d like to avoid that if possible.
from shiny import App, ui, reactive
app_ui = ui.page_fluid(
ui.chat_ui("chat")
)
def server(input, output, session):
# Initialize the chat
chat = ui.Chat("chat")
@chat.on_user_submit
async def on_user_submit():
user_messages = chat.messages()
user_text = user_messages[-1]["content"]
shiny_link_html = ui.input_action_link("my_special_link", "Click me!")
# This appears as a link visually but doesn't increment input.my_special_link()
await chat.append_message(
f"Here is a special link in the chat: {shiny_link_html}"
)
# Uncommenting the code below triggers a re-scan, which "fixes" it:
# ui.insert_ui(
# ui.HTML("<script></script>"),
# selector="body",
# where="beforeEnd"
# )
@reactive.effect
def watch_special_click():
clicks = input.my_special_link()
if clicks > 0:
print(f"[Server] Special link clicked {clicks} time(s).")
app = App(app_ui, server)
-
Is there an official or recommended way to add Shiny inputs inside a chat message so that they’re automatically recognized by
input[...]
without needing a manual DOM insertion workaround? -
Is there a stable, built-in function to trigger a “DOM re-bind” (like
Shiny.bindAll()
in R Shiny) from Python, rather than usingui.insert_ui(ui.HTML("<script></script>"))
as a hack? -
Is embedding Shiny inputs in chat messages expected to work in the future, or should I rely on separate UI insertion?