SCPlotterChat is an R6 class that provides a conversational interface for
generating single-cell data visualizations through natural language. It
leverages Large Language Models (LLMs) via the tidyprompt package to
interpret user requests, select appropriate visualization functions,
identify relevant data objects, and generate executable R code — all
without requiring the user to know function names, parameter details, or
data formats.
Architecture
SCPlotterChat orchestrates a three-stage LLM pipeline for each user
request:
Stage 1 — Tool identification (locate_tool): The user's prompt is
analyzed against a registry of all exported scplotter functions
(built automatically from package documentation). The LLM selects the most
appropriate visualization function, considering the current conversation
history for context (e.g., "do a heatmap instead" reuses the previous
tool).
Stage 2 — Data identification (locate_data): The LLM identifies the
target data object from available sources:
Data frames and Seurat objects in the global environment
Datasets exported by scplotter, Seurat, SeuratObject, and scRepertoire
Manually set data via
$set_data()
Stage 3 — Code generation and execution (run_tool): The LLM
generates R code to call the selected visualization function with the
identified data object. The code is evaluated in an isolated environment,
and the resulting plot is returned. Validation ensures the generated code
is syntactically valid R.
Conversation history
SCPlotterChat maintains a conversation history across calls to $ask().
Each interaction records the user's prompt, the selected tool, the data
object, and the generated R code. This enables contextual follow-up
requests such as "change the palette to Spectral" or "facet by condition"
— the LLM understands that these are refinements of the previous plot
rather than new independent requests.
Tool registry
On initialization, SCPlotterChat scans the scplotter package
namespace for all exported functions and parses their Rd documentation to
build a tool registry. Each tool entry contains the function's title,
description, usage signature, arguments (with descriptions), and usage
examples. For ... arguments that reference other package functions
(e.g., [plotthis::Heatmap()]), the documentation is recursively
expanded to include those functions' arguments as well.
Two special pseudo-tools are also registered:
ListTools— Lists all available visualization functionsListData— Lists all available data objects
When the LLM selects either of these, the chat responds with a listing rather than generating a plot.
LLM provider setup
SCPlotterChat requires an LLM provider object from the tidyprompt
package. Any provider supported by tidyprompt can be used:
OpenAI, DeepSeek, Ollama (local), and others. See
https://tjarkvandemerwe.github.io/tidyprompt/articles/getting_started.html#setup-an-llm-provider
for setup instructions.
Methods
SCPlotterChat$new()
Create a new instance of the SCPlotterChat class. On initialization, the chat scans the scplotter package for all exported visualization functions and builds a tool registry from their documentation. It also discovers available data objects from the global environment and from packages (scplotter, Seurat, SeuratObject, scRepertoire).
Usage
SCPlotterChat$new(provider, verbose = FALSE)Arguments
providerAn LLM provider object from the tidyprompt package (e.g., created via
tidyprompt::llm_provider_openai()). See https://tjarkvandemerwe.github.io/tidyprompt/articles/getting_started.html#setup-an-llm-provider for a full list of supported providers and setup instructions.verboseLogical; if
TRUE, prints the full prompt and LLM response for each interaction. Useful for debugging prompt engineering or understanding how the LLM interprets requests. Default isFALSE. Note: this overrides theverbosesetting of the provider object.
SCPlotterChat$clear_history()
Clear the conversation history. Useful for starting a
fresh conversation without creating a new SCPlotterChat instance.
After clearing, the LLM will have no memory of previous requests.
SCPlotterChat$get_history()
Retrieve the conversation history. Each entry records the user prompt and the assistant's response (tool used, data object, and generated R code). The history provides conversational context for follow-up requests.
SCPlotterChat$list_tools()
Print a list of all available visualization tools (exported scplotter functions) that the LLM can use. Each entry shows the function name, its title, and a brief description extracted from the package documentation.
SCPlotterChat$list_data()
Print a list of all available data objects that the LLM can use for visualization. Data sources include: objects in the global environment (data frames and Seurat objects), and datasets exported by scplotter, Seurat, SeuratObject, and scRepertoire.
SCPlotterChat$set_data()
Manually set the data object to be used for
visualization. This bypasses the automatic data detection in
$ask() — when data is set via this method, all subsequent
$ask() calls will use this data regardless of what the LLM
identifies from the prompt. Set to NULL to restore automatic
detection.
Arguments
dataThe data object (e.g., a Seurat object, data frame, or any object compatible with scplotter visualization functions). Pass
NULLto clear the preset data and revert to automatic detection.nameOptional character string to use as the data object's name in generated code. If
NULL(default), the name is inferred from thedataargument viadeparse(substitute()).
SCPlotterChat$get_data()
Retrieve the currently set data object (if any).
Returns NULL if no data has been manually set via $set_data().
SCPlotterChat$ask()
Send a natural language prompt to the chat interface.
This is the primary method for interacting with SCPlotterChat.
It executes a three-stage pipeline:
Tool identification — The LLM selects the most appropriate scplotter visualization function based on the prompt and conversation history.
Data identification — The LLM identifies the target data object from available sources (unless data has been manually set via
$set_data()).Code generation and execution — The LLM generates R code to call the selected function with the identified data, which is then evaluated. The resulting plot is returned.
If the selected tool is ListTools or ListData, the
respective listing is printed instead of generating a plot.
Arguments
promptA character string containing the user's query or instruction in natural language (e.g., "Plot a UMAP of the pancreas data colored by cell type", "Make it a heatmap instead").
verboseLogical; if
TRUE, prints the full LLM prompt and response for this interaction. Default isNULL, which falls back to theverbosesetting of theSCPlotterChatinstance. Use this to debug a single interaction without enabling verbose mode globally.add_to_historyLogical; if
TRUE(default), this interaction is recorded in the conversation history, enabling the LLM to understand follow-up requests in context. Set toFALSEfor one-off queries that should not influence subsequent interactions.
Examples
# \donttest{
if (FALSE) {
# Setup LLM provider (requires an API key)
provider <- tidyprompt::llm_provider_openai(
parameters = list(model = "deepseek-v4-flash", stream = TRUE),
url = "https://api.deepseek.com/chat/completions",
api_key = Sys.getenv("OPENAI_API_KEY")
)
# Create chat instance
chat <- SCPlotterChat$new(provider)
# List available tools
chat$ask("What are the tools to use?")
# Tool identified: ListTools
# Available tools:
# - CCCPlot : Cell-Cell Communication Plot
# Visualizes ligand-receptor interaction inference results ...
# ...
# List available data
chat$ask("What data is available?")
# Generate a plot from natural language
chat$ask("Plot the default cell-cell communication plot for the cellphonedb_res dataset")
# Tool identified: CCCPlot
# Data object identified: cellphonedb_res
# Code ran: CCCPlot(cellphonedb_res)
# Refine with conversational context
chat$ask("do a heatmap instead")
# Tool identified: CCCPlot
# Data object identified: cellphonedb_res
# Code ran: CCCPlot(cellphonedb_res, plot_type = "heatmap")
# Add a title
chat$ask("Add a proper title to the plot")
# Manually set data to avoid auto-detection
chat$set_data(scplotter::cellphonedb_res)
chat$ask("Make a dot plot")
# Inspect conversation history
chat$get_history()
# Clear history for a fresh conversation
chat$clear_history()
}
# }
