The project is in a healthy, maintained state
Enhanced Ruby library for OpenRouter API with tool calling, structured outputs, and intelligent model selection. Based on the original work by Obie Fernandez.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

>= 6.0, < 9.0
>= 2.0, < 4.0
>= 1.0, < 3.0
>= 1.0, < 2.0
 Project Readme

OpenRouter Enhanced - Ruby Gem

The future will bring us hundreds of language models and dozens of providers for each. How will you choose the best?

The OpenRouter API is a single unified interface for all LLMs, accessible through an idiomatic Ruby interface.

OpenRouter Enhanced is an advanced fork of the original OpenRouter Ruby gem by Obie Fernandez that adds comprehensive AI application development features including tool calling, structured outputs, intelligent model selection, prompt templates, observability, and automatic response healing—all while maintaining full backward compatibility.

Read the story behind OpenRouter Enhanced - Learn why this gem was built and the philosophy behind its design.

Enhanced Features

Core AI Features

  • Tool Calling: Full support for OpenRouter's function calling API with Ruby-idiomatic DSL
  • Structured Outputs: JSON Schema validation with automatic healing for non-native models
  • Smart Model Selection: Intelligent model selection with cost optimization and capability matching
  • Prompt Templates: Reusable prompt templates with variable interpolation and few-shot learning

Performance & Reliability

  • Model Registry: Local caching and querying of OpenRouter model data with capability detection
  • Enhanced Response Handling: Rich Response objects with automatic parsing for tool calls and structured outputs
  • Automatic Healing: Self-healing responses for malformed JSON from models without native structured output support
  • Model Fallbacks: Automatic failover between models with graceful degradation
  • Streaming Support: Enhanced streaming client with callback system and response reconstruction

Observability & Analytics

  • Usage Tracking: Comprehensive token usage and cost tracking across all API calls
  • Response Analytics: Detailed metadata including tokens, costs, cache hits, and performance metrics
  • Callback System: Extensible event system for monitoring requests, responses, and errors
  • Cost Management: Built-in cost estimation and budget constraints

Core OpenRouter Benefits

  • Prioritize price or performance: OpenRouter scouts for the lowest prices and best latencies/throughputs across dozens of providers
  • Standardized API: No need to change your code when switching between models or providers
  • Easy integration: Simple and intuitive Ruby interface for AI capabilities

Table of Contents

  • Installation
  • Quick Start
  • Configuration
  • Features
    • Tool Calling
    • Structured Outputs
    • Smart Model Selection
    • Prompt Templates
    • Streaming
    • Usage Tracking
  • Model Exploration
  • Testing
  • Troubleshooting
  • Contributing
  • License

Installation

Bundler

Add this line to your application's Gemfile:

gem "open_router_enhanced"

And then execute:

bundle install

Gem install

Or install it directly:

gem install open_router_enhanced

Quick Start

1. Get Your API Key

2. Basic Setup

require "open_router"

# Configure the gem
OpenRouter.configure do |config|
  config.access_token = ENV["OPENROUTER_API_KEY"]
  config.site_name = "Your App Name"
  config.site_url = "https://yourapp.com"
end

# Create a client
client = OpenRouter::Client.new

# Basic completion
response = client.complete([
  { role: "user", content: "What is the capital of France?" }
])

puts response.content
# => "The capital of France is Paris."

3. Enhanced Features Example

# Smart model selection with capabilities
model = OpenRouter::ModelSelector.new
  .require(:function_calling)
  .optimize_for(:cost)
  .choose

# Define a tool
weather_tool = OpenRouter::Tool.define do
  name "get_weather"
  description "Get current weather"
  parameters do
    string :location, required: true
  end
end

# Define a schema for structured output
weather_schema = OpenRouter::Schema.define("weather") do
  string :location, required: true
  number :temperature, required: true
  string :conditions, required: true
end

# Use everything together
response = client.complete(
  [{ role: "user", content: "What's the weather in Tokyo?" }],
  model: model,
  tools: [weather_tool],
  response_format: weather_schema
)

# Process results
if response.has_tool_calls?
  weather_data = response.structured_output
  puts "Temperature in #{weather_data['location']}: #{weather_data['temperature']}°"
end

Configuration

Configure the gem globally (e.g., in an initializer):

OpenRouter.configure do |config|
  # Required
  config.access_token = ENV["OPENROUTER_API_KEY"]
  config.site_name = "Your App Name"
  config.site_url = "https://yourapp.com"

  # Optional: Response healing for non-native structured output models
  config.auto_heal_responses = true
  config.healer_model = "openai/gpt-4o-mini"
  config.max_heal_attempts = 2

  # Optional: Strict mode for capability validation
  config.strict_mode = true

  # Optional: Request timeout (default: 120 seconds)
  config.request_timeout = 120
end

Per-Client Configuration

client = OpenRouter::Client.new(
  access_token: ENV["OPENROUTER_API_KEY"],
  request_timeout: 240
)

