Use ellmer types to ask for a list or data frame with specified
fields. Deputy stores the converted R value in
result$structured_output so you can use it in the next step
of your analysis.
Extract directly
Use chat_structured() when the conversation already
contains the information to extract. It requests structured output
without calling ordinary tools, as ellmer does.
chat_structured_async() returns a promise with the same
value. Automatic compaction and optional validation corrections can add
model requests; all count toward the run’s usage limits.
agent <- Agent$new(ellmer::chat("openai/gpt-5.6-luna"))
review_type <- ellmer::type_object(
status = ellmer::type_enum(c("ok", "needs_review")),
findings = ellmer::type_array(ellmer::type_string())
)
data <- agent$chat_structured("The review found no problems.", type = review_type)
agent$last_run()$usageExisting JSON Schema can enter through
ellmer::type_from_schema(text = ...). ellmer handles schema
support and conversion to R values.
status_type <- ellmer::type_from_schema(
'{"type":"object","properties":{"status":{"type":"string"}},"required":["status"],"additionalProperties":false}'
)Complete a task, then extract
run_sync(type = ...),
run_async(type = ...), and the semantic
run(type = ...) stream first complete an ordinary
tool-using task, then ask ellmer to extract from that conversation. Both
phases share one run ID, hook lifecycle, permission policy, and
request/token/cost budget. The extraction never repeats tool work. The
response is the task’s text; structured_output
is the extracted value. Even a task without tools uses these two
explicit phases; use chat_structured() to extract directly
without the initial task phase.
agent <- Agent$new(
ellmer::chat("openai/gpt-5.6-luna"),
tools = tools_file(),
permissions = permissions_readonly(),
usage_limits = UsageLimits(max_requests = 8)
)
result <- agent$run_sync("Review the README", type = status_type)
if (!result_is_success(result)) {
cli::cli_abort("Review stopped early: {result$stop_reason}.")
}
result$structured_outputBounded application validation
A validate function receives ellmer’s converted value
and returns TRUE, FALSE, or non-empty text
explaining a failed application rule. Set max_corrections
to a finite non-negative integer to permit more structured requests. The
default is zero. Corrections have no tools and consume the remaining run
budget. They retain failed values, available turns, feedback, and
conditions in structured_attempt events. These local events
can contain user data; Deputy’s tracing adapter omits that content.
data <- agent$chat_structured(
"Extract the review status.",
type = status_type,
validate = function(x) {
if (identical(x$status, "ok")) TRUE else "status must equal ok"
},
max_corrections = 1L
)Corrections cover failed application validation and JSON parsing
failures confirmed through ellmer’s recorded ContentJson.
Unclassified conversion errors and application callback errors are
terminal, preserving their original conditions. An exhausted correction
policy signals deputy_structured_output_invalid. An
exception or NA from the validator is terminal. Provider
failures are not application corrections. Incomplete responses rejected
by ellmer before a turn can be recorded are terminal; the caller must
change the token/context policy. Cancellation is cooperative: an
in-flight structured request can finish, but no correction starts after
cancellation. With on_exceed = "stop", a budget stop
returns a result with a stop reason and no successful structured value;
with on_exceed = "error", it signals the typed limit
condition. Inspect last_run() for completed-run evidence
even after an error.
Native structured streaming
stream(type = ...) and
stream_async(type = ...) forward to ellmer’s native
structured streaming API. Chunks contain raw JSON text/content; the
completed upstream assistant turn stores ContentJson. These
methods do not add an application correction loop. Providers requiring
ellmer’s schema-tool fallback must use chat_structured()
instead.
The former output_format argument and its
parsed/valid wrapper were removed before
Deputy’s first CRAN release. Update callers to ellmer types and consume
structured_output directly.