Skip to content

Commit c7567d7

Browse files
authored
Migrate chat module API to shinychat's non-module chat_ui()/chat_server() (#38)
shinychat deprecated chat_mod_ui()/chat_mod_server() in favor of chat_ui()/chat_server(), which run in the caller's own session scope instead of a private module scope (posit-dev/shinychat#264). commons_ui()/ commons_server() replace commons_mod_ui()/commons_mod_server() to match. Since chat_server() now handles history persistence automatically, commons_server() no longer takes bookmark_on_input/bookmark_on_response. Fixes a bug introduced by dropping the module wrapper: the provenance-pill messages targeted a hardcoded "chat" id (previously correct only because session$ns() was scoped to the module id), so pills silently failed to attach for any chat id other than "chat". They now resolve the id the same way shinychat's own chat_server() internals do, via session$ns(id).
1 parent b0b3916 commit c7567d7

10 files changed

Lines changed: 138 additions & 135 deletions

File tree

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@ Suggests:
4848
readr,
4949
rmarkdown,
5050
shiny,
51-
shinychat,
51+
shinychat (> 0.4.0),
5252
testthat (>= 3.0.0),
5353
vitals,
5454
withr,
5555
yaml
5656
VignetteBuilder: knitr
5757
Remotes:
58+
posit-dev/shinychat,
5859
simonpcouch/ragnar@writable-extensions
5960
Config/testthat/edition: 3
6061
Encoding: UTF-8

NAMESPACE

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
export(Commons)
44
export(commons)
5-
export(commons_mod_server)
6-
export(commons_mod_ui)
5+
export(commons_server)
6+
export(commons_ui)
77
export(context_layer)
88
export(data_source)
99
export(list_tables)

