Reusable 'Shiny' user interface and server components from which the graphical applications in the 'LLMR' package family are assembled.

LLMR.shiny provides reusable Shiny user interface and server components for authors of LLMR family applications. Research users normally encounter it as a dependency of LLMRpanel, LLMRcontent, or FocusGroup. The package does not implement their research workflows.
shell_sidebar() supplies provider, model, execution-mode, credential, and
usage controls. shell_context() connects those controls to shared reactive
values and server helpers, and each workflow module receives the same context.
The app below runs offline. Its module echoes the texts entered, records the
planned calls before a run and the result rows after it, and shows a card in
place of a result when the input is empty or the mode is Live.
library(shiny)
library(LLMR.shiny)
echo_ui <- function(id) {
ns <- NS(id)
tagList(
textAreaInput(ns("texts"), "One text per line", "first text\nsecond text"),
actionButton(ns("run"), "Run"),
uiOutput(ns("status")),
tableOutput(ns("result"))
)
}
echo_server <- function(id, shared) {
moduleServer(id, function(input, output, session) {
requests <- reactive({
lines <- trimws(unlist(strsplit(as.character(input$texts), "\n", fixed = TRUE)))
data.frame(text = lines[nzchar(lines)])
})
observeEvent(requests(), shared$set_plan(nrow(requests()), "Echo run"))
attempt <- reactiveVal(NULL)
observeEvent(input$run, {
attempt(safe_llmr_call({
if (!identical(shared$mode(), "demo")) {
stop("This example makes no live calls; switch the mode to Demo.")
}
if (nrow(requests()) == 0L) stop("Enter at least one text.")
result <- build_runner("demo")(requests())
shared$add_usage(extract_token_counts(result))
result
}))
})
output$status <- renderUI({
if (!shared$can_run()) return(live_run_blocker_ui(shared$key()))
req(attempt())
if (attempt()$ok) demo_banner_ui() else attempt()$ui
})
output$result <- renderTable({
req(attempt()$ok)
as_display_table(attempt()$value[c("text", "response_text")])
})
})
}
ui <- bslib::page_sidebar(
title = "Shared context",
sidebar = shell_sidebar(),
echo_ui("echo")
)
server <- function(input, output, session) {
shared <- shell_context(input, output, session)
echo_server("echo", shared)
}
shinyApp(ui, server)
The context gives a module provider(), model(), mode(), the generation
settings, key(), can_run(), set_plan(), and add_usage(). can_run()
is TRUE in demonstration mode or when the selected provider's key is set. It
does not examine a workflow's data or instrument, so each module validates its
own input, as echo_server() does for empty input.
provider_registry() defines the provider choices and optional model defaults.
Model fields start blank unless local defaults are supplied:
options(LLMR.shiny.default_models = c(groq = "your-current-model"))
provider_registry()
key_state() reports whether a provider key is present and names the
environment variable in use. It does not return the key value. Applications
can display that status with key_state_tile() or explain a blocked live run
with live_run_blocker_ui(). build_llm_config() constructs the configuration
used by live execution and requires the optional LLMR package.
read_csv_upload() reads a Shiny fileInput() value. By default it infers
each column's type, as read.csv() does, so the identifiers 001 and 01
both become 1 and the text "NA" becomes missing. Reading every column as
written keeps identifiers and text intact. Column mapping then validates the
selected names before creating the text and optional labels columns
expected by an application workflow.
uploaded <- read_csv_upload(input$file, col_classes = "character",
na_strings = character(0))
validate_column_mapping(uploaded, text_col = input$text_col,
label_col = input$label_col)
mapped <- map_columns(uploaded, text_col = input$text_col,
label_col = input$label_col)
Columns that should be numbers or logical values can be converted afterwards
with utils::type.convert(x, as.is = TRUE, na.strings = "NA"), which gives
the types of the default reading. read_csv_path() reads a file path with
the same options.
The persona selector is a reusable Shiny module. Its server returns the row indices selected from a supplied persona data frame.
# UI
persona_selector_ui("personas")
# server
selected_rows <- persona_selector_server(
"personas",
data = shiny::reactive(personas)
)
The module uses LLMR::llm_persona_overview() when the data support it and
LLMR is installed. Otherwise, it displays the first columns. The optional DT
package supplies the selectable table.
report_text() and diagnostics_table() display results returned by
LLMR::report() and LLMR::diagnostics(). as_display_table() prepares data
frames, matrices, or a selected list component for tabular display.
safe_llmr_call() evaluates an expression and returns a status list. On
success, the value is in value. On error, the caught condition is in error
and a Shiny error card is in ui. The exported condition_category()
identifies authentication, rate-limit, parameter, and server conditions when
that information is available.
attempt <- safe_llmr_call(
build_llm_config(shared$provider(), shared$model()),
provider = shared$provider()
)
if (!attempt$ok) {
output$run_error <- shiny::renderUI(attempt$ui)
category <- condition_category(attempt$error)
}
The usage_*() helpers keep planned calls, realized calls or result rows, and
token counts in a session record. extract_token_counts() reads available
counts from result objects before usage_add() updates the record.
response_rows <- data.frame(
response_id = c("r1", "r2"),
sent_tokens = c(20L, 25L),
rec_tokens = c(4L, 6L)
)
usage <- usage_set_plan(usage_empty(), calls = 120, label = "Panel run")
usage <- usage_add(usage, extract_token_counts(response_rows))
usage_tile(usage)
Applications built with shell_context() can use set_plan() and
add_usage() to update the same record.
build_runner("live") returns a function that passes a request data frame to
LLMR::call_llm_par(). build_runner("demo") returns an offline response
function for examples and interface demonstrations. Demonstration results are
marked by annotate_demo_result() and can be checked with is_demo_result();
demo_notice() and demo_banner_ui() label them in application output.
respond <- build_runner("demo")
demo_result <- respond(data.frame(text = c("first", "second")))
is_demo_result(demo_result)
demo_runner() also accepts a response function when an application needs
fixed example output.
Application packages install LLMR.shiny as a dependency. GUI authors can install it directly from CRAN:
install.packages("LLMR.shiny")
or the development version:
remotes::install_github("asanaei/LLMR.shiny")
LLMR is optional for package installation. Live execution and live configuration construction require it:
install.packages("LLMR")
LLMR.shiny provides the shared Shiny components used by the graphical interfaces in the LLMR family. LLMR is the common provider interface on CRAN. LLMRcontent codes text with codebooks and builds replication archives. LLMRpanel administers survey and experimental instruments to panels of model personas. FocusGroup runs moderated group discussions. LLMRagent provides tools for agent experiments. The ecosystem page describes the package boundaries.
Report bugs and feature requests in the GitHub repository. Pull requests may be submitted there.
This project uses the MIT License; see LICENSE.