Skip to content

Releases: crmne/ruby_llm

1.5.1

01 Aug 13:23
1213612
Compare
Choose a tag to compare

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

31 Jul 22:52
7bcc9db
Compare
Choose a tag to compare

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:

  1. Chats
  2. Messages (depends on Chats)
  3. 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

30 Jul 15:53
db2444e
Compare
Choose a tag to compare

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, and acts_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

Full Changelog: 1.3.1...1.4.0

1.3.1

11 Jun 13:30
fb90276
Compare
Choose a tag to compare

RubyLLM 1.3.1

Bug fixes and improvements.

Bug Fixes

  • Fixed incorrect temperature parameter for OpenAI gpt-4o-search-preview and gpt-4o-mini-search-preview models
  • Fixed intermittent "text content blocks must be non-empty" error with Anthropic API
  • Added missing inverse_of to has_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

Full Changelog: 1.3.0...1.3.1

1.3.0

03 Jun 15:23
Compare
Choose a tag to compare

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

Full Changelog: 1.2.0...1.3.0

1.3.0rc1

12 May 15:38
Compare
Choose a tag to compare
1.3.0rc1 Pre-release
Pre-release

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

Full Changelog: 1.2.0...1.3.0rc1

1.2.0

17 Apr 15:21
Compare
Choose a tag to compare

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 using assume_model_exists
  • Deprecated "Computer Use Preview" models removed from registry

gem 'ruby_llm', '~> 1.2.0'

Merged Pull Requests

Full Changelog: 1.1.2...1.2.0

1.1.2

15 Apr 12:18
Compare
Choose a tag to compare

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

11 Apr 14:13
Compare
Choose a tag to compare

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 to acts_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

New Contributors

Full Changelog: 1.1.0...1.1.1

1.1.0

08 Apr 13:01
Compare
Choose a tag to compare

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 retries
  • retry_backoff_factor: Multiplier for subsequent retry intervals
  • retry_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

Full Changelog: 1.0.1...1.1.0