R/chat.R

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,54 @@
1-
#' Shiny module for commons agents
1+
#' Shiny chat UI and server for commons agents
22
#'
3-
#' These functions wrap [shinychat::chat_mod_ui()] and
4-
#' [shinychat::chat_mod_server()] with commons-specific answer provenance UI.
5-
#' Answers produced from registered measures get a compact verified-answer
6-
#' pill. Answers produced from fallback SQL or R can cite text from the
7-
#' agent's context, measure definitions, or data documentation; verified
8-
#' citations render as footnotes whose tooltips name their source. Fallback
9-
#' answers with no verified citation get a potentially-untrusted caution pill.
3+
#' These functions wrap [shinychat::chat_ui()] and [shinychat::chat_server()]
4+
#' with commons-specific answer provenance UI. Answers produced from
5+
#' registered measures get a compact verified-answer pill. Answers produced
6+
#' from fallback SQL or R can cite text from the agent's context, measure
7+
#' definitions, or data documentation; verified citations render as footnotes
8+
#' whose tooltips name their source. Fallback answers with no verified
9+
#' citation get a potentially-untrusted caution pill.
1010
#'
11-
#' @param id Module ID.
12-
#' @param ... Arguments passed to [shinychat::chat_mod_ui()] or
13-
#' [shinychat::chat_mod_server()].
11+
#' @param id The ID of the chat element; must match between `commons_ui()`
12+
#' and `commons_server()`.
13+
#' @param ... Extra HTML attributes passed to [shinychat::chat_ui()].
1414
#' @param messages Initial messages shown in the chat. Passed to
15-
#' [shinychat::chat_mod_ui()].
15+
#' [shinychat::chat_ui()].
1616
#' @param height Chat container height. Defaults to `"100%"` so the chat input
1717
#' stays docked at the bottom of fill layouts.
1818
#' @param client A [commons()] agent. Create a new agent for each Shiny session.
19-
#' @param bookmark_on_input,bookmark_on_response Whether to add Shiny
20-
#' bookmarking hooks for user inputs and assistant responses.
2119
#'
22-
#' @return `commons_mod_ui()` returns UI. `commons_mod_server()` returns the
23-
#' shinychat module server result.
20+
#' @return `commons_ui()` returns UI. `commons_server()` returns the
21+
#' [shinychat::chat_server()] result.
2422
#'
2523
#' @examples
2624
#' \dontrun{
2725
#' library(shiny)
2826
#'
2927
#' ui <- page_fillable(
30-
#' commons_mod_ui("chat")
28+
#' commons_ui("chat")
3129
#' )
3230
#'
3331
#' server <- function(input, output, session) {
3432
#' agent <- commons(
3533
#' ellmer::chat_anthropic(),
3634
#' data_sources = data_source(sales = sales)
3735
#' )
38-
#' commons_mod_server("chat", agent)
36+
#' commons_server("chat", agent)
3937
#' }
4038
#'
4139
#' shinyApp(ui, server)
4240
#' }
4341
#'
4442
#' @export
45-
commons_mod_ui <- function(id, ..., messages = NULL, height = "100%") {
43+
commons_ui <- function(id, ..., messages = NULL, height = "100%") {
4644
check_chat_packages()
47-
ui <- shinychat::chat_mod_ui(id, ..., messages = messages, height = height)
45+
ui <- shinychat::chat_ui(id, ..., messages = messages, height = height)
4846
htmltools::attachDependencies(ui, commons_chat_dependency(), append = TRUE)
4947
}
5048

51-
#' @rdname commons_mod_ui
49+
#' @rdname commons_ui
5250
#' @export
53-
commons_mod_server <- function(
54-
id,
55-
client,
56-
bookmark_on_input = TRUE,
57-
bookmark_on_response = TRUE
58-
) {
51+
commons_server <- function(id, client) {
5952
check_chat_packages()
6053
check_commons_client(client)
6154

@@ -67,37 +60,35 @@ commons_mod_server <- function(
6760
tryCatch(client$prewarm(), error = function(err) NULL)
6861
})
6962

70-
mod <- shinychat::chat_mod_server(
71-
id,
72-
client = client,
73-
bookmark_on_input = bookmark_on_input,
74-
bookmark_on_response = bookmark_on_response
75-
)
63+
chat <- shinychat::chat_server(id, client = client)
7664

77-
shiny::moduleServer(id, function(input, output, session) {
78-
session$onFlushed(function() {
79-
seed_commons_pills(session, client)
80-
}, once = TRUE)
65+
session <- shiny::getDefaultReactiveDomain()
8166

82-
shiny::observeEvent(mod$last_turn(), {
83-
provenance <- commons_last_provenance(client)
84-
if (is.na(provenance$tag)) {
85-
return()
86-
}
67+
shiny::observeEvent(chat$last_turn(), ignoreNULL = TRUE, {
68+
provenance <- commons_last_provenance(client)
69+
if (is.na(provenance$tag)) {
70+
return()
71+
}
8772

88-
send_commons_pill(session, provenance)
89-
}, ignoreNULL = TRUE)
73+
send_commons_pill(session, id, provenance)
9074
})
9175

92-
mod
76+
session$onFlushed(
77+
function() {
78+
seed_commons_pills(session, id, client)
79+
},
80+
once = TRUE
81+
)
82+
83+
chat
9384
}
9485

95-
send_commons_pill <- function(session, provenance) {
86+
send_commons_pill <- function(session, id, provenance) {
9687
html <- htmltools::renderTags(commons_answer_pill(provenance$tag))$html
9788
session$sendCustomMessage(
9889
"commonsProvenancePill",
9990
list(
100-
id = session$ns("chat"),
91+
id = session$ns(id),
10192
html = html,
10293
citations = citations_payload(provenance$citations)
10394
)
@@ -106,7 +97,7 @@ send_commons_pill <- function(session, provenance) {
10697

10798
# Restored history renders as streams, so all seeded pills go in one
10899
# message and the client places them only once the transcript settles.
109-
seed_commons_pills <- function(session, client) {
100+
seed_commons_pills <- function(session, id, client) {
110101
provenances <- commons_exchange_provenance(
111102
client$get_turns(include_system_prompt = FALSE),
112103
client$citation_corpus()
@@ -131,7 +122,7 @@ seed_commons_pills <- function(session, client) {
131122

132123
session$sendCustomMessage(
133124
"commonsProvenancePillSeed",
134-
list(id = session$ns("chat"), count = n, pills = pills)
125+
list(id = session$ns(id), count = n, pills = pills)
135126
)
136127
}
137128

@@ -161,7 +152,7 @@ check_chat_packages <- function(call = rlang::caller_env()) {
161152
cli::cli_abort(
162153
c(
163154
"The {.pkg commons} chat module requires missing package{?s}: {.pkg {missing}}.",
164-
i = "Install {.pkg {missing}} to use {.fn commons_mod_ui} and {.fn commons_mod_server}."
155+
i = "Install {.pkg {missing}} to use {.fn commons_ui} and {.fn commons_server}."
165156
),
166157
call = call
167158
)

R/commons.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
#' layer, context search, table inspection, and SQL queries.
55
#'
66
#' The provider and model come from `client`; commons sets its own system prompt
7-
#' and tools. Use `agent$chat()` to ask questions, [commons_mod_ui()] and
8-
#' [commons_mod_server()] to embed the agent in Shiny, and [vitals::generate()]
7+
#' and tools. Use `agent$chat()` to ask questions, [commons_ui()] and
8+
#' [commons_server()] to embed the agent in Shiny, and [vitals::generate()]
99
#' to use the agent as a vitals solver.
1010
#'
1111
#' @param client An [ellmer::Chat] giving the provider and model to use, e.g.
@@ -253,15 +253,15 @@ Commons <- R6::R6Class(
253253

254254
#' @description Text that can back an answer's citations: context layer
255255
#' documents, measure definitions, and data dictionary entries. Used by
256-
#' [commons_mod_server()] to verify the citations fallback answers
256+
#' [commons_server()] to verify the citations fallback answers
257257
#' provide; not typically called directly.
258258
citation_corpus = function() {
259259
private$corpus
260260
},
261261

262262
#' @description Build the context layer's search index ahead of the first
263263
#' `search_context` call, e.g. during idle time right after a Shiny
264-
#' session starts. [commons_mod_server()] does this automatically.
264+
#' session starts. [commons_server()] does this automatically.
265265
prewarm = function() {
266266
layer <- private$context_layer
267267
if (!is.null(layer) && length(layer$docs) > 0) {

man/commons.Rd

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/commons_mod_ui.Rd

Lines changed: 0 additions & 66 deletions
This file was deleted.

man/commons_ui.Rd

Lines changed: 58 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/chat.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# commons_mod_server requires a commons agent
1+
# commons_server requires a commons agent
22

33
Code
4-
commons_mod_server("chat", client = test_client())
4+
commons_server("chat", client = test_client())
55
Condition
6-
Error in `commons_mod_server()`:
6+
Error in `commons_server()`:
77
! `client` must be a <Commons> object created by `commons()`.
88

0 commit comments

Comments
 (0)