chat_claude() is no longer deprecated and is an aliasforchat_anthropic(), reflecting Anthropic’s recentrebranding of developer tools under the Claude name (#758).models_claude() is now an alias formodels_anthropic().parallel_chat() andbatch_chat() are nolonger experimental.Chat$extract_data() ->chat$chat_structured() (0.2.0)Chat$extract_data_async() ->chat$chat_structured_async() (0.2.0)chat_anthropic(max_tokens) ->chat_anthropic(params) (0.2.0)chat_azure() ->chat_azure_openai()(0.2.0)chat_azure_openai(token) (0.1.1)chat_bedrock() ->chat_aws_bedrock()(0.2.0)chat_claude() ->chat_anthropic()(0.2.0)chat_cortex() ->chat_snowflake()(0.2.0)chat_gemini() ->chat_google_gemini()(0.2.0)chat_openai(seed) ->chat_openai(params) (0.2.0)create_tool_def(model) ->create_tool_def(chat) (0.2.0)batch_*() no longer hashes properties of the providerbesides thename,model, andbase_url. This should provide some protection fromaccidentally reusing the same.json file with differentproviders, while still allowing you to use the same batch file acrossellmer versions. It also has a newignore_hash argumentthat allows you to opt out of the check if you’re confident thedifference only arises because ellmer itself has changed.chat_claude() gains newcache parameter tocontrol caching. By default it is set to “5m”. This should (on average)reduce the cost of your chats (#584).chat_openai() now uses OpenAI’s responses endpoint(#365, #801). This is their recommended endpoint and gives more accessto built-in tools.chat_openai_compatible() replaceschat_openai() as the interface to use for OpenAI-compatibleAPIs, andchat_openai() is reserved for the official OpenAIAPI. Unlike previous versions ofchat_openai(), thebase_url parameter is now required (#801).chat_*() functions now use acredentialsfunction instead of anapi_key (#613). This means that APIkeys are never stored in the chat object (which might be saved to disk),but are instead retrieved on demand as needed. You generally shouldn’tneed to use thecredentials argument, but when you do, youshould use it to dynamically retrieve the API key from some other source(i.e. never inline a secret directly into a function call).claude_file_() functions for managing fileuploads with Claude (claude_tool_web_search() andclaude_tool_web_fetch() for Claude.google_tool_web_search() andgoogle_tool_web_fetch() for Gemini.openai_tool_web_search() for OpenAI. If you want to doweb fetch for other providers, you could usebtw::btw_tool_web_read_url().parallel_chat() and friends now have a more permissiveattitude to errors. By default, they will now return when hitting thefirst error (rather than erroring), and you can control this behaviourwith theon_error argument. Or if you interrupt the job, itwill finish up current requests and then return all the work done sofar. The main downside of this work is that the output ofparallel_chat() is more complex: it is now a mix ofChat objects, error objects, andNULL(#628).parallel_chat_structured() no longer errors if someresults fail to parse. Instead it warns, and the corresponding rows willbe filled in with the appropriate missing values (#628).schema_df() to describe the schema of a data frameto an LLM (#744).tool()s can now return image or PDF content types, withcontent_image_file() orcontent_image_pdf()(#735).params() gains newreasoning_effort andreasoning_tokens so you can control the amount of effort amodel spends on thinking. Initial support is provided forchat_claude(),chat_google_gemini(), andchat_openai() (#720).type_ignore() allows you to specify that a toolargument should not be provided by the LLM when the R function has asuitable default value (#764).AssistantTurns now have a@duration slot,containing the total time to complete the request (batch_chat() logs tokens once, on retrieval(#743).batch_chat() now retrieves failed results forchat_openai() (#830) and gracefully handles invalid JSON(#845).batch_chat() now works once more forchat_anthropic() (#835).batch_chat_*() andparallel_chat_*() nowaccept a string as the chat object, following the same rules aschat() (#677).chat_claude() andchat_aws_bedrock() nowdefault to Claude Sonnet 4.5 (#800).chat_databricks() lifts many of its restrictions nowthat Databricks’ API is more OpenAI compatible (#757).chat_google_gemini() andchat_openai()support image generation (#368).chat_google_gemini() has an experimental fallbackinteractive OAuth flow, if you’re in an interactive session and no otherauthentication options can be found (#680).chat_groq() now defaults to llama-3.1-8b-instant.chat_openai() gains aservice_tierargument (#712).chat_portkey() now requires you to supply a model(#786).chat_portkey(virtual_key) no longer needs to besupplied; instead Portkey recommends including the virtual key/providerin themodel (#786).Chat$chat(),Chat$stream(), and similarmethods now add empty tool results when a the chat is interrupted duringa tool call loop, allowing the conversation to be resumed withoutcausing an API error (#840).Chat$chat_structured() and friends now only warn ifmultiple JSON payloads found (instead of erroring) (Chat$get_tokens() gives a brief description of the turncontents to make it easier to see which turn tokens are spent on (#618)and also returns the cost (#824). It now returns one row for eachassistant turn, better representing the underlying data received fromLLM APIs. Similarly, theprint() method now reports costson each assistant turn, rather than trying to parse out individualcosts.interpolate_package() now provides an informative errorif the requested prompt file is not found in the package’sprompts/ directory (#763) and now works with in-developmentpackages loaded with devtools (#766).models_mistral() lists available models (models_ollama() was fixed to correctly query modelcapabilities from remote Ollama servers (#746).chat_ollama() now usescredentials whenchecking if Ollama is available andmodels_ollama() now hasacredentials argument. This is useful when accessingOllama servers that require authentication (parallel_chat_structured() now returns a tibble, sincethis does a better job of printing more complex data frames (#787).chat() is now compatible with mostchat_functions (#699).chat_aws_bedrock(),chat_databricks(),chat_deepseek(),chat_github(),chat_groq(),chat_ollama(),chat_openrouter(),chat_perplexity(), andchat_vllm() now support aparams argument thataccepts common model parameters fromparams().deployment_id argument inchat_azure_openai() was deprecated and replaced withmodel to better align with other providers.chat_openai() now correctly mapsmax_tokens andtop_k fromparams() to the OpenAI API parameters (#699).chat_anthropic() drops empty assistant turns toavoid API errors (#710).
chat_github() now uses thehttps://models.github.ai/inference endpoint andchat() supports GitHub models in the formatchat("github/openai/gpt-4.1") (#726).
chat_google_vertex() authentication was fixed usingbroader scope (#704,
chat_google_vertex() can now useglobalproject location (#704,
chat_openai() now usesOPENAI_BASE_URL,if set, for thebase_url. Similarly,chat_ollama() also usesOLLAMA_BASE_URL if set(#713).
contents_record() andcontents_replay()now record and replay custom classes that extend ellmer’sTurn orContent classes (#689).contents_replay() now also restores the tool definition inContentToolResult objects (in@request@tool)(#693).
chat_snowflake() now supports Privatelink accounts(#694,
models_google_vertex() works once again (#704,
In thevalue_turn() method for OpenAI providers,usage is checked ifNULL before logging tokensto avoid errors when streaming with some OpenAI-compatible services(#706,
Newchat() allows you to chat with any providerusing a string likechat("anthropic") orchat("openai/gpt-4.1-nano") (#361).
tool() has a simpler specification: you now specifythename,description, andarguments. I have done my best to deprecate old usage andgive clear errors, but I have likely missed a few edge cases. Iapologize for any pain that this causes, but I’m convinced that it isgoing to make tool usage easier and clearer in the long run. If you havemany calls to convert,?tool contains a prompt that willhelp you use an LLM to convert them (#603). It also now returns afunction so that you can call it (and/or export it from your package)(#602).
type_array() andtype_enum() now havethe description as the second argument anditems/values as the first. This makes themeasier to use in the common case where the description isn’t necessary(#610).
ellmer now retries requests up to 3 times, controllable withoption(ellmer_max_tries), and will retry if the connectionfails (rather than just if the request itself returns a transienterror). The default timeout, controlled byoption(ellmer_timeout_s), now applies to the initialconnection phase. Together, these changes should make it much morelikely for ellmer requests to succeed.
Newparallel_chat_text() andbatch_chat_text() make it easier to just get the textresponse from multiple prompts (#510).
ellmer’s cost estimates are considerably improved.chat_openai(),chat_google_gemini(), andchat_anthropic() capture the number of cached input tokens.This is primarily useful for OpenAI and Gemini since both offerautomatic caching, yielding improved cost estimates (#466). We also havea better source of pricing data, LiteLLM. This considerably expands thenumber of providers and models that include cost information(#659).
ellmer_echo option controls the default valueforecho.batch_chat_structured() provides clear messaging whenprompts/path/provider don’t match (#599).chat_aws_bedrock() allows you to set thebase_url() (#441).chat_aws_bedrock(),chat_google_gemini(),chat_ollama(), andchat_vllm() use a morerobust method to generate model URLs from thebase_url(#593,chat_cortex_analyst() is deprecated; please usechat_snowflake() instead (#640).chat_github() (and other OpenAI extensions) no longerwarn aboutseed (#574).chat_google_gemini() andchat_google_vertex() default to Gemini 2.5 flash(#576).chat_huggingface() works much better.chat_openai() supportscontent_pdf_()(#650).chat_portkey() works once again, and reads the virtualAPI key from thePORTKEY_VIRTUAL_KEY env var (#588).chat_snowflake() works with tool calling (#557,Chat$chat_structured() and friends no longerunnecessarily wraptype_object() forchat_openai() (#671).Chat$chat_structured() suppresses tool use. If you needto use tools and structured data together, first use$chat() for any needed tools, then$chat_structured() to extract the data you need.Chat$chat_structured() no longer requires a prompt(since it may be obvious from the context) (#570).Chat$register_tool() shows a message when you replacean existing tool (#625).contents_record() andcontents_replay()record and replayTurn related information from aChat instance (#502). These methods can be used forbookmarking within {shinychat}.models_github() lists models forchat_github() (#561).models_ollama() includes acapabilitiescolumn with a comma-separated list of model capabilities (#623).parallel_chat() and friends accept lists ofContent objects in theprompt (#597,tool() checks that thename is valid(#625).When you save aChat object to disk, API keys areThis means that you can no longer easily resume a chat you’ve saved ondisk (we’ll figure this out in a future release) but ensures that younever accidentally save your secret key in an RDS file (#534).
chat_anthropic() now defaults to Claude Sonnet 4,and I’ve added pricing information for the latest generation of Claudemodels.
chat_databricks() now picks up on Databricksworkspace URLs set in the configuration file, which should improvecompatibility with the Databricks CLI (#521,
chat_snowflake() no longer streams answers thatinclude a mysteriouslist(type = "text", text = "") trailer(#533,@atheriel).It now parses streaming outputs correctly into turns (#542), supportsstructured ouputs (#544), and standard model parameters (#545,
chat_snowflake() andchat_databricks()now default to Claude Sonnet 3.7, the same default aschat_anthropic() (#539 and #546,
type_from_schema() lets you to use pre-existing JSONschemas in structured chats (#133,
We have made a number of refinements to the way ellmer convertsJSON to R data structures. These are breaking changes, although we don’texpect them to affect much code in the wild. Most importantly, tools arenow invoked with their inputs coerced to standard R data structures(#461); opt-out by settingconvert = FALSE intool().
Additionally ellmer now convertsNULL toNAfortype_boolean(),type_integer(),type_number(), andtype_string() (#445), anddoes a better job with arrays whenrequired = FALSE(#384).
chat_ functions no longer have aturnargument. If you need to set the turns, you can now useChat$set_turns() (#427). Additionally,Chat$tokens() has been renamed toChat$get_tokens() and returns a data frame of tokens,correctly aligned to the individual turn. The print method now uses thisto show how many input/output tokens were used by each turn(#354).
Two new interfaces help you do multiple chats with a singlefunction call:
batch_chat() andbatch_chat_structured() allow you to submit multiple chatsto OpenAI and Anthropic’s batched interfaces. These only guarantee aresponse within 24 hours, but are 50% of the price of regular requests(#143).
parallel_chat() andparallel_chat_structured() work with any provider and allowyou to submit multiple chats in parallel (#143). This doesn’t give youany cost savings, but it’s can be much, much faster.
This new family of functions is experimental because I’m not 100%sure that the shape of the user interface is correct, particularly as itpertains to handling errors.
google_upload() lets you upload files to GoogleGemini or Vertex AI (#310). This allows you to work with videos, PDFs,and other large files with Gemini.
models_google_gemini(),models_anthropic(),models_openai(),models_aws_bedrock(),models_ollama() andmodels_vllm(), list available models for Google Gemini,Anthropic, OpenAI, AWS Bedrock, Ollama, and VLLM respectively. Differentproviders return different metadata so they are only guaranteed toreturn a data frame with at least anid column (#296).Where possible (currently for Gemini, Anthropic, and OpenAI) we includeknown token prices (per million tokens).
interpolate() and friends are now vectorised so youcan generate multiple prompts for (e.g.) a data frame of inputs. Theyalso now return a specially classed object with a custom print method(#445). Newinterpolate_package() makes it easier tointerpolate from prompts stored in theinst/promptsdirectory inside a package (#164).
chat_anthropic(),chat_azure(),chat_openai(), andchat_gemini() now take aparams argument, that coupled with theparams() helper, makes it easy to specify common modelparameters (likeseed andtemperature) acrossproviders. Support for other providers will grow as you request it(#280).
ellmer now tracks the cost of input and output tokens. The costis displayed when you print aChat object, intokens_usage(), and withChat$get_cost(). Youcan also request costs inparallel_chat_structured(). We doour best to accurately compute the cost, but you should treat it as anestimate rather than the exact price. Unfortunately LLM providerscurrently make it very difficult to figure out exactly how much yourqueries cost (#203).
We have support for three new providers:
chat_huggingface() for models hosted athttps://huggingface.co(#359,chat_mistral() for models hosted athttps://mistral.ai(#319).chat_portkey() andmodels_portkey() formodels hosted athttps://portkey.ai (#363,We also renamed (with deprecation) a few functions to make thenaming scheme more consistent (#382,
chat_azure_openai() replaceschat_azure().chat_aws_bedrock() replaceschat_bedrock().chat_anthropic() replaceschat_anthropic().chat_google_gemini() replaceschat_gemini().We have updated the default model for a couple of providers:
chat_anthropic() uses Sonnet 3.7 (which it also nowdisplays) (#336).chat_openai() uses GPT-4.1 (#512)NewChat$get_provider() lets you access theunderlying provider object (#202).
Chat$chat_async() andChat$stream_async() gain atool_mode argumentto decide between"sequential" and"concurrent" tool calling. This is an advanced feature thatprimarily affects asynchronous tools (#488,
Chat$stream() andChat$stream_async()gain support for streaming the additional content types generated duringa tool call with a newstream argument. Whenstream = "content" is set, the streaming response yieldsContent objects, including theContentToolRequest andContentToolResultobjects used to request and return tool calls (#400,
NewChat$on_tool_request() and$on_tool_result() methods allow you to register callbacksto run on a tool request or tool result. These callbacks can be used toimplement custom logging or other actions when tools are called, withoutmodifying the tool function (#493,
Chat$chat(echo = "output") replaces thenow-deprecatedecho = "text" option. When usingecho = "output", additional output, such as tool requestsand results, are shown as they occur. Whenecho = "none",tool call failures are emitted as warnings (#366,
ContentToolResult objects can now be returneddirectly from thetool() function and now includesadditional information (#398 #399,
extra: A list of additional data associated with thetool result that is not shown to the chatbot.request: TheContentToolRequest thattriggered the tool call.ContentToolResult no longer has anid property, instead the tool call ID can be retrieved fromrequest@id.They also include the error condition in theerrorproperty when a tool call fails (#421,
ContentToolRequest gains atoolproperty that includes thetool() definition when a requestis matched to a tool by ellmer (#423,
tool() gains an.annotations argumentthat can be created with thetool_annotations() helper.Tool annotations are described in theModel ContextProtocol and can be used to describe the tool to clients. (#402,
Newtool_reject() function can be used to reject atool request with an explanation for the rejection reason.tool_reject() can be called within a tool function or in aChat$on_tool_request() callback. In the latter case,rejecting a tool call will ensure that the tool function is notevaluated (#490, #493,
All requests now set a custom User-Agent that identifies that therequests come from ellmer (#341). The default timeout has been increasedto 5 minutes (#451, #321).
chat_anthropic() now supports the thinking contenttype (#396), andcontent_image_url() (#347). It gains abeta_header argument to opt-in to beta features (#339). It(along withchat_bedrock()) no longer chokes afterreceiving an output that consists only of whitespace (#376). Finally,chat_anthropic(max_tokens =) is now deprecated in favour ofchat_anthropic(params = ) (#280).
chat_google_gemini() andchat_google_vertex() gain more ways to authenticate. Theycan useGEMINI_API_KEY if set (
chat_ollama() now works withtool()definitions with optional arguments or empty properties (#342, #348,@gadenbuie), andnow acceptsapi_key and consults theOLLAMA_API_KEY environment variable. This is not needed forlocal usage, but enables bearer-token authentication when Ollama isrunning behind a reverse proxy (#501,
chat_openai(seed =) is now deprecated in favour ofchat_openai(params = ) (#280).
create_tool_def() can now use any Chat instance(#118,
live_browser() now requires {shinychat} v0.2.0 orlater which provides access to the app that powerslive_browser() viashinychat::chat_app(), aswell as a Shiny module for easily including a chat interface for anellmerChat object in your Shiny apps (#397,
Provider gainsname andmodel fields (#406). These are now reported when you printa chat object and are used intoken_usage().
option(ellmer_verbosity) is no longer supported;instead use the standard httr2 verbosity functions, such ashttr2::with_verbosity(); these now support streamingdata.
chat_cortex() has been renamedchat_cortex_analyst() to better disambiguate it fromchat_snowflake() (whichalso uses “Cortex”) (#275,@atheriel).
All providers now wait for up to 60s to get the completeresponse. You can increase this with, e.g.,option(ellmer_timeout_s = 120) (#213, #300).
chat_azure(),chat_databricks(),chat_snowflake(), andchat_cortex_analyst()now detect viewer-based credentials when running on Posit Connect (#285,@atheriel).
chat_deepseek() provides support for DeepSeek models(#242).
chat_openrouter() provides support for models hostedby OpenRouter (#212).
chat_snowflake() allows chatting with models hostedthrough Snowflake’sCortexLLM REST API (#258,
content_pdf_file() andcontent_pdf_url() allow you to upload PDFs to supportedmodels. Models that currently support PDFs are Google Gemini and ClaudeAnthropic. With help from
Chat$get_model() returns the model name(#299).
chat_azure() has greatly improved support for AzureEntra ID. API keys are now optional and we can pick up on ambientcredentials from Azure service principals or attempt to use interactiveEntra ID authentication when possible. The broken-by-designtoken argument has been deprecated (it could not handlerefreshing tokens properly), but a newcredentials argumentcan be used for custom Entra ID support when needed instead (forinstance, if you’re trying to use tokens generated by theAzureAuth package) (#248, #263, #273, #257,
chat_azure() now reports better error messages whenthe underlying HTTP requests fail (#269,api_version = "2024-10-21" which includes data forstructured data extraction (#271).
chat_bedrock() now handles temporary IAM credentialsbetter (#261,chat_bedrock()gainsapi_args argument (
chat_databricks() now handles theDATABRICKS_HOST environment variable correctly whether itincludes an HTTPS prefix or not (#252,SPARK_CONNECT_USER_AGENT environment variable when makingrequests (#254,
chat_gemini() now defaults to using thegemini-2.0-flash model.
print(Chat) no longer wraps long lines, making iteasier to read code and bulleted lists (#246).
Newchat_vllm() to chat with models served by vLLM(#140).
The defaultchat_openai() model is nowGPT-4o.
NewChat$set_turns() to set turns.Chat$turns() is nowChat$get_turns().Chat$system_prompt() is replaced withChat$set_system_prompt() andChat$get_system_prompt().
Async and streaming async chat are now event-driven and uselater::later_fd() to wait efficiently on curl socketactivity (#157).
Newchat_bedrock() to chat with AWS bedrock models(#50).
Newchat$extract_data() uses the structured data APIwhere available (and tool calling otherwise) to extract data structuredaccording to a known type specification. You can create specs withfunctionstype_boolean(),type_integer(),type_number(),type_string(),type_enum(),type_array(), andtype_object() (#31).
The generalToolArg() has been replaced by the morespecifictype_*() functions.ToolDef() hasbeen renamed totool.
content_image_url() will now create inline imageswhen given a data url (#110).
Streaming ollama results works once again (#117).
Streaming OpenAI results now capture more results, includinglogprobs (#115).
Newinterpolate() andprompt_file()make it easier to create prompts that are a mix of static text anddynamic values.
You can find how many tokens you’ve used in the current sessionby callingtoken_usage().
chat_browser() andchat_console() arenowlive_browser() andlive_console().
Theecho can now be one of three values: “none”,“text”, or “all”. If “all”, you’ll now see both user and assistantturns, and all content types will be printed, not just text. Whenrunning in the global environment,echo defaults to “text”,and when running inside a function it defaults to “none”.
You can now log low-level JSON request/response info by settingoptions(ellmer_verbosity = 2).
chat$register_tool() now takes an object created byTool(). This makes it a little easier to reuse tooldefinitions (#32).
new_chat_openai() is nowchat_openai().
Claude and Gemini are now supported viachat_claude() andchat_gemini().
The Snowflake Cortex Analyst is now supported viachat_cortex() (#56).
Databricks is now supported viachat_databricks()(#152).