Releases: crmne/ruby_llm
1.5.1
RubyLLM 1.5.1: Model Registry Validation and Fixes 🛠️
A quick patch release introducing schema validation for our model registry and fixing configuration issues.
📋 Model Schema Validation
To prevent future model configuration errors, we've introduced models_schema.json
that validates our model registry. This ensures consistency across all provider model definitions and caught the issues fixed in this release.
🐛 Bug Fixes
Model Capabilities Format
Fixed incorrect capabilities format for Mistral models that was caught by our new schema validation. The capabilities field was incorrectly set as a Hash instead of an Array:
# Before: capabilities: { "chat" => true }
# After: capabilities: ["chat"]
Image Generation Models
Google's Imagen models disappeared from Parsera (our model data source), causing them to lose their proper output modality. We've fixed this by explicitly setting their output modality to image
.
🔧 Infrastructure Updates
- JRuby CI: Updated to JRuby 10.0.1.0 for better compatibility
- Appraisal: Automated generation of Appraisal gemfiles for cleaner multi-Rails version testing
Installation
gem 'ruby_llm', '1.5.1'
This is a patch release with full backward compatibility. If you're using Mistral models or Google's Imagen, this update is recommended.
Full Changelog: 1.5.0...1.5.1
1.5.0
RubyLLM 1.5.0: Mistral, Perplexity, and Generator Fixes 🚀
Two new providers join the family, bringing 68 new models and specialized capabilities. Plus critical bug fixes for Rails users.
🌟 New Providers
Mistral AI
Full support for Mistral's model lineup - 63 models including their latest releases:
RubyLLM.configure do |config|
config.mistral_api_key = ENV['MISTRAL_API_KEY']
end
# Chat with their flagship model
chat = RubyLLM.chat(model: 'mistral-large-latest')
# Use the efficient Ministral models
small_chat = RubyLLM.chat(model: 'ministral-8b-latest')
# Vision with Pixtral
vision = RubyLLM.chat(model: 'pixtral-12b-latest')
vision.ask("What's in this image?", with: "path/to/image.jpg")
# Embeddings
embed = RubyLLM.embed("Ruby metaprogramming", model: 'mistral-embed')
Streaming, tools, and all standard RubyLLM features work seamlessly.
Perplexity
Real-time web search meets LLMs with Perplexity's Sonar models:
RubyLLM.configure do |config|
config.perplexity_api_key = ENV['PERPLEXITY_API_KEY']
end
# Get current information with web search
chat = RubyLLM.chat(model: 'sonar-pro')
response = chat.ask("What are the latest Ruby 3.4 features?")
# Searches the web and returns current information
# Deep research mode for comprehensive answers
researcher = RubyLLM.chat(model: 'sonar-deep-research')
answer = researcher.ask("Compare Ruby's fiber scheduler implementations")
Choose from Sonar, Sonar Pro, Sonar Reasoning, and Deep Research models based on your needs.
🐛 Critical Rails Generator Fixes
Migration Order
The Rails generator now creates migrations in the correct dependency order:
- Chats
- Messages (depends on Chats)
- Tool Calls (depends on Messages)
No more foreign key errors during rails db:migrate
.
PostgreSQL Detection
Fixed namespace collision with RubyLLM::ActiveRecord
that prevented proper database detection. PostgreSQL users now correctly get jsonb
columns instead of json
.
📚 Documentation & Quality
- Rails guide enhanced with instant message display patterns using Action Cable
- Comprehensive feature list added to README
- Available models guide promoted to top-level navigation
- Model pricing and capabilities updated across all providers
- Fixed broken links after documentation reorganization
Installation
gem 'ruby_llm', '1.5.0'
Full backward compatibility maintained. Your existing code continues to work.
Full Changelog: 1.4.0...1.5.0
1.4.0
RubyLLM 1.4.0: Structured Output, Custom Parameters, and Rails Generators 🚀
We're shipping 1.4.0! This release brings structured output that actually works, direct access to provider-specific parameters, and Rails generators that produce idiomatic Rails code.
🎯 Structured Output with JSON Schemas
Wrestling with LLMs to return valid JSON is over. We've added with_schema
that makes structured output as simple as defining what you want:
# Define your schema with the RubyLLM::Schema DSL
# First: gem install ruby_llm-schema
require 'ruby_llm/schema'
class PersonSchema < RubyLLM::Schema
string :name
integer :age
array :skills, of: :string
end
# Get perfectly structured JSON every time
chat = RubyLLM.chat.with_schema(PersonSchema)
response = chat.ask("Generate a Ruby developer profile")
# => {"name" => "Yukihiro", "age" => 59, "skills" => ["Ruby", "C", "Language Design"]}
No more prompt engineering gymnastics. Just schemas and results. Use the RubyLLM::Schema gem for the cleanest DSL, or provide raw JSON schemas if you prefer.
🛠️ Direct Provider Access with with_params
Need to use that one provider-specific parameter? with_params
gives you direct access:
# OpenAI's JSON mode
chat.with_params(response_format: { type: "json_object" })
.ask("List Ruby features as JSON")
No more workarounds. Direct access to any parameter your provider supports.
🚄 Rails Generator That Produces Idiomatic Rails Code
From rails new
to chatting with LLMs in under 5 minutes:
rails generate ruby_llm:install
This creates:
- Migrations with proper Rails conventions
- Models with
acts_as_chat
,acts_as_message
, andacts_as_tool_call
- A readable initializer with sensible defaults
- Zero boilerplate, maximum convention
Your Chat
model works exactly as you'd expect:
chat = Chat.create!(model: "gpt-4")
response = chat.ask("Explain Ruby blocks")
# Messages are automatically persisted with proper associations
# Tool calls are tracked, tokens are counted
🔍 Tool Call Transparency
New on_tool_call
callback lets you observe and log tool usage:
chat.on_tool_call do |tool_call|
puts "🔧 AI is calling: #{tool_call.name}"
puts " Arguments: #{tool_call.arguments}"
# Perfect for debugging and auditing
Rails.logger.info "[AI Tool] #{tool_call.name}: #{tool_call.arguments}"
end
chat.ask("What's the weather in Tokyo?").with_tools([weather_tool])
# => 🔧 AI is calling: get_weather
# Arguments: {"location": "Tokyo"}
🔌 Raw Response Access
Access the underlying Faraday response for debugging or advanced use cases:
response = chat.ask("Hello!")
# Access headers, status, timing
puts response.raw.headers["x-request-id"]
puts response.raw.status
puts response.raw.env.duration
🏭 GPUStack Support
Run models on your own hardware with GPUStack:
RubyLLM.configure do |config|
config.gpustack_api_base = 'http://localhost:8080/v1'
config.gpustack_api_key = 'your-key'
end
chat = RubyLLM.chat(model: 'qwen3', provider: 'gpustack')
🐛 Important Bug Fixes
- Anthropic multiple tool calls now properly handled (was only processing the first tool)
- Anthropic system prompts fixed to use plain text instead of JSON serialization
- Message ordering in streaming responses is rock solid
- Embedding arrays return consistent formats for single and multiple strings
- URL attachments work properly without argument errors
- Streaming errors handled correctly in both Faraday V1 and V2
- JRuby officially supported and tested
🎁 Enhanced Rails Integration
- Message ordering guidance to prevent race conditions
- Provider-specific configuration examples
- Custom model name support with
acts_as_
helpers - Improved generator output
Context isolation works seamlessly without global config pollution:
# Each request gets its own isolated configuration
tenant_context = RubyLLM.context do |config|
config.openai_api_key = tenant.api_key
end
tenant_context.chat.ask("Process this tenant's request")
# Global configuration remains untouched
📚 Quality of Life Improvements
- Removed 60MB of test fixture data
- OpenAI base URL configuration in bin/console
- Better error messages for invalid models
- Enhanced Ollama documentation
- More code examples throughout
Installation
gem 'ruby_llm', '1.4.0'
Full backward compatibility maintained. Your existing code continues to work while new features await when you need them.
Merged PRs
- Add OpenAI base URL config to bin/console by @infinityrobot in #283
- Reject models from Parsera that does not have :provider or :id by @K4sku in #271
- Fix embedding return format inconsistency for single-string arrays by @finbarr in #267
- Fix compatibility issue with URL attachments wrong number of arguments by @DustinFisher in #250
- Add JRuby to CI test job by @headius in #255
- Add provider specifying example to rails guide by @tpaulshippy in #233
- More details for configuring Ollama by @jslag in #252
- Remove 60 MB of the letter 'a' from spec/fixtures/vcr_cassettes by @compumike in #287
- docs: add guide for using custom model names with acts_as helpers by @matheuscumpian in #171
- Add
RubyLLM::Chat#with_params
to add custom parameters to the underlying API payload by @compumike in #265 - Support gpustack by @graysonchen in #142
- Update CONTRIBUTING.md by @graysonchen in #289
- Fix handling of multiple tool calls in single LLM response by @finbarr in #241
- Rails Generator for RubyLLM Models by @kieranklaassen in #75
- Anthropic: Fix system prompt (use plain text instead of serialized JSON) by @MichaelHoste in #302
- Provide access to raw response object from Faraday by @tpaulshippy in #304
- Add Chat#on_tool_call callback by @bryan-ash in #299
- Added proper handling of streaming error responses across both Faraday V1 and V2 by @dansingerman in #273
- Add message ordering guidance to Rails docs by @crmne in #288
New Contributors
- @infinityrobot made their first contribution in #283
- @K4sku made their first contribution in #271
- @finbarr made their first contribution in #267
- @DustinFisher made their first contribution in #250
- @headius made their first contribution in #255
- @jslag made their first contribution in #252
- @compumike made their first contribution in #287
- @matheuscumpian made their first contribution in #171
- @graysonchen made their first contribution in #142
- @MichaelHoste made their first contribution in #302
- @bryan-ash made their first contribution in #299
- @dansingerman made their first contribution in #273
Full Changelog: 1.3.1...1.4.0
1.3.1
RubyLLM 1.3.1
Bug fixes and improvements.
Bug Fixes
- Fixed incorrect temperature parameter for OpenAI
gpt-4o-search-preview
andgpt-4o-mini-search-preview
models - Fixed intermittent "text content blocks must be non-empty" error with Anthropic API
- Added missing
inverse_of
tohas_many :messages
association for proper ActiveRecord bi-directional relationships - Fixed
Encoding::CompatibilityError
when processing non-ASCII characters
New Features
- Added support for custom logger configuration
- Updated model registry with latest provider offerings
Improvements
- Enhanced documentation for logging configuration
- Updated test suite with improved Ollama model support
Installation
gem 'ruby_llm', '1.3.1'
Merged PRs
- Fix Encoding::CompatibilityError: Unicode Normalization not appropriate for ASCII-8BIT by @Eric-Guo in #218
- Resolve "text content blocks must be non-empty" issue against Anthropic/Bedrock API by @tpaulshippy in #237
- Add inverse_of to has_many :messages by @niciliketo in #230
- Add support for custom logger via configuration by @tristandunn in #225
New Contributors
- @Eric-Guo made their first contribution in #218
- @niciliketo made their first contribution in #230
- @tristandunn made their first contribution in #225
Full Changelog: 1.3.0...1.3.1
1.3.0
RubyLLM 1.3.0: Just When You Thought the Developer Experience Couldn't Get Any Better 🎉
I'm thrilled to ship RubyLLM 1.3.0 stable! Just when you thought RubyLLM's developer experience couldn't get any better, we've made attachments ridiculously simple, added isolated configuration contexts, and officially ended the era of manual model tracking.
📎 The Attachment Revolution: From Complex to Magical
The biggest transformation in 1.3.0 is how stupidly simple attachments have become. Before, you had to categorize every file:
# The old way (still works, but why would you?)
chat.ask("What's in this image?", with: { image: "diagram.png" })
chat.ask("Describe this meeting", with: { audio: "meeting.wav" })
chat.ask("Summarize this document", with: { pdf: "contract.pdf" })
Now? Just throw files at it and RubyLLM figures out the rest:
# The new way - pure magic ✨
chat.ask("What's in this file?", with: "diagram.png")
chat.ask("Describe this meeting", with: "meeting.wav")
chat.ask("Summarize this document", with: "contract.pdf")
# Multiple files? Mix and match without thinking
chat.ask("Analyze these files", with: [
"quarterly_report.pdf",
"sales_chart.jpg",
"customer_interview.wav",
"meeting_notes.txt"
])
# URLs work too
chat.ask("What's in this image?", with: "https://example.com/chart.png")
This is what the Ruby way looks like: you shouldn't have to think about file types when the computer can figure it out for you.
🔄 Isolated Configuration with Contexts
Building multi-tenant applications just got trivial. Contexts let you create isolated configuration scopes without touching your global settings:
# Each tenant gets their own isolated configuration
tenant_context = RubyLLM.context do |config|
config.openai_api_key = tenant.openai_key
config.anthropic_api_key = tenant.anthropic_key
config.request_timeout = 180 # This tenant needs more time
end
# Use it without polluting the global namespace
response = tenant_context.chat.ask("Process this customer request...")
# Global configuration remains untouched
RubyLLM.chat.ask("This still uses your default settings")
Perfect for multi-tenancy, A/B testing different providers, environment targeting, or any situation where you need temporary configuration changes.
🚂 Rails Integration That Finally Feels Like Rails
The Rails integration now works seamlessly with ActiveStorage:
# Enable attachment support in your Message model
class Message < ApplicationRecord
acts_as_message
has_many_attached :attachments # Add this line
end
# Handle file uploads directly from forms
chat_record.ask("Analyze this upload", with: params[:uploaded_file])
# Work with existing ActiveStorage attachments
chat_record.ask("What's in my document?", with: user.profile_document)
# Process multiple uploads at once
chat_record.ask("Review these files", with: params[:files])
We've brought the Rails attachment handling to complete parity with the plain Ruby implementation. No more "it works in Ruby but not in Rails" friction.
💻 Local Models with Ollama
Your development machine shouldn't need to phone home to OpenAI every time you want to test something:
RubyLLM.configure do |config|
config.ollama_api_base = 'http://localhost:11434/v1'
end
# Same API, different model
chat = RubyLLM.chat(model: 'mistral', provider: 'ollama')
response = chat.ask("Explain Ruby's eigenclass")
Perfect for privacy-sensitive applications, offline development, or just experimenting with local models.
🔀 Hundreds of Models via OpenRouter
Access models from dozens of providers through a single API:
RubyLLM.configure do |config|
config.openrouter_api_key = ENV['OPENROUTER_API_KEY']
end
# Access any model through OpenRouter
chat = RubyLLM.chat(model: 'anthropic/claude-3.5-sonnet', provider: 'openrouter')
One API key, hundreds of models. Simple.
🌐 The End of Manual Model Tracking
We've partnered with Parsera to create a single source of truth for LLM capabilities and pricing. When you run RubyLLM.models.refresh!
, you're now pulling from the Parsera API - a continuously updated registry that scrapes model information directly from provider documentation.
No more manually updating capabilities files every time OpenAI changes their pricing. No more hunting through documentation to find context windows. It's all there, always current.
Read more about this revolution in my blog post.
🔢 Custom Embedding Dimensions
Fine-tune your embeddings for specific use cases:
# Generate compact embeddings for memory-constrained environments
embedding = RubyLLM.embed(
"Ruby is a programmer's best friend",
model: "text-embedding-3-small",
dimensions: 512 # Instead of the default 1536
)
🏢 Enterprise OpenAI Support
Organization and project IDs are now supported:
RubyLLM.configure do |config|
config.openai_api_key = ENV['OPENAI_API_KEY']
config.openai_organization_id = ENV['OPENAI_ORG_ID']
config.openai_project_id = ENV['OPENAI_PROJECT_ID']
end
📊 Official Version Support
We now officially support and test against:
- Ruby 3.1 to 3.4
- Rails 7.1 to 8.0
Your favorite Ruby version is covered.
Upgrade Today
gem 'ruby_llm', '1.3.0'
As always, we've maintained full backward compatibility. Your existing code continues to work exactly as before, but now with magical attachment handling and powerful new capabilities.
Read the full story in my blog post and check out the complete model guide.
Merged PRs
- Support Embedding Dimensions by @papgmez in #73
- Updated acts_as_* helpers to use canonical 'rails-style' foreign keys by @timaro in #151
- Add support for logging to file via configuration by @rhys117 in #148
- Use foreign_key instead of to_s for acts_as methods by @bborn in #157
- Fix inflector by @bborn in #159
- Handle OpenAI organization and project IDs by @xymbol in #162
- Don't call blob on a blob by @bborn in #164
- fix: prevent with_instructions from throwing error by @roelbondoc in #176
- replace Utils.deep_symbolize_keys with native symbolize_names: true by @max-power in #179
- [Gemspec] - Allow lower faraday versions by @itstheraj in #173
- feat(Configuration): Add http_proxy configuration option by @stadia in #189
- Support gpt-image-1 by @tpaulshippy in #201
- Issue 203: Fix model alias matching for provider prefixes (claude-3-7-sonnet support) by @Sami-Tanquary in #206
- Update tools.md by @seemiller in #212
- Use consistent name for Weather tool in docs by @seemiller in #215
New Contributors
- @papgmez made their first contribution in #73
- @timaro made their first contribution in #151
- @rhys117 made their first contribution in #148
- @bborn made their first contribution in #157
- @xymbol made their first contribution in #162
- @roelbondoc made their first contribution in #176
- @max-power made their first contribution in #179
- @itstheraj made their first contribution in #173
- @stadia made their first contribution in #189
- @Sami-Tanquary made their first contribution in #206
- @seemiller made their first contribution in #212
Full Changelog: 1.2.0...1.3.0
1.3.0rc1
RubyLLM 1.3.0.rc1: Isolation, Configuration & Model Expansion 🚀
We're excited to introduce RubyLLM 1.3.0.rc1 with isolated configuration contexts, expanded provider support, and major improvements to attachments and Rails integration.
🔄 Isolation with Contexts
Introducing scoped configuration with Contexts - perfect for multi-tenant applications, environment targeting, and testing:
# Create an isolated configuration context
context = RubyLLM.context do |config|
config.openai_api_key = ENV['TENANT_A_OPENAI_KEY']
config.anthropic_api_key = ENV['TENANT_A_ANTHROPIC_KEY']
config.request_timeout = 180 # Longer timeout just for this context
end
# Use the isolated context for specific operations
embedding = context.embed("Text for tenant A")
response = context.chat.ask("Question from tenant A")
# Global configuration remains untouched
RubyLLM.embed("This uses the default configuration")
📎 Smart Attachments with Auto Type Detection
The new attachment system works everywhere - not just Rails! RubyLLM automatically detects file types and handles the appropriate API formatting:
# Send a single file - auto detects if it's an image, PDF, or audio
chat.ask("What's in this file?", with: "diagram.png")
# Send multiple files - all types are automatically detected
chat.ask("Analyze these files", with: [
"report.pdf",
"chart.jpg",
"recording.mp3"
])
# Still works with manual categorization if you prefer
chat.ask("What's in this?", with: {
image: "diagram.png",
audio: "recording.mp3"
})
🚂 Enhanced Rails Integration
The Rails integration now supports attachments with ActiveStorage, making it easy to work with uploaded files:
# After setting up ActiveStorage
class Message < ApplicationRecord
acts_as_message
has_many_attached :attachments # Add this for attachment support
end
# Works seamlessly with Rails uploads
chat_record.ask("Analyze this", with: params[:uploaded_file])
# Works with existing ActiveStorage attachments
chat_record.ask("What's in my document?", with: user.profile_document)
# Send multiple files from a form
chat_record.ask("Review these documents", with: params[:files])
The persistence flow has also been enhanced with better error handling and support for validation-focused approaches.
💻 Ollama Integration
Local LLMs are now first-class citizens with Ollama support:
RubyLLM.configure do |config|
config.ollama_api_base = 'http://localhost:11434/api'
end
# Use local models
chat = RubyLLM.chat(model: 'mistral')
🔀 OpenRouter Support
Access hundreds of models through a single API:
RubyLLM.configure do |config|
config.openrouter_api_key = ENV['OPENROUTER_API_KEY']
end
# Access models from various providers through OpenRouter
chat = RubyLLM.chat(model: 'anthropic/claude-3.5-sonnet', provider: 'openrouter')
🏢 OpenAI Organization Support
Support for OpenAI organization and project IDs:
RubyLLM.configure do |config|
config.openai_api_key = ENV['OPENAI_API_KEY']
config.openai_organization_id = ENV['OPENAI_ORG_ID']
config.openai_project_id = ENV['OPENAI_PROJECT_ID']
end
🌐 Better Model Information via Parsera API
We now use the Parsera API for getting accurate, up-to-date model information when you run RubyLLM.models.refresh!
. This provides scraped data directly from provider websites, ensuring the most current model details, pricing, and capabilities.
Check out the model browser built by Parsera for more information.
🔢 Custom Embedding Dimensions
Control the dimensionality of your embeddings:
# Generate compact embeddings for specific use cases
embedding = RubyLLM.embed(
"Ruby is a programmer's best friend",
model: "text-embedding-3-small",
dimensions: 512
)
🪵 Configurable Logging
Control where and how RubyLLM logs:
RubyLLM.configure do |config|
config.log_file = '/path/to/ruby_llm.log'
config.log_level = :debug
end
🔧 Core Improvements
- Completely refactored Content implementation for simplified provider integration
- Fixed duplicate messages when calling
chat.to_llm
multiple times - Enhanced media handling across providers (including PDF support)
- Fixed embedding when using default model
- Configuration.inspect now properly filters sensitive data
- Rails-style foreign keys for custom class names in ActiveRecord integration
- Fixed empty message cleanup on API failures
- Enhanced test coverage for streaming vs non-streaming consistency
This is a release candidate. Please test thoroughly, especially the new context and provider features!
gem 'ruby_llm', '1.3.0.rc1'
PRs Merged
- Support Embedding Dimensions by @papgmez in #73
- Updated acts_as_* helpers to use canonical 'rails-style' foreign keys by @timaro in #151
- Add support for logging to file via configuration by @rhys117 in #148
- Use foreign_key instead of to_s for acts_as methods by @bborn in #157
- Fix inflector by @bborn in #159
- Handle OpenAI organization and project IDs by @xymbol in #162
- Don't call blob on a blob by @bborn in #164
New Contributors
- @papgmez made their first contribution in #73
- @timaro made their first contribution in #151
- @rhys117 made their first contribution in #148
- @bborn made their first contribution in #157
- @xymbol made their first contribution in #162
Full Changelog: 1.2.0...1.3.0rc1
1.2.0
RubyLLM 1.2.0: Universal OpenAI Compatibility & Custom Models
This release massively expands RubyLLM's reach by adding support for ANY provider with an OpenAI-compatible API. Connect to Azure OpenAI, local models, API proxies, or any service that speaks the OpenAI protocol.
See it in action connecting to a local Ollama server: Watch Demo
🌟 Major Features
- Universal OpenAI Compatibility: Connect to ANY OpenAI-compatible endpoint:
- Azure OpenAI Service
- Self-hosted models (Ollama, LM Studio)
- API proxies (LiteLLM, FastAI)
- Local deployments
- Custom fine-tunes
RubyLLM.configure do |config|
# Works with any OpenAI-compatible API
config.openai_api_base = "https://your-endpoint/v1"
config.openai_api_key = ENV.fetch('YOUR_API_KEY', nil) # if needed
end
- Unlisted Model Support: For providers or models not in the official registry (needed when using
openai_api_base
)
# FIRST: Always try refreshing the model registry
RubyLLM.models.refresh!
# For truly custom/local providers that won't be in the registry:
chat = RubyLLM.chat(
model: "my-local-model",
provider: :openai, # Required for custom endpoints
assume_model_exists: true
)
# or during a chat
chat.with_model(model: "my-local-model", provider: :openai, assume_exists: true)
🚀 Improvements
- Default Model Update: Switched default model to
gpt-4.1-nano
- New Models: Added support for:
- LearnLM 2.0 Flash
- O4 Mini models
- Claude 3.7 Sonnet (with proper Bedrock support)
- Fixes:
- Fixed temperature normalization for O-series models
- Added proper support for Bedrock inference profiles
- Documentation: Completely rewrote documentation
⚠️ Important Notes
- New Models? Always try
RubyLLM.models.refresh!
first -assume_model_exists
is for custom/local providers - The
provider
parameter is mandatory when usingassume_model_exists
- Deprecated "Computer Use Preview" models removed from registry
gem 'ruby_llm', '~> 1.2.0'
Merged Pull Requests
- Fix Claude 3.7 Sonnet in Bedrock by @tpaulshippy in #117
Full Changelog: 1.1.2...1.2.0
1.1.2
RubyLLM 1.1.2: GPT-4.1 Million-Token Support & New Model Families
This release adds support for OpenAI's GPT-4.1 family with its massive context window, along with several new model families from Google and other providers.
🎯 New Features
- GPT-4.1 Support: Full integration of the GPT-4.1 family with support for:
- Million-token context window (1,047,576 tokens)
- Vision capabilities
- Function calling
- Structured output
- Cached input pricing optimization
chat = RubyLLM.chat(model: "gpt-4.1") # Standard model
chat = RubyLLM.chat(model: "gpt-4.1-mini") # Cost-optimized version
chat = RubyLLM.chat(model: "gpt-4.1-nano") # Minimal pricing version
- New Google Models: Added support for latest Gemini models:
- Gemini 2.0 Flash Live for real-time interactions
- Gemini 2.5 Pro Preview with enhanced capabilities
- Gemma 3 for open model support
- Veo 2 model family
💎 Improvements
- Enhanced capabilities detection for new model families
This is an optional update unless you need support for the new model families.
gem 'ruby_llm', '~> 1.1.2'
Full Changelog: 1.1.1...1.1.2
1.1.1
RubyLLM 1.1.1: Streaming Dependency Fix & Minor Improvements
This patch release addresses a dependency issue that affects streaming when using faraday-net_http 2.x
.
🐛 Bug Fixes
- Streaming Dependency Fix: Added requirement for
faraday-net_http >= 3.0
to ensure the streaming functionality works correctly due to the callback signature changes. Fixes #107.
🚂 Rails Enhancements
- Active Record Touch Option: Added
touch_chat
option toacts_as_message
for automatically updating the timestamp on the associated chat record when messages are created or updated.
class Message < ApplicationRecord
acts_as_message touch_chat: true
end
🧹 Clean-up
- Removed unreachable model alias entries to avoid confusion
- Enhanced version check logic in CI workflow for better handling of both releases and prereleases
This is a optional update, but a must-have if you had faraday-net_http 2.x
.
gem 'ruby_llm', '~> 1.1.1'
Merged Pull Requests
- Removing unreachable alias entries to avoid confusion by @jimjimovich in #104
New Contributors
- @jimjimovich made their first contribution in #104
Full Changelog: 1.1.0...1.1.1
1.1.0
RubyLLM 1.1.0: Cloud Expansion, Smarter Conversations, Resilient Tooling, and More 🚀
We're thrilled to announce RubyLLM 1.1.0, a major update expanding cloud provider support, enhancing conversation management, and introducing a resilient error handling paradigm for tools.
🌩️ AWS Bedrock Support
Access Claude models through your existing AWS infrastructure:
RubyLLM.configure do |config|
config.bedrock_api_key = ENV.fetch('AWS_ACCESS_KEY_ID')
config.bedrock_secret_key = ENV.fetch('AWS_SECRET_ACCESS_KEY')
config.bedrock_region = ENV.fetch('AWS_REGION')
config.bedrock_session_token = ENV.fetch('AWS_SESSION_TOKEN') # optional
end
# Using Claude through Bedrock
chat = RubyLLM.chat(model: 'claude-3-5-sonnet', provider: 'bedrock')
Great for teams who want to keep everything within their existing AWS environment.
🧠 Simpler System Instructions
Introducing with_instructions
for a cleaner way to set system prompts:
chat = RubyLLM.chat
.with_instructions("You are a helpful coding assistant that specializes in Ruby")
.ask("How would I implement a binary search tree?")
You can also explicitly replace previous instructions instead of accumulating them:
chat.with_instructions("New instructions", replace: true)
This works across all providers, with smart handling of differences between OpenAI, Anthropic, and others.
🛠️ Smarter Tool Error Handling
A completely new approach to handling errors in tools that lets LLMs handle recoverable issues while bubbling up critical errors to your application:
class Weather < RubyLLM::Tool
description "Gets current weather for a location"
param :latitude, desc: "Latitude (e.g., 52.5200)"
param :longitude, desc: "Longitude (e.g., 13.4050)"
def execute(latitude:, longitude:)
validate_coordinates!(latitude, longitude)
response = Faraday.get(weather_api_url(latitude, longitude))
case response.status
when 429
# Return errors the LLM should know about and can retry
{ error: "Rate limit exceeded. Please try again in 60 seconds." }
when 200
JSON.parse(response.body)
else
# Let serious problems bubble up
raise "Weather API error: #{response.status}"
end
end
end
When to Return vs Raise Errors
- Return errors to the LLM when:
- Input validation fails
- The operation can be retried (rate limits, temporary failures)
- Alternative approaches might work
- Let errors bubble up when:
- The tool encounters unexpected states
- System resources are unavailable
- Authentication or authorization fails
- Data integrity is compromised
🔄 Configurable Retries
New configurable retry settings for API requests, making your applications more resilient:
RubyLLM.configure do |config|
# Configure retry settings
config.max_retries = 5 # Default: 3
config.retry_interval = 0.5 # Default: 0.1 (seconds)
config.retry_backoff_factor = 3 # Default: 2
config.retry_interval_randomness = 0.3 # Default: 0.5
end
The retry mechanism now includes:
retry_interval
: Initial wait time between retriesretry_backoff_factor
: Multiplier for subsequent retry intervalsretry_interval_randomness
: Adds jitter to prevent thundering herd problems
We've also expanded the retry status codes to handle service overload errors.
🔀 Smarter Model Resolution
Model identification is now much smarter:
- Model Aliases: Use simple names like
claude-3-5-sonnet
instead of full version strings - Provider-Specific Matching: Target specific providers with the same base model
- Exact Match Priority: Exact matches are prioritized over aliases
# These all work now
chat = RubyLLM.chat(model: 'claude-3-5-sonnet')
chat = RubyLLM.chat(model: 'claude-3-5-sonnet', provider: 'anthropic')
chat = RubyLLM.chat(model: 'claude-3-5-sonnet', provider: 'bedrock')
🚂 Rails Enhancements
ActiveRecord integration improvements make Rails usage even better:
# Now all these methods return self for chainability
chat = Chat.create!(model_id: 'gpt-4o-mini')
.with_instructions("You are a helpful assistant")
.with_tool(Calculator)
.ask("What's 123 * 456?")
🚿 Streaming Improvements
Fixed several streaming issues:
- Improved error parsing across all providers during streaming
- Fixed JSON parsing errors when using Gemini with tools and no arguments
- Enhanced multi-turn streaming conversations
🔧 Technical Improvements
- Fixed multimodal inputs for Bedrock Anthropic
- Improved system prompt handling across all providers
- Better error parsing for AWS Bedrock responses
- Fixed model refreshing for read-only filesystems in production
- Improvements to DeepSeek and Anthropic providers
- Support for parameterless tools
📖 Documentation Updates
- New comprehensive model information guide listing 100+ models with capabilities
- Updated error handling guidelines
- Working with multiple providers
- Using the model registry effectively
- Deploying in production environments
Install or update RubyLLM today:
gem 'ruby_llm', '~> 1.1.0'
Merged Pull Requests
- Support tools without params by @redox in #62
- Switched from git ls-files to Ruby's Dir.glob for defining spec.file by @seuros in #84
- 16: Add support for Claude through Bedrock by @tpaulshippy in #50
- Fix refresh of Bedrock models and remove some expired credentials from cassettes by @tpaulshippy in #89
- fix acts_as delegation to return self instead of RubyLLM by @kieranklaassen in #82
- Add a rake task to generate an 'available model information' markdown guide. by @keithrbennett in #71
- Parse errors from Bedrock properly by @tpaulshippy in #94
New Contributors
- @redox made their first contribution in #62
- @seuros made their first contribution in #84
- @tpaulshippy made their first contribution in #50
- @kieranklaassen made their first contribution in #82
- @keithrbennett made their first contribution in #71
Full Changelog: 1.0.1...1.1.0