Apply an LLM prompt over vectors/data frames
Usage
llm_fn(
x,
prompt,
.config,
.system_prompt = NULL,
...,
.tags = NULL,
.fields = NULL,
.return = c("text", "columns", "object"),
.na_action = c("send", "skip", "error"),
.rows_per_prompt = 1L,
.rowpack_payload = c("user", "system"),
.rowpack_recovery = c("halve_recursive", "halve_once", "singletons", "retry_same",
"none")
)Arguments
- x
A character vector or a data.frame/tibble.
- prompt
A glue template string. With a data-frame you may reference columns (
{col}); with a vector the placeholder is{x}.- .config
An llm_config object.
- .system_prompt
Optional system message (character scalar).
- ...
Passed unchanged to
call_llm_broadcast()(e.g.tries,progress,verbose).Optional character vector of XML-like tag names to request and parse. When supplied, delegates to
llm_fn_tags()for tag-based extraction.- .fields
Optional field selector for tag extraction (see
llm_fn_tags()).- .return
One of
c("text","columns","object")."columns"returns a tibble of diagnostic columns;"text"returns a character vector;"object"returns a list ofllmr_response(orNAon failure).- .na_action
What to do with elements whose template references an
NAvalue."send"(default) rendersNAas an empty string and sends the prompt anyway;"skip"does not call the API for those elements and returnsNAfor them (finish_reason = "skipped"in column mode);"error"stops before any call is made."skip"/"error"are not available together with.tags.- .rows_per_prompt
Integer scalar, or
Inf. Number of input elements packed into a single generative request. The default,1, sends one request per element (the historical behaviour). When greater than1, elements are grouped and transmitted in one call wrapped in numbered<row_1>...</row_1>tags (see Row batching below);Infsends all elements in a single call. Ignored for embedding configurations, which useget_batched_embeddings()instead.- .rowpack_payload
One of
c("user","system"). Channel to which the<row_i>data block is appended when batching. The imperative instruction is always placed in the system message; this argument controls only where the data goes. The default,"user", keeps a static system prompt cacheable.- .rowpack_recovery
How to handle rows that a batched call leaves unresolved (dropped, malformed, or truncated). One of:
"halve_recursive"(default) re-issue the unresolved rows at half the batch size, recursing down to single rows.
"halve_once"re-issue at half the batch size exactly once, then give up on any still-unresolved rows.
"singletons"re-issue each unresolved row on its own.
"retry_same"re-issue the failed batch once at the same size.
"none"do not recover; unresolved rows are returned as
NA.
Recovery is bounded by an internal call budget so it always terminates.
Value
For generative mode:
.return = "text": character vector.return = "columns": tibble with diagnostics.return = "object": list ofllmr_response(orNAon failure; unavailable when.rows_per_prompt > 1) For embedding mode, always a numeric matrix.
Batching, chunking, and row packing
"Batch" appears in three distinct senses in LLMR, and they are easy to keep
apart once you note which path each belongs to. (1) The asynchronous provider
Batch API (llm_batch_submit() and friends) defers a whole job for later
delivery at a reduced price; it is the only deferred path here. (2) In the
embedding path, get_batched_embeddings()'s batch_size sets how many texts
go in one synchronous embedding request, bounded by the provider's per-call
limit. (3) In the generative path, the .rows_per_prompt argument here packs
several data rows into one generative prompt and parses them back into rows.
Senses (2) and (3) are synchronous: batch_size and .rows_per_prompt
control how work is grouped into requests, not the per-token rate, so the
synchronous helpers do not themselves apply a provider discount. Pricing is a
separate axis: several providers bill batched embeddings at a reduced rate
through a dedicated async/batch tier (the embedding analogue of (1)), so
embeddings are not categorically full-price; that discount is a property of
the endpoint, not of batch_size. Row packing (3) can still lower total
tokens by amortizing shared prompt overhead across rows, again without
changing the rate.
Row packing
With .rows_per_prompt > 1, several input elements travel in one generative
request: LLMR wraps each element'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 the
original elements by those numbers. Batching trades a smaller number of
(larger) requests for some dependence on the model following the protocol; it
is most useful with capable models at temperature = 0, and it is a net loss
when the model ignores the wrapping. Results are deterministic given the
model's outputs: partitioning and parsing add no randomness. Rows the model
drops, reorders, duplicates, or truncates are detected and re-issued
according to .rowpack_recovery. Because a batch shares one underlying call,
token counts are reported once per batch (on its first resolved row, NA
elsewhere), as is the wall-clock duration, so that summing those columns is
correct. When a batch reply is entirely unusable and its rows succeed only
through recovery calls, the failed call's spend has no successful row to
land on, so sums can slightly undercount in heavy-recovery runs.
Examples
if (FALSE) { # \dontrun{
words <- c("excellent", "awful")
cfg <- llm_config("groq", "openai/gpt-oss-20b", temperature = 0)
llm_fn(words, "Classify '{x}' as Positive/Negative.", cfg, .return = "text")
df <- tibble::tibble(text = words, source = c("review", "review"))
llm_fn(df, "Classify '{text}' from {source}.", cfg, .return = "columns")
} # }