Faraday Configuration

Configure Faraday middleware for retries, logging, etc:

require 'faraday/retry'

OpenRouter::Client.new(access_token: ENV["ACCESS_TOKEN"]) do |config|
  config.faraday do |f|
    f.request :retry, max: 2, interval: 0.05
    f.response :logger, ::Logger.new($stdout), { headers: true, bodies: true }
  end
end

Full configuration documentation

Features

Tool Calling

Enable AI models to call functions and interact with external APIs.

# Define a tool with the DSL
weather_tool = OpenRouter::Tool.define do
  name "get_weather"
  description "Get current weather for a location"

  parameters do
    string :location, required: true, description: "City name"
    string :units, enum: ["celsius", "fahrenheit"], default: "celsius"
  end
end

# Use in completion
response = client.complete(
  [{ role: "user", content: "What's the weather in London?" }],
  model: "anthropic/claude-3.5-sonnet",
  tools: [weather_tool]
)

# Handle tool calls
if response.has_tool_calls?
  response.tool_calls.each do |tool_call|
    result = fetch_weather(tool_call.arguments["location"])
    puts "Weather: #{result}"
  end
end

Complete Tool Calling Documentation

Structured Outputs

Get JSON responses that conform to specific schemas with automatic validation and healing.

# Define a schema
user_schema = OpenRouter::Schema.define("user") do
  string :name, required: true, description: "Full name"
  integer :age, required: true, minimum: 0, maximum: 150
  string :email, required: true
  boolean :premium, description: "Premium account status"
end

# Get structured response
response = client.complete(
  [{ role: "user", content: "Create a user: John Doe, 30, john@example.com" }],
  model: "openai/gpt-4o",
  response_format: user_schema
)

# Access parsed JSON
user = response.structured_output
puts "#{user['name']} (#{user['age']}) - #{user['email']}"

Key Features:

  • Ruby DSL for JSON schemas
  • Automatic healing for models without native support
  • Validation with detailed error reporting
  • Support for nested objects and arrays

Complete Structured Outputs Documentation

Smart Model Selection

Automatically choose the best model based on requirements.

# Find cheapest model with function calling
model = OpenRouter::ModelSelector.new
  .require(:function_calling)
  .optimize_for(:cost)
  .choose

# Advanced selection with multiple criteria
model = OpenRouter::ModelSelector.new
  .require(:function_calling, :vision)
  .within_budget(max_cost: 0.01)
  .min_context(50_000)
  .prefer_providers("anthropic", "openai")
  .optimize_for(:performance)
  .choose

# Get multiple options for fallbacks
models = OpenRouter::ModelSelector.new
  .require(:structured_outputs)
  .choose_with_fallbacks(limit: 3)
# => ["openai/gpt-4o-mini", "anthropic/claude-3-haiku", "google/gemini-flash"]

Available capabilities: :chat, :function_calling, :structured_outputs, :vision, :code_generation

Optimization strategies: :cost, :performance, :latest, :context

Complete Model Selection Documentation

Prompt Templates

Create reusable, parameterized prompts with variable interpolation.

# Basic template
translation_template = OpenRouter::PromptTemplate.new(
  template: "Translate '{text}' from {source_lang} to {target_lang}",
  input_variables: [:text, :source_lang, :target_lang]
)

response = client.complete(
  translation_template.to_messages(
    text: "Hello world",
    source_lang: "English",
    target_lang: "French"
  ),
  model: "openai/gpt-4o-mini"
)

# Few-shot learning template
classification_template = OpenRouter::PromptTemplate.new(
  prefix: "Classify the sentiment:",
  suffix: "Now classify: {text}",
  examples: [
    { text: "I love this!", sentiment: "positive" },
    { text: "This is terrible.", sentiment: "negative" }
  ],
  example_template: "Text: {text}\nSentiment: {sentiment}",
  input_variables: [:text]
)

Complete Prompt Templates Documentation

Streaming

Stream responses in real-time with callbacks and automatic response reconstruction.

streaming_client = OpenRouter::StreamingClient.new

# Set up callbacks
streaming_client
  .on_stream(:on_start) { |data| puts "Starting..." }
  .on_stream(:on_chunk) { |chunk| print chunk.content }
  .on_stream(:on_finish) { |response| puts "\nDone! Tokens: #{response.total_tokens}" }
  .on_stream(:on_error) { |error| puts "Error: #{error.message}" }

# Stream with automatic response accumulation
response = streaming_client.stream_complete(
  [{ role: "user", content: "Write a story about a robot" }],
  model: "openai/gpt-4o-mini",
  accumulate_response: true
)

puts "Final response: #{response.content}"

Complete Streaming Documentation

Usage Tracking

Track token usage, costs, and performance metrics.

# Enable tracking
client = OpenRouter::Client.new(track_usage: true)

# Make requests
3.times do
  response = client.complete(
    [{ role: "user", content: "Tell me a fact" }],
    model: "openai/gpt-4o-mini"
  )
