Skip to content

Adopt shinychat 0.4.0 greeting API with bookmark persistence#249

Draft
gadenbuie wants to merge 8 commits into
mainfrom
fix/greeting
Draft

Adopt shinychat 0.4.0 greeting API with bookmark persistence#249
gadenbuie wants to merge 8 commits into
mainfrom
fix/greeting

Conversation

@gadenbuie

Copy link
Copy Markdown
Contributor

Summary

Migrates greeting handling in both the R and Python packages onto the first-class greeting API in shinychat (which both packages already require at >= 0.4.0), replacing the old "append the greeting as a permanent assistant message on startup" pattern.

  • Static greetings render in the UI via chat_ui(greeting=) / chat_greeting() — shown instantly on load with no server round-trip, and they persist across bookmark/restore for free because they're part of the UI definition.
  • Generated greetings (when no greeting is provided) now use the {id}_greeting_requested input + chat_set_greeting() / Chat.set_greeting(), instead of an always-running startup observer/effect. They're generated on an isolated client (client(tools = NULL) / client(tools=None)) so GREETING_PROMPT and its response stay out of the live conversation's model history. The existing "no greeting provided" warning is preserved.
  • Drops the has_greeted bookkeeping (reactive value + its bookmark save/restore). shinychat now owns greeting visibility and dismissal state.
  • Greetings use dismissible = FALSE so the welcome + suggestion cards remain visible after the conversation starts (closest to the prior permanent-message behavior).

Bookmark-restore workaround (shinychat#253)

shinychat's bookmarking only round-trips the ellmer/chatlas client's chat turns; a greeting set via chat_set_greeting() is neither a turn nor exposed as readable state, so generated greetings are lost on restore. Until posit-dev/shinychat#253 is fixed, this PR captures the generated greeting text (from the isolated client's last turn) and saves/restores it through the module's own onBookmark/onRestore hooks. The greeting_requested handler reuses a restored greeting instead of regenerating. Static greetings need no tracking. Source comments at each site point back to #253 with removal hints for when it lands.

Verification

Static greeting (persists for free):

library(querychat)
qc <- QueryChat$new(mtcars, greeting = "Welcome! Ask me about the cars dataset.")
qc$app()

The greeting renders immediately on load (no LLM call). Bookmark the app and reopen the URL — the greeting is still there.

Generated greeting (captured + restored via the workaround):

qc <- QueryChat$new(mtcars)  # no greeting -> generated on first load
qc$app()

A warning is emitted and a greeting is generated once. Send a message, bookmark, then reopen the bookmark URL: the same generated greeting is restored above the conversation rather than disappearing or being regenerated.

Python mirrors both behaviors (QueryChat(...).app() / from_* constructors).

Render a static greeting via chat_ui(greeting=) and generate a dynamic
greeting on input$chat_greeting_requested via chat_set_greeting(),
replacing the startup observer that appended the greeting as a message.
Greetings are non-dismissible. Generated greetings stream from an
isolated client (tools = NULL) so they stay out of the conversation
history, and the has_greeted bookmark bookkeeping is removed.
Render a static greeting via chat_ui(greeting=) and generate a dynamic
greeting on the greeting_requested input via Chat.set_greeting(),
replacing the startup effect that appended the greeting as a message.
Greetings are non-dismissible. Generated greetings stream from an
isolated client (tools=None) so they stay out of the conversation
history, and the has_greeted bookmark bookkeeping is removed.
shinychat bookmarking only saves the ellmer client's chat state, not the
greeting set via chat_set_greeting(). Since generated greetings are
produced on an isolated client, they are absent on reload. Capture the
generated greeting text and save/restore it via the module's
onBookmark/onRestore hooks, re-displaying it on restore and reusing it
instead of regenerating when the greeting is requested again.
shinychat bookmarking only saves the chatlas client's chat state, not the
greeting set via Chat.set_greeting(). Since generated greetings are
produced on an isolated client, they are absent on reload. Capture the
generated greeting text and save/restore it via the module's
on_bookmark/on_restore hooks, re-displaying it on restore and reusing it
instead of regenerating when the greeting is requested again.
The generated-greeting bookmark/restore plumbing exists only because
shinychat does not persist or expose greeting state. Point the workaround
sites at posit-dev/shinychat#253 and spell out what to remove if it is fixed.
On empty-chat bookmark restore both the greeting_requested guard and
onRestore set the greeting (harmless, identical content); on non-empty
restore greeting_requested never fires, so onRestore is the only path.
Document this at the guard branch in both packages.
The greeting now renders in shinychat's dedicated greeting slot
(.shiny-chat-greeting) rather than as a persistent message in
.shiny-chat-messages-content. Point the e2e greeting assertions and viz
setup fixtures at the greeting slot instead of loc_messages /
loc_latest_message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant