Title: | Chat UI Component for 'shiny' |
---|---|
Description: | Provides a scrolling chat interface with multiline input, suitable for creating chatbot apps based on Large Language Models (LLMs). Designed to work particularly well with the 'elmer' R package for calling LLMs. |
Authors: | Joe Cheng [aut, cre], Carson Sievert [aut], Posit Software, PBC [cph, fnd] |
Maintainer: | Joe Cheng <[email protected]> |
License: | MIT + file LICENSE |
Version: | 0.1.1 |
Built: | 2024-12-28 05:20:03 UTC |
Source: | https://github.com/cran/shinychat |
The chat_append
function appends a message to an existing chat control. The
response
can be a string, string generator, string promise, or string
promise generator (as returned by the 'elmer' package's chat
, stream
,
chat_async
, and stream_async
methods, respectively).
This function should be called from a Shiny app's server. It is generally
used to append the model's response to the chat, while user messages are
added to the chat UI automatically by the front-end. You'd only need to use
chat_append(role="user")
if you are programmatically generating queries
from the server and sending them on behalf of the user, and want them to be
reflected in the UI.
chat_append( id, response, role = c("assistant", "user"), session = getDefaultReactiveDomain() )
chat_append( id, response, role = c("assistant", "user"), session = getDefaultReactiveDomain() )
id |
The ID of the chat element |
response |
The message or message stream to append to the chat element |
role |
The role of the message (either "assistant" or "user"). Defaults to "assistant". |
session |
The Shiny session object |
Returns a promise. This promise resolves when the message has been successfully sent to the client; note that it does not guarantee that the message was actually received or rendered by the client. The promise rejects if an error occurs while processing the response (see the "Error handling" section).
If the response
argument is a generator, promise, or promise generator, and
an error occurs while producing the message (e.g., an iteration in
stream_async
fails), the promise returned by chat_append
will reject with
the error. If the chat_append
call is the last expression in a Shiny
observer, Shiny will see that the observer failed, and end the user session.
If you prefer to handle the error gracefully, use promises::catch()
on the
promise returned by chat_append
.
library(shiny) library(coro) library(bslib) library(shinychat) # Dumbest chatbot in the world: ignores user input and chooses # a random, vague response. fake_chatbot <- async_generator(function(input) { responses <- c( "What does that suggest to you?", "I see.", "I'm not sure I understand you fully.", "What do you think?", "Can you elaborate on that?", "Interesting question! Let's examine thi... **See more**" ) await(async_sleep(1)) for (chunk in strsplit(sample(responses, 1), "")[[1]]) { yield(chunk) await(async_sleep(0.02)) } }) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { response <- fake_chatbot(input$chat_user_input) chat_append("chat", response) }) } shinyApp(ui, server)
library(shiny) library(coro) library(bslib) library(shinychat) # Dumbest chatbot in the world: ignores user input and chooses # a random, vague response. fake_chatbot <- async_generator(function(input) { responses <- c( "What does that suggest to you?", "I see.", "I'm not sure I understand you fully.", "What do you think?", "Can you elaborate on that?", "Interesting question! Let's examine thi... **See more**" ) await(async_sleep(1)) for (chunk in strsplit(sample(responses, 1), "")[[1]]) { yield(chunk) await(async_sleep(0.02)) } }) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { response <- fake_chatbot(input$chat_user_input) chat_append("chat", response) }) } shinyApp(ui, server)
For advanced users who want to control the message chunking behavior. Most
users should use chat_append()
instead.
chat_append_message( id, msg, chunk = TRUE, operation = c("append", "replace"), session = getDefaultReactiveDomain() )
chat_append_message( id, msg, chunk = TRUE, operation = c("append", "replace"), session = getDefaultReactiveDomain() )
id |
The ID of the chat element |
msg |
The message to append. Should be a named list with |
chunk |
Whether |
operation |
The operation to perform on the message. If |
session |
The Shiny session object |
Returns nothing (invisible(NULL)
).
library(shiny) library(coro) library(bslib) library(shinychat) # Dumbest chatbot in the world: ignores user input and chooses # a random, vague response. fake_chatbot <- async_generator(function(id, input) { responses <- c( "What does that suggest to you?", "I see.", "I'm not sure I understand you fully.", "What do you think?", "Can you elaborate on that?", "Interesting question! Let's examine thi... **See more**" ) # Use low-level chat_append_message() to temporarily set a progress message chat_append_message(id, list(role = "assistant", content = "_Thinking..._ ")) await(async_sleep(1)) # Clear the progress message chat_append_message(id, list(role = "assistant", content = ""), operation = "replace") for (chunk in strsplit(sample(responses, 1), "")[[1]]) { yield(chunk) await(async_sleep(0.02)) } }) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { response <- fake_chatbot("chat", input$chat_user_input) chat_append("chat", response) }) } shinyApp(ui, server)
library(shiny) library(coro) library(bslib) library(shinychat) # Dumbest chatbot in the world: ignores user input and chooses # a random, vague response. fake_chatbot <- async_generator(function(id, input) { responses <- c( "What does that suggest to you?", "I see.", "I'm not sure I understand you fully.", "What do you think?", "Can you elaborate on that?", "Interesting question! Let's examine thi... **See more**" ) # Use low-level chat_append_message() to temporarily set a progress message chat_append_message(id, list(role = "assistant", content = "_Thinking..._ ")) await(async_sleep(1)) # Clear the progress message chat_append_message(id, list(role = "assistant", content = ""), operation = "replace") for (chunk in strsplit(sample(responses, 1), "")[[1]]) { yield(chunk) await(async_sleep(0.02)) } }) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { response <- fake_chatbot("chat", input$chat_user_input) chat_append("chat", response) }) } shinyApp(ui, server)
Inserts a chat UI element into a Shiny UI, which includes a scrollable section for displaying chat messages, and an input field for the user to enter new messages.
To respond to user input, listen for input$ID_user_input
(for example, if
id="my_chat"
, user input will be at input$my_chat_user_input
), and use
chat_append()
to append messages to the chat.
chat_ui( id, ..., messages = NULL, placeholder = "Enter a message...", width = "min(680px, 100%)", height = "auto", fill = TRUE )
chat_ui( id, ..., messages = NULL, placeholder = "Enter a message...", width = "min(680px, 100%)", height = "auto", fill = TRUE )
id |
The ID of the chat element |
... |
Extra HTML attributes to include on the chat element |
messages |
A list of messages to prepopulate the chat with. Each
message can be a string or a named list with |
placeholder |
The placeholder text for the chat's user input field |
width |
The CSS width of the chat element |
height |
The CSS height of the chat element |
fill |
Whether the chat element should try to vertically fill its container, if the container is fillable |
A Shiny tag object, suitable for inclusion in a Shiny UI
library(shiny) library(bslib) library(shinychat) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { # In a real app, this would call out to a chat model or API, # perhaps using the 'elmer' package. response <- paste0( "You said:\n\n", "<blockquote>", htmltools::htmlEscape(input$chat_user_input), "</blockquote>" ) chat_append("chat", response) }) } shinyApp(ui, server)
library(shiny) library(bslib) library(shinychat) ui <- page_fillable( chat_ui("chat", fill = TRUE) ) server <- function(input, output, session) { observeEvent(input$chat_user_input, { # In a real app, this would call out to a chat model or API, # perhaps using the 'elmer' package. response <- paste0( "You said:\n\n", "<blockquote>", htmltools::htmlEscape(input$chat_user_input), "</blockquote>" ) chat_append("chat", response) }) } shinyApp(ui, server)