github crmne/ruby_llm v2.2.0
RubyLLM 2.2.0

38 minutes ago

RubyLLM 2.2.0 lets a model run the code it writes in a sandbox on your own servers, teaches it your workflows with skills, keeps files and long tool results out of the conversation, and makes persisted chats safe to run from several processes at once. Four more providers join, bringing the built-in total to twenty-three.

bundle add ruby_llm --version 2.2.0

Upgrading from 2.1? Require 2.2 in your Gemfile, run bin/rails generate ruby_llm:upgrade, and run the migration before deploying. 2.2 removes and renames some names, listed under Changed in 2.2, and the upgrade guide gives the exact replacement for each. Applications on 2.0 upgrade to 2.1 first. See What's New in 2.2 for examples.

New

  • Let a model run code on your own servers. with_code_execution gives the model one tool that runs Ruby, Python, JavaScript, or shell in a sandbox built on the mxc gem, using Bubblewrap on Linux and Seatbelt on macOS. It needs mxc 0.4.1 or later, so run bundle update mxc if you already use it. Files the code writes come back as attachments, and runs emit an event, write a usage row, and report their progress. By @crmne, @stephankaag, and @yorzi. (93f582e0, 5209fa51, #1227, #1233, #1285, #1487, #1552, #1596)
  • Widen a sandbox one call at a time. escalate_to: lets the model ask for a named sandbox for a single call, with a reason. The call waits for approval like any other, and nothing is remembered for the next one. By @crmne. (797814b4)
  • Teach the model your workflows with skills. with_skills loads folders in the open Agent Skills format only when a task needs them, and their scripts run in the sandbox. The skill gem finds and installs them, with gem exec skill add, and installs RubyLLM's own skill for your coding assistant. By @crmne and @yorzi. (8e0b1470, #1226, #1232, #1235, #1237)
  • Keep files and long tool results out of the conversation. with_file_store keeps them in memory, a folder, or Active Storage, and the model works with them through tools shaped like a coding agent's. Word, Excel, and PowerPoint files need no setup. By @crmne. (#1238, #1443, #1462, #1464, #1469, #1571)
  • Run each persisted chat in one process at a time. complete and the other loop verbs claim the chat first, so a double-submitted approval no longer runs a tool twice. A process that dies gives up its claims within a minute, Chat.interrupted finds the chats it left, and the chat UI generator resumes them. A tool call that had started never runs again by itself. By @crmne. (bf1ff089, #1517, #1572, #1583, #1586, #1589)
  • Queue messages while a chat runs. queue holds a message until the loop can take it, from any thread or process. A message joins the running turn, or the next one with steer: false, and can be withdrawn until it does. By @crmne. (#1223, #1417, #1442, #1534)
  • Let a tool ask the person who runs the chat. ask in a tool pauses the call with no process waiting, and execute runs again with the answer. Approvals and MCP server questions share the same pending_questions list, and any tool can pause on a long-running task. By @crmne. (#1249, #1257, #1540, #1546)
  • Control the agent loop. with_tool_options(rounds:) stops a model that loops on its tools by raising RubyLLM::ToolRoundLimitError, with every call answered. Context budgets keep tool results within the model's window, exclusive tools run alone in a round, around_tool_call wraps each tool in your block, and halt ends the turn from a tool. An agent class now goes wherever tools go, as a subagent. By @crmne. (#1453, #1457, #1460, #1461, #1467, #1562, #1602)
  • Generate every building block. Generators now cover MCP servers, skills, sandboxes, evaluations, and judges. ruby_llm:install adds ApplicationAgent, and the chat UI gains a Stop button, costs, files, a model picker, and a card for each question a tool asks. Its Stimulus controller and view helpers ship with the gem, so bundle update keeps them current. By @crmne. (1cb5c713, #1488, #1495, #1527, #1531, #1541, #1581)
  • Test your agents without a provider. RubyLLM::TestHelper scripts what the model replies, so your tests run real chats, agents, and pages with no API key. By @crmne. (#1498)
  • Moonshot AI, MiniMax, Z.ai, and Meta join the built-in providers. That makes twenty-three. MiniMax and Z.ai also bring images, video, speech, or OCR, and Moonshot brings files, web search, and batches. By @crmne. (#1252, #1253, #1254, #1255, #1608, #1612, #1613, #1636)
  • Fill in what each provider offers. RubyLLM now covers 518 of the 524 shared features its providers offer, each checked against the provider's own documentation. New this release: judgments on OpenRouter and Azure, Chirp 3: HD voices on Vertex AI, Azure AI Speech transcription, Mistral OCR on Azure and Vertex AI, Perplexity file uploads and batches, Meta transcription, Deepgram chat, video and audio input on Meta, MiniMax, Kimi, and Z.ai, and DeepSeek, Z.ai, Perplexity, and Bedrock Nova hosted tools. By @crmne, @mickey, and @cupatea. (#1264, #1437, #1408, #1523, #1626, #1631, #1632, #1635, #1609, #1607, #1620, #1622, #1623, #1624, #1483, #1601)
  • Choose your own timeouts and certificates. config.stream_idle_timeout ends a stream that goes quiet, config.connect_timeout limits opening a connection, and config.vertexai_token_timeout limits waiting for a Vertex AI token. config.ssl sets TLS options for every connection, globally or per context. By @crmne and @wdcyxxycdw; thanks @josh-sanders. (#1598, #1591, #1590, #1559)
  • Fall back with less to configure. with_fallbacks(drop_unsupported: true) retries a model without an OpenAI parameter it rejects, fallbacks work with prompt caching across providers, RubyLLM.paint takes fallbacks:, and a timeout or a long retry wait moves to the next model at once. By @crmne. (#1468, #1529, #1553, #1561, #1597, #1599)
  • Read where answers came from. response.web_sources lists the pages a hosted web search returned, documents a tool returns can be cited, and search results can carry a source_id. By @crmne and @s-andringa. (#1243, #1250, #1455, #1478)
  • Trace, rescue, and shape what you need. Traces can record prompts and responses on request, rescue_from works on tools and MCP servers, split_think_tags moves reasoning a server leaves in the answer into thinking, and prompt templates trim like Rails. A long video can use processing: :agentic, and Azure's content filter verdicts land on messages. By @crmne, @kalicki, and @yorzi; thanks @clarissalimab and @mvysny. (#1333, #1548, #1405, #1086, #1082, #1284)

Fixed

Changed in 2.2

These need attention when upgrading. The upgrade guide has the replacement for each.

  • waiting?, pending_questions, and pending_tasks say what a chat waits on. pending_approvals, awaiting_approval?, pending_inputs, awaiting_input?, and awaiting_tasks? are gone, and decline replaces deny. By @crmne. (#1540, #1546)
  • tool_calls is a collection of calls, and jobs use complete? and succeeded?. The Agent chat_model macro is now chat_class. By @crmne. (#1367, #1368, #1380)
  • RubyLLM.chat defaults to gpt-6.1-sol. Set config.default_model to keep your current model. By @crmne. (47b2427e)
  • A message sent during a reply waits in the queue, and a cancel with nothing to stop does nothing. A cancelled stream keeps its text. By @crmne. (#1223, #1470)
  • Provider gems and custom providers rename protocol methods. Methods that build a request start with render_, methods that read a response start with parse_, and capability predicates drop supports_. By @kieranklaassen. (#1136)
  • Deprecated, and removed in 3.0: iterating a chat directly and chat.cache_until_here, in favor of chat.messages and marking the message itself, and RubyLLM.with_usage_owner, now RubyLLM.with_owner. The old names still work and warn. By @crmne. (#1440, #1448)

Known limitations

  • drop_unsupported recognizes OpenAI's rejections only. Anthropic and Gemini name the parameter only in free text, so their rejections stay BadRequestError.
  • A context budget only cuts the text of tool results. Attachments and long tool call arguments still count against the window.
  • Code execution needs a host that can start its sandbox. On Linux that means Bubblewrap with unprivileged user namespaces; the Sandboxes guide covers hosts that block them.
  • A few provider routes are not verified on a live account yet. Bedrock Mantle's /openai/v1 path, judge on Ollama Cloud, and gpt-image-2 on Azure follow each provider's documentation but have no recording.
  • Perplexity Orchestrator is unverified. protocol: :router_chat_completions follows Perplexity's documentation, but Orchestrator is in limited preview and our key gets HTTP 403, so its tool choice, parallel tool control and cache boundaries have no live recording until Perplexity grants access.
  • On Gemini's Interactions API, tools plus with_schema can loop. The model can keep calling the tool after each result. Use tools or a schema alone, or Gemini's default protocol.
  • xAI batches can be slow. A batch can stay pending for a long time before xAI processes it, so poll with patience.
  • MCP Apps need a UI supplied by your application. The conformance baseline still excludes the older-server elicitation-defaults scenario.
  • Provider access still determines availability. Hosted tools, media, and judgments depend on the selected service and account. See provider coverage for what each one supports.

Thanks

Thanks to @kieranklaassen for 132 pull requests fixing bugs across the framework, and to @yorzi for 33 pull requests of fixes and features and for the reports behind many of them.

Thanks to @bruno-costanzo, @wdcyxxycdw, @s-andringa, @stephankaag, @merlin-croain, @mickey, @monsterlag, @alextakitani, @ammadakram, @jordanyeo, @kalicki, @ningyixi, @lukasedw, and @cupatea for their contributions.

Thanks to @stephankaag, @sheredega303, @juneira, @Boux, @patodevilla, @bjorn-mb, @clarissalimab, @mvysny, @frostmark, @josh-sanders, and @jordan-brough for the reports fixed in this release.

Full changelog: v2.1.1...v2.2.0

Don't miss a new ruby_llm release

NewReleases is sending notifications on new releases.