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.
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 |