
LLMR is an R package for running large language model workflows as ordinary R code. It provides one interface for hosted and local providers, with tidy helpers for repeated calls, structured JSON, XML-like tags, embeddings, chat sessions, retries, and parallel experiments.
install.packages("LLMR") # CRAN
# Development:
# remotes::install_github("asanaei/LLMR")library(LLMR)
cfg <- llm_config(
provider = "openai",
model = "gpt-4.1-nano",
temperature = 0.2,
max_tokens = 256
)Store keys in environment variables such as OPENAI_API_KEY,
ANTHROPIC_API_KEY, GEMINI_API_KEY. Reasoning models such as gpt-5-nano
often reject custom temperature values. Omit temperature
unless the selected model accepts it.
For Gemini on Vertex AI, keep provider = "gemini" and
opt in to Vertex routing:
cfg_vertex <- llm_config(
provider = "gemini",
model = "gemini-2.5-flash-lite",
vertex = TRUE,
project = "my-gcp-project",
location = "us-central1",
api_key = "VERTEX_ACCESS_TOKEN"
)r <- call_llm(
config = cfg,
messages = c(
system = "You are a branding expert.",
user = "Six-word catch-phrase for eco-friendly balloons."
)
)
print(r) # text + status line
as.character(r) # just the text
finish_reason(r)
tokens(r)
is_truncated(r)schema <- list(
type = "object",
properties = list(
label = list(type = "string"),
score = list(type = "number")
),
required = list("label", "score"),
additionalProperties = FALSE
)
cfg_s <- enable_structured_output(cfg, schema = schema)
resp <- call_llm(cfg_s, c(system = "Reply JSON only.", user = "Label and score for 'MNIST'."))
parsed <- llm_parse_structured(resp)
str(parsed)Or use higher-level helpers:
words <- c("excellent", "awful", "fine")
out <- llm_fn_structured(
x = words,
prompt = "Classify '{x}' and output {label, score in [0,1]} as JSON.",
.config = cfg,
.schema = schema,
.fields = c("label", "score")
)
outUse llm_fn() for vectors and llm_mutate()
inside data-frame pipelines. The shorthand form puts the output column
and prompt in one argument.
words <- c("excellent", "awful", "fine")
llm_fn(words, "Classify '{x}'.", .config = cfg, .return = "text")
df <- tibble::tibble(
city = c("Cairo", "Lima"),
text = c("Cairo is large.", "Lima is coastal.")
)
df |>
llm_mutate(
country = "Where is {city}? Answer with only the country.",
.config = cfg
)Strict JSON extraction can be used directly in
llm_mutate():
df |>
llm_mutate(
out = "{text}",
.config = cfg,
.structured = TRUE,
.schema = schema
)For lighter extraction, use XML-like tags:
df |>
llm_mutate(
geo = "Where is {city}? Give country and continent in their own tags.",
.config = cfg,
.system_prompt = paste(
"Use XML tags to specify different parts of the answer, but do not nest tags.",
"Return <country>...</country> and <continent>...</continent>."
),
.tags = c("country", "continent")
)sentences <- c(
one = "Quiet rivers mirror bright skies.",
two = "Thunder shakes the mountain path."
)
emb_cfg <- llm_config(
provider = "voyage",
model = "voyage-3.5-lite",
embedding = TRUE
)
emb <- call_llm(emb_cfg, sentences) |> parse_embeddings()
dim(emb)Batch embeddings:
emb <- get_batched_embeddings(
texts = sentences,
embed_config = emb_cfg,
batch_size = 8
)chat <- chat_session(cfg, system = "You teach statistics tersely.")
chat$send("Explain p-values in 12 words.")
chat$send("Now give a three-word analogy.")
print(chat)setup_llm_parallel(workers = 4)
experiments <- build_factorial_experiments(
configs = list(cfg),
user_prompts = c("Summarize in one sentence: The Apollo program."),
system_prompts = "Be concise."
)
res <- call_llm_par(experiments, progress = TRUE)
reset_llm_parallel()call_llm() or
call_llm_robust().llm_fn().llm_mutate()..structured = TRUE,
llm_fn_structured(), or
llm_mutate_structured()..tags or
llm_mutate_tags().llm_parse_structured_col()
or llm_parse_tags_col().build_factorial_experiments() plus
call_llm_par().llmr_response; use
as.character() when you want plain text.enable_structured_output() for
provider-specific request settings, then
llm_parse_structured() and
llm_validate_structured_col() for local parsing and
validation.call_llm() remain provider-native until
parse_embeddings() converts them to a numeric matrix.call_llm_robust() handles transient failures and rate
limits with backoff.Bug reports, feature requests, and support questions should be opened as GitHub issues. Please include a minimal reproducible example when relevant. Pull requests are welcome for focused improvements. See CONTRIBUTING.md for contribution, support, and reporting guidance. See COPYING for the full MIT license text.