Skip to content

Feature: standalone register_slash_command() for chat_ui() without chat_server() #273

Description

@kaipingyang

Summary

chat_app.R has a standing TODO to "Support a standalone register_slash_command() that works outside the returned environment (e.g., so callers don't have to thread the return value)." This proposes exactly that.

Motivation

Slash commands are currently only reachable through chat_server()'s $slash_command() method. That has two friction points:

  1. You must thread the returned object around. To register a command from anywhere other than right next to the chat_server() call, you have to capture and pass its return value.
  2. Fully custom chat loops can't use slash commands at all. The Get started vignette says as much: "Slash commands are currently available only through chat_server(), not when building a fully custom chat loop with chat_ui() and chat_append() directly." Apps that drive chat_ui() with their own server logic (custom streaming, an external agent loop) can't offer the official palette.

Proposal

Export a free function that mirrors $slash_command() but is callable anywhere with just the chat id and session:

register_slash_command(
  id, name, description, handler, ...,
  echo = NULL, force = FALSE,
  session = getDefaultReactiveDomain()
)
  • Same handler semantics as $slash_command() (0/1-arg, or NULL for client-side handling via shiny:chat-slash-command); a 1-arg handler receives a ContentSlashCommand.
  • The command registry lives in session$userData keyed by the namespaced id (the same idiom already used for bookmark info in chat_restore.R), and the dispatch + sync observers are set up once, lazily — so no return value needs threading.
  • The slash machinery is factored into an internal helper that chat_server() reuses, so both paths share a single implementation and can't drift.

Follows shinychat's argument conventions (id first, session last, matching chat_append() / chat_clear() / update_chat_user_input()).

Open question

R's $slash_command() returns an unregister closure; I've kept that for register_slash_command(). Would you prefer a separate remove_slash_command(id, name, session) for parity with the Python Chat.remove_slash_command() method, or is returning the unregister function the right R idiom here? Happy to adjust.

I have a draft PR ready that implements this as a pure refactor (no behavior change to chat_server()), with standalone tests and a clean R CMD check.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Priority: MediumValid bug or well-defined request with moderate impact or a workaround.ai-triage:doneMarks an issue whose AI triage workflow is complete.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions