Ruby's most capable AI runtime

Ruby's AI runtime. Standard-library by default, with one runtime for providers, agents, tools, skills, MCP, A2A, RAG, streaming, files, and persisted state.

Ruby gemstone

Runtime

llm.rb is Ruby's most capable AI runtime.

It runs on Ruby's standard library by default and loads optional pieces only when they are needed. Applications can start with a provider and a single context, then add agents, tools, streaming, persistence, and protocol clients without changing the core shape of the code.

It supports OpenAI, OpenAI-compatible endpoints, Anthropic, Google Gemini, DeepSeek, xAI, Z.ai, AWS Bedrock, Ollama, and llama.cpp. It also includes ActiveRecord and Sequel support, plus concurrent tool execution through threads, tasks, fibers, ractors, and fork.

Install

Source code and releases are available from github.com/llmrb/llm.rb.

$ gem install llm.rb

Capabilities

Area What it gives you
Providers One interface across hosted APIs, local endpoints, and OpenAI-compatible services.
Agents High-level conversations with automatic tool loops, confirmation hooks, skills, and loop guards.
Tools Typed Ruby tool classes with schema generation and provider-specific adaptation.
MCP and A2A Remote tools and skills plugged into the same runtime as local Ruby tools.
Persistence JSON serialization, ActiveRecord integration, and Sequel integration for long-lived state.
ORM Integration Persist contexts and agents through ActiveRecord and Sequel with built-in serialization.
Operations Streaming, request cancellation, tracing, cost tracking, model registry data, and concurrency controls.

Quick start

LLM::Agent

LLM::Agent is the default high-level interface. It manages tool execution for you and keeps the conversation state in its underlying context.

require "llm"

llm = LLM.openai(key: ENV["KEY"])
agent = LLM::Agent.new(llm, stream: $stdout)
agent.talk "Hello world"

LLM::Context

LLM::Context is the lower-level runtime object. Use it when you want to manage tool execution manually.

require "llm"

llm = LLM.openai(key: ENV["KEY"])
ctx = LLM::Context.new(llm, stream: $stdout)
ctx.talk "Hello world"

Tools

Tools are plain Ruby classes with typed parameters. They can be used by contexts, agents, MCP clients, A2A clients, and skills.

class ReadFile < LLM::Tool
  name "read-file"
  description "Read a file"
  parameter :path, String, "The filename or path"
  required %i[path]

  def call(path:)
    {contents: File.read(path)}
  end
end

Protocols and skills

Feature Use
MCP Use tools provided by local stdio servers or remote HTTP MCP servers.
A2A Use skills provided by remote A2A agents as local runtime tools.
Skills Package reusable instructions and scoped tool access in SKILL.md directories.
Transports Use standard Net::HTTP by default, persistent Net::HTTP when needed, or optional libcurl through curb.
require "llm"

llm = LLM.openai(key: ENV["KEY"])
mcp = LLM::MCP.stdio(argv: ["ruby", "server.rb"])

mcp.session do
  agent = LLM::Agent.new(llm, stream: $stdout, tools: mcp.tools)
  agent.talk "Use the available tools to inspect the environment."
end

Streaming and concurrency

Streams can be simple IO objects or subclasses of LLM::Stream with structured callbacks for content, reasoning, tool calls, tool returns, and compaction. Agents can resolve tool work sequentially or concurrently with threads, tasks, fibers, ractors, or forks.

class Agent < LLM::Agent
  model "gpt-5.4-mini"
  tools ReadFile
  concurrency :thread
end

llm = LLM.openai(key: ENV["KEY"])
agent = Agent.new(llm, stream: $stdout)
agent.talk "Read README.md and CHANGELOG.md and compare them."

ORM Integration

llm.rb treats ActiveRecord and Sequel as first-class integration points. The built-in wrappers persist the serialized runtime state in one column while your application keeps control of provider, model, and tool configuration.

With ActiveRecord, acts_as_agent wraps LLM::Agent directly on a model, including agent DSL features such as instructions, tools, and concurrency.

require "llm"
require "active_record"
require "llm/active_record"

class Ticket < ApplicationRecord
  acts_as_agent provider: :set_provider, context: :set_context
  model "gpt-5.4-mini"
  instructions "You are a concise support assistant."
  tools SearchDocs, Escalate
  concurrency :thread

  private

  def set_provider
    LLM.openai(key: ENV["OPENAI_SECRET"])
  end

  def set_context
    {mode: :responses, store: false}
  end
end

ticket = Ticket.create!
puts ticket.talk("How do I rotate my API key?").content

Persistence

Agents and contexts can serialize to JSON and restore later. The same serialized state powers the ActiveRecord and Sequel integrations, so applications can persist conversations in ordinary database columns without designing a separate message store first.

require "llm"

llm = LLM.openai(key: ENV["KEY"])
agent1 = LLM::Agent.new(llm)
agent1.talk "Remember that my favorite language is Ruby"
string = agent1.to_json

agent2 = LLM::Agent.new(llm, stream: $stdout)
agent2.restore(string:)
agent2.talk "What is my favorite language?"

Applications

The llm.rb runtime and its ports power small terminal applications that can be tried over SSH.

Application Try it Runtime
Matz ssh matz@r.uby.dev mruby-llm
Robert ssh robert@4.4bsd.dev mruby-llm