end

# View statistics
tracker = client.usage_tracker
puts "Total requests: #{tracker.request_count}"
puts "Total tokens: #{tracker.total_tokens}"
puts "Total cost: $#{tracker.total_cost.round(4)}"

# Per-model breakdown
tracker.model_usage.each do |model, stats|
  puts "#{model}: #{stats[:total_tokens]} tokens, $#{stats[:cost].round(4)}"
end

# Print detailed report
tracker.print_summary

Complete Usage Tracking Documentation

Model Exploration

The gem includes rake tasks for exploring available models:

# View model summary with statistics
bundle exec rake models:summary

# Search by provider
bundle exec rake models:search provider=anthropic

# Search by capabilities and optimize for cost
bundle exec rake models:search capability=function_calling optimize=cost limit=10

# Advanced filtering
bundle exec rake models:search provider=anthropic capability=function_calling min_context=100000 max_cost=0.01

Available search parameters:

  • provider=name - Filter by provider
  • capability=cap1,cap2 - Required capabilities
  • optimize=strategy - Optimization (cost, performance, latest, context)
  • min_context=tokens - Minimum context length
  • max_cost=amount - Maximum cost per 1k tokens
  • newer_than=YYYY-MM-DD - Filter by release date
  • limit=N - Maximum results (default: 20)

Testing

Running Tests

# Run all tests
bundle exec rspec

# Run with documentation format
bundle exec rspec --format documentation

# Run specific test types
bundle exec rspec spec/unit/           # Unit tests only
bundle exec rspec spec/vcr/            # VCR integration tests (requires API key)

VCR Testing

The project includes VCR tests that record real API interactions:

# Set API key
export OPENROUTER_API_KEY="your_api_key"

# Run VCR tests
bundle exec rspec spec/vcr/

# Re-record cassettes
rm -rf spec/fixtures/vcr_cassettes/
bundle exec rspec spec/vcr/

Examples

Comprehensive examples for all features are available in the examples/ directory:

# Set your API key
export OPENROUTER_API_KEY="your_key_here"

# Run examples
ruby -I lib examples/basic_completion.rb
ruby -I lib examples/tool_calling_example.rb
ruby -I lib examples/structured_outputs_example.rb
ruby -I lib examples/model_selection_example.rb
ruby -I lib examples/streaming_example.rb

Troubleshooting

Common Issues

Authentication Errors

# Set your API key
export OPENROUTER_API_KEY="your_key_here"

# Or in code
OpenRouter.configure do |config|
  config.access_token = ENV["OPENROUTER_API_KEY"]
end

Model Selection Issues

# Use ModelSelector to find compatible models
model = OpenRouter::ModelSelector.new
  .require(:function_calling)
  .choose

Structured Output Issues

# Enable automatic healing
OpenRouter.configure do |config|
  config.auto_heal_responses = true
  config.healer_model = "openai/gpt-4o-mini"
end

Performance Issues

# Optimize configuration
client = OpenRouter::Client.new(
  request_timeout: 30,
  strict_mode: false,
  auto_heal_responses: false
)

Debug Mode

Enable detailed logging:

require 'logger'

OpenRouter.configure do |config|
  config.log_errors = true
  config.faraday do |f|
    f.response :logger, Logger.new($stdout), { headers: true, bodies: true, errors: true }
  end
end

Getting Help

  1. Check the documentation: Detailed docs in the docs/ directory
  2. Review examples: Working examples in examples/
  3. Enable debug mode: Turn on logging to see details
  4. Check OpenRouter status: OpenRouter Status
  5. Open an issue: GitHub Issues

Complete Troubleshooting Guide

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/estiens/open_router_enhanced.

Branch Strategy

We use a two-branch workflow:

  • main - Stable releases only. Protected branch.
  • dev - Active development. All PRs should target this branch.

Important: Always target your PRs to the dev branch, not main.

Development Setup

git clone https://github.com/estiens/open_router_enhanced.git
cd open_router_enhanced
bundle install
bundle exec rspec

For detailed contribution guidelines, see CONTRIBUTING.md.

Acknowledgments

This enhanced fork builds upon the excellent foundation laid by Obie Fernandez and the original OpenRouter Ruby gem. The original library was bootstrapped from the Anthropic gem by Alex Rudall.

We extend our heartfelt gratitude to:

  • Obie Fernandez - Original OpenRouter gem author
  • Alex Rudall - Creator of the Anthropic gem foundation
  • The OpenRouter Team - For creating an amazing unified AI API
  • The Ruby Community - For continuous support and contributions

Consulting

I'm available for consulting on Ruby AI applications, LLM integration, and building production-ready AI systems. My work extends beyond Ruby to include real-time AI orchestration, character-based AI systems, multi-agent architectures, and low-latency voice/streaming applications.

Get in touch:

License

The gem is available as open source under the terms of the MIT License.