Adds one or more columns to .data that are produced by a Large-Language-Model.
Usage
llm_mutate(
.data,
output,
prompt = NULL,
.messages = NULL,
.config,
.system_prompt = NULL,
.before = NULL,
.after = NULL,
.return = c("columns", "text", "object"),
.na_action = c("send", "skip", "error"),
.structured = FALSE,
.schema = NULL,
.fields = NULL,
.tags = NULL,
.rows_per_prompt = 1L,
.rowpack_payload = c("user", "system"),
.rowpack_recovery = c("halve_recursive", "halve_once", "singletons", "retry_same",
"none"),
...
)Arguments
- .data
A data.frame / tibble.
- output
Unquoted name that becomes the new column (generative) or the prefix for embedding columns. In shorthand form, omit this argument and pass
newcol = "<glue prompt>"ornewcol = c(system = "...", user = "...")through....- prompt
Optional glue template string for a single user turn; reference any columns in
.data(e.g."{id}. {question}\nContext: {context}"). Ignored if.messagesis supplied.- .messages
Optional named character vector of glue templates to build a multi-turn message, using roles in
c("system","user","assistant","file"). Values are glue templates evaluated per-row; all can reference multiple columns. For multimodal, use role"file"with a column containing a path template.- .config
An llm_config object (generative or embedding).
- .system_prompt
Optional system message sent with every request when
.messagesdoes not include asystementry.- .before, .after
Standard dplyr::relocate helpers controlling where the generated column(s) are placed.
- .return
One of
c("columns","text","object"). For generative mode, controls how results are added."columns"(default) adds text plus diagnostic columns;"text"adds a single text column;"object"adds a list-column ofllmr_responseobjects named<output>_obj. Ignored (with a warning) when.structured = TRUEor.tagsis supplied, which always return parsed columns.- .na_action
What to do with rows whose template references an
NAvalue."send"(default) rendersNAas an empty string and sends the prompt anyway;"skip"does not call the API for those rows (the output column isNAandfinish_reasonis"skipped");"error"stops before any call is made. With.structuredor.tags, only"send"and"error"are available.- .structured
Logical. If
TRUE, enables structured JSON output with automatic parsing. When enabled, this is equivalent to callingllm_mutate_structured(). Default isFALSE.- .schema
Optional JSON Schema (R list). When
.structured = TRUE, this schema is sent to the provider for validation and used for local parsing. WhenNULL, only JSON mode is enabled (no strict schema validation).- .fields
Optional character vector of fields to extract from parsed JSON or tag output. In JSON mode, supports nested paths (e.g.,
"user.name"or"/data/items/0"). WhenNULLand.schemais provided, auto-extracts all top-level schema properties. In tag mode,NULLextracts all.tags. Set toFALSEto skip field extraction entirely.Optional character vector of XML-like tag names to request and parse, such as
c("age", "job"). When supplied,llm_mutate()delegates tollm_mutate_tags()and addstags_ok,tags_data, and one column per tag unless.fields = FALSE.- .rows_per_prompt
Integer scalar, or
Inf. Number of rows packed into a single generative request. The default,1, sends one request per row (the historical behaviour). When greater than1, rows are grouped and sent in one call wrapped in numbered<row_1>...</row_1>tags (see Row batching below);Infsends all rows at once. Works in generative, tag, and structured modes; not applicable to embedding configurations.- .rowpack_payload
One of
c("user","system"). Channel to which the<row_i>data block is appended when batching. The default"user"keeps a static system prompt cacheable; the imperative instruction is always placed in the system message.- .rowpack_recovery
How to handle rows a batched call leaves unresolved. One of
"halve_recursive"(default),"halve_once","singletons","retry_same", or"none"; seellm_fn()for the precise meaning of each.- ...
Passed to the underlying calls:
call_llm_broadcast()in generative mode,get_batched_embeddings()in embedding mode.
Details
Multi-column injection: templating is NA-safe (
NA-> empty string).Multi-turn templating: supply
.messages = c(system=..., user=..., file=...). Duplicate role names are allowed (e.g., twouserturns).Generative mode: one request per row via
call_llm_broadcast().Parallelism: calls
call_llm_broadcast(), which usescall_llm_robust()under the hood. If no future plan is active, workers are auto-configured; callsetup_llm_parallel()to set worker count explicitly.Embedding mode: the per-row text is embedded via
get_batched_embeddings(). Result expands to numeric columns namedpaste0(<output>, 1:N). If all rows fail to embed, a single<output>1column ofNAis returned.Diagnostic columns use suffixes:
_finish,_sent,_rec,_tot,_reason,_ok,_err,_id,_status,_ecode,_param,_t.Row packing: with
.rows_per_prompt > 1, three further columns are added (_rowpack,_rpn,_rpi: the row-pack identifier, the size of the resolving call, and the within-call position). They appear only when packing actually groups rows, so the default schema is unchanged at.rows_per_prompt = 1.
Row batching
With .rows_per_prompt > 1, several rows travel in one generative request. LLMR
wraps each row's prompt in a numbered tag, <row_1>...</row_1>,
<row_2>...</row_2>, and so on, appends that block to the message (see
.rowpack_payload), and instructs the model to answer each item inside a
matching numbered tag; the reply is split back into rows by those numbers.
This also composes with .tags (each <row_i> then wraps the requested field
tags) and with .structured = TRUE (rows are returned as one JSON object
{"results":[{"row":i, ...}]}, de-multiplexed by the integer row field; a
one-time warning notes that this relies on the model honouring the protocol
and that strict provider-side schema validation is replaced by local parsing).
Batching is most useful with capable models at temperature = 0 and is a net
loss when the model ignores the wrapping. Dropped, reordered, duplicated, or
truncated rows are detected and re-issued per .rowpack_recovery; token counts
are reported once per batch so that summing token columns stays correct.
Shorthand
You can supply the output column and prompt in one argument:
df |> llm_mutate(answer = "{question} (hint: {hint})", .config = cfg)
df |> llm_mutate(answer = c(system = "One word.", user = "{question}"), .config = cfg)
df |> llm_mutate(country = "Where is {city}? Answer with only the country.", .config = cfg)This is equivalent to:
df |> llm_mutate(answer, prompt = "{question} (hint: {hint})", .config = cfg)
df |> llm_mutate(answer, .messages = c(system = "One word.", user = "{question}"), .config = cfg)Structured modes
.structured = TRUEdelegates tollm_mutate_structured()for JSON..tagsdelegates tollm_mutate_tags()for XML-like tags. If both are supplied,.structuredtakes precedence.
Examples
if (FALSE) { # \dontrun{
library(dplyr)
df <- tibble::tibble(
id = 1:2,
question = c("Capital of France?", "Author of 1984?"),
hint = c("European city", "English novelist")
)
cfg <- llm_config("openai", "gpt-4.1-nano",
temperature = 0)
# Generative: single-turn with multi-column injection
df |>
llm_mutate(
answer,
prompt = "{question} (hint: {hint})",
.config = cfg,
.system_prompt = "Respond in one word."
)
# Generative: multi-turn via .messages (system + user)
df |>
llm_mutate(
advice,
.messages = c(
system = "You are a helpful zoologist. Keep answers short.",
user = "What is a key fact about this? {question} (hint: {hint})"
),
.config = cfg
)
# Multimodal: include an image path with role 'file'
pics <- tibble::tibble(
img = c("path/to/cat.png", "path/to/dog.jpg"),
prompt = c("Describe the image.", "Describe the image.")
)
pics |>
llm_mutate(
vision_desc,
.messages = c(user = "{prompt}", file = "{img}"),
.config = llm_config("openai","gpt-4.1-mini")
)
# Embeddings: output name becomes the prefix of embedding columns
emb_cfg <- llm_config("voyage", "voyage-3.5-lite",
embedding = TRUE)
df |>
llm_mutate(
vec,
prompt = "{question}",
.config = emb_cfg,
.after = id
)
# Structured output: using .structured = TRUE (equivalent to llm_mutate_structured)
schema <- list(
type = "object",
properties = list(
answer = list(type = "string"),
confidence = list(type = "number")
),
required = list("answer", "confidence")
)
df |>
llm_mutate(
result,
prompt = "{question}",
.config = cfg,
.structured = TRUE,
.schema = schema
)
# Structured with shorthand
df |>
llm_mutate(
result = "{question}",
.config = cfg,
.structured = TRUE,
.schema = schema
)
# Soft structured output with XML-like tags
df |>
llm_mutate(
result = "Extract the person's age and job from: {question}",
.config = cfg,
.tags = c("age", "job")
)
cities <- tibble::tibble(city = c("Cairo", "Lima"))
cities |>
llm_mutate(
geo = "Where is {city}? Give country and continent in their own tags.",
.config = cfg,
.system_prompt = paste(
"Use XML tags for different parts of the answer, but do not nest tags.",
"Return <country>...</country> and <continent>...</continent>."
),
.tags = c("country", "continent")
)
} # }