-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ui.Chat: Adding Shiny inputs via chat.append_message()
#1813
Comments
@cpsievert will be able to give more context on the road map and his vision for embedding Shiny inputs in chat messages when he returns from vacation later this month. In the mean time, here's a small amount of JavaScript that you can use to bind Shiny inputs after they are added to the chat: $('#chat').on('shiny-chat-append-message', () => $(document).one('shiny:idle', () => Shiny.bindAll())) Throw that line in a |
@cpsievert While here I noticed that we use a |
Thanks for the answer! It would seem there are some additional considerations when using from shiny import App, ui, reactive
app_ui = ui.page_fluid(
ui.chat_ui("chat"),
ui.tags.script(
"""
$(function() {
// We'll call Shiny.bindAll() after each appended message/chunk.
function rebind() {
console.log("[Debug] => calling Shiny.bindAll()");
Shiny.bindAll();
}
// Fired by chat.append_message(...)
$('#chat').on('shiny-chat-append-message', () => {
console.log("[Debug] chat-append-message event fired!");
$(document).one('shiny:idle', rebind);
});
// Fired by chat.append_message_stream(...)
$('#chat').on('shiny-chat-append-message-chunk', () => {
console.log("[Debug] chat-append-message-chunk event fired!");
$(document).one('shiny:idle', rebind);
});
});
"""
),
)
def server(input, output, session):
chat = ui.Chat("chat")
@chat.on_user_submit
async def on_user_submit():
user_messages = chat.messages()
user_text = user_messages[-1]["content"]
# Attempt to stream a message that includes an action link
shiny_link_html = ui.input_action_link("my_special_link", "Click me!")
await chat.append_message_stream(
f"Here is a special link in the chat: {shiny_link_html}"
)
@reactive.effect
def watch_link():
clicks = input.my_special_link()
if clicks > 0:
print(f"[Server] Link clicked {clicks} time(s).")
app = App(app_ui, server) While the streaming variant of appending a message might be a less frequent use case, I think it cannot be fully excluded. For example, if UI is generated by an LLM, as a part of its structured output, one might want to stream it (this would be actual Generative UI, unlike my initial example, where the UI element is pre-defined). Any tips on how to navigate this situation? Thanks in advance! |
The streaming case will definitely benefit from us solving this problem directly in the chat component, or at the very least in providing an "appended" event you can hook into. For the time being, you can replace the |
I’m using Python Shiny’s built-in
ui.chat_ui()
andchat.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 aninput_action_link
in the text passed tochat.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 usingui.insert_ui
) does force a re-scan, making it work. But I’d like to avoid that if possible.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?
The text was updated successfully, but these errors were encountered: