Soka Rails
Rails Integration for Soka AI Agent Framework
Features • Installation • Quick Start • Usage • Generators • Testing • Contributing
Soka Rails is a Rails integration package for the Soka AI Agent Framework, providing seamless integration with the Rails ecosystem. It follows Rails conventions and best practices, making it easy for developers to use AI Agents in their Rails applications.
Features
- 🚂 Native Rails Integration: Following Rails conventions and best practices
- 📁 Auto-loading Support: Automatically loads the
app/soka
directory - 🛠️ Generator Support: Quickly generate Agent and Tool templates
- ⚙️ Rails Configuration Integration: Uses Rails' configuration system
- 🧪 Rails Testing Integration: Seamless integration with RSpec
- 🔄 Rails Lifecycle Hooks: Integrates with Rails logging and error tracking
- 💾 Session Memory Support: Store conversation history in Rails sessions
- 🔐 Authentication Integration: Works with Rails authentication systems
- 🗣️ Custom Instructions: Customize agent personality and behavior
- 🎯 Dynamic Instructions: Generate instructions dynamically via methods
- 🌐 Multilingual Thinking: Support for reasoning in different languages
Installation
Add the following to your Gemfile:
gem 'soka-rails'
Then execute:
bundle install
Run the installation generator:
rails generate soka:install
This will generate:
-
config/initializers/soka.rb
- Main configuration file -
app/soka/agents/application_agent.rb
- Base Agent class -
app/soka/tools/application_tool.rb
- Base Tool class
Quick Start
1. Configure API Keys
# config/initializers/soka.rb
Soka::Rails.configure do |config|
config.ai do |ai|
ai.provider = ENV.fetch('SOKA_PROVIDER', :gemini)
ai.model = ENV.fetch('SOKA_MODEL', 'gemini-2.5-flash-lite')
ai.api_key = ENV['SOKA_API_KEY']
end
end
2. Create an Agent
rails generate soka:agent customer_support
# app/soka/agents/customer_support_agent.rb
class CustomerSupportAgent < ApplicationAgent
tool OrderLookupTool
tool UserInfoTool
end
3. Use in Controller
class ConversationsController < ApplicationController
def create
agent = CustomerSupportAgent.new
result = agent.run(params[:message])
render json: {
answer: result.final_answer,
status: result.status
}
end
end
Usage
Directory Structure
rails-app/
├── app/
│ └── soka/
│ ├── agents/ # Agent definitions
│ │ ├── application_agent.rb
│ │ └── customer_support_agent.rb
│ └── tools/ # Tool definitions
│ ├── application_tool.rb
│ └── order_lookup_tool.rb
└── config/
└── initializers/
└── soka.rb # Global configuration
Creating Agents
class WeatherAgent < ApplicationAgent
# Configure AI settings
provider :gemini
model 'gemini-2.5-flash-lite'
max_iterations 10
# Register tools
tool WeatherApiTool
tool LocationTool
# Custom instructions
# instructions "You are a helpful weather assistant"
# Dynamic instructions via method
# instructions :generate_instructions
# Multilingual thinking
# think_in 'zh-TW' # Think in Traditional Chinese
# Rails integration hooks
before_action :log_request
on_error :notify_error_service
private
def log_request(input)
Rails.logger.info "[WeatherAgent] Processing: #{input}"
end
def notify_error_service(error, context)
Rails.error.report(error, context: context)
:continue
end
# Example of dynamic instructions method
# def generate_instructions
# <<~PROMPT
# You are a weather assistant for #{Rails.env} environment.
# Current time: #{Time.zone.now}
# User location: #{request&.location&.city || 'Unknown'}
# PROMPT
# end
end
Creating Tools
class OrderLookupTool < ApplicationTool
desc "Look up order information"
params do
requires :order_id, String, desc: "Order ID"
optional :include_items, :boolean, desc: "Include order items", default: false
end
def call(order_id:, include_items: false)
order = Order.find_by(id: order_id)
return { error: "Order not found" } unless order
data = {
id: order.id,
status: order.status,
total: order.total,
created_at: order.created_at
}
data[:items] = order.items if include_items
data
end
end
Session Memory
class ConversationsController < ApplicationController
def create
# Load memory from session
memory = session[:conversation_memory] || []
agent = CustomerSupportAgent.new(memory: memory)
result = agent.run(params[:message])
# Update session memory
session[:conversation_memory] = agent.memory.to_a
render json: { answer: result.final_answer }
end
end
Custom Instructions and Dynamic Instructions
class CustomerSupportAgent < ApplicationAgent
# Static instructions
instructions <<~PROMPT
You are a friendly customer support agent.
Always be polite and helpful.
Use emojis when appropriate.
PROMPT
# OR Dynamic instructions via method
instructions :generate_context_aware_instructions
private
def generate_context_aware_instructions
business_hours = (9..17).include?(Time.zone.now.hour)
<<~PROMPT
You are a customer support agent for #{Rails.application.class.module_parent_name}.
Environment: #{Rails.env}
Current time: #{Time.zone.now.strftime('%Y-%m-%d %H:%M:%S %Z')}
Business hours: #{business_hours ? 'Yes' : 'No (after hours)'}
#{business_hours ? 'Provide immediate assistance.' : 'Inform about callback options.'}
PROMPT
end
end
Multilingual Thinking
class GlobalSupportAgent < ApplicationAgent
# Set default thinking language
think_in 'zh-TW' # Think in Traditional Chinese
# Or set dynamically at runtime
def initialize(options = {})
super
# Detect user's preferred language from session or request
self.think_in = detect_user_language
end
private
def detect_user_language
# Logic to detect language from session, user preferences, or request headers
session[:locale] || I18n.locale || 'en'
end
end
Event Streaming
class ConversationsController < ApplicationController
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
agent = CustomerSupportAgent.new
agent.run(params[:message]) do |event|
response.stream.write("event: #{event.type}\n")
response.stream.write("data: #{event.content.to_json}\n\n")
end
ensure
response.stream.close
end
end
Generators
Install Generator
rails generate soka:install
Agent Generator
# Basic usage
rails generate soka:agent weather
# With tools - automatically registers tools in the agent
rails generate soka:agent weather forecast temperature humidity
Creates app/soka/agents/weather_agent.rb
with a template structure.
When tools are specified, the generated agent will include them:
class WeatherAgent < ApplicationAgent
# Tool registration
tool ForecastTool
tool TemperatureTool
tool HumidityTool
# Custom instructions
# instructions "You are a weather expert. Always provide temperature in both Celsius and Fahrenheit."
# Dynamic instructions via method
# instructions :generate_instructions
# Multilingual thinking
# think_in 'en' # Supported: 'en', 'zh-TW', 'ja-JP', etc.
# Configuration
# max_iterations 10
end
Tool Generator
# Basic usage
rails generate soka:tool weather_api
# With parameters - automatically generates parameter definitions
rails generate soka:tool weather_api location:string units:string
Creates app/soka/tools/weather_api_tool.rb
with parameter definitions.
When parameters are specified, the generated tool will include them:
class WeatherApiTool < ApplicationTool
desc 'Description of your tool'
params do
requires :location, String, desc: 'Location'
requires :units, String, desc: 'Units'
end
def call(**params)
# Implement your tool logic here
end
end
Testing
RSpec Configuration
# spec/rails_helper.rb
require 'soka/rails/rspec'
RSpec.configure do |config|
config.include Soka::Rails::TestHelpers, type: :agent
config.include Soka::Rails::TestHelpers, type: :tool
end
Testing Agents
RSpec.describe WeatherAgent, type: :agent do
let(:agent) { described_class.new }
before do
mock_ai_response(
final_answer: "Today in Tokyo it's sunny with 28°C"
)
end
it "responds to weather queries" do
result = agent.run("What's the weather in Tokyo?")
expect(result).to be_successful
expect(result.final_answer).to include("28°C")
end
end
Testing Tools
RSpec.describe OrderLookupTool, type: :tool do
let(:tool) { described_class.new }
let(:order) { create(:order, id: "123", status: "delivered") }
it "finds existing orders" do
result = tool.call(order_id: order.id)
expect(result[:status]).to eq("delivered")
expect(result[:id]).to eq("123")
end
it "handles missing orders" do
result = tool.call(order_id: "nonexistent")
expect(result[:error]).to include("not found")
end
end
Rails-Specific Tools
RailsInfoTool
A built-in tool for accessing Rails application information:
class RailsInfoTool < ApplicationTool
desc "Get Rails application information"
params do
requires :info_type, String, desc: "Type of information",
validates: { inclusion: { in: %w[routes version environment config] } }
end
def call(info_type:)
case info_type
when 'routes'
# Returns application routes
when 'version'
# Returns Rails and Ruby versions
when 'environment'
# Returns environment information
when 'config'
# Returns safe configuration values
end
end
end
Configuration
Environment-based Configuration
Soka::Rails.configure do |config|
config.ai do |ai|
ai.provider = ENV.fetch('SOKA_PROVIDER', :gemini)
ai.model = ENV.fetch('SOKA_MODEL', 'gemini-2.5-flash-lite')
ai.api_key = ENV['SOKA_API_KEY']
end
config.performance do |perf|
perf.max_iterations = Rails.env.production? ? 10 : 5
end
end
Compatibility
- Ruby: >= 3.4
- Rails: >= 7.0
- Soka: >= 0.0.4
Contributing
We welcome all forms of contributions!
- Fork the project
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Please ensure:
- Add appropriate tests
- Update relevant documentation
- Follow Rails coding conventions
- Pass Rubocop checks
License
This project is licensed under the MIT License. See the LICENSE file for details.
Made with ❤️ for the Rails Community
Built on top of Soka AI Agent Framework
Created by Claude Code