Project

soka-rails

0.0
A long-lived project that still receives updates
Soka Rails provides seamless integration between the Soka AI Agent Framework and Ruby on Rails applications, following Rails conventions for easy adoption.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

>= 7.0, < 9.0
~> 0.0.6
~> 2.6
 Project Readme

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!

  1. Fork the project
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. 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