SkillAgentSearch skills...

Raix

Ruby AI eXtensions

Install / Use

/learn @OlympiaAI/Raix
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Ruby AI eXtensions

What's Raix

Raix (pronounced "ray" because the x is silent) is a library that gives you everything you need to add discrete large-language model (LLM) AI components to your Ruby applications. Raix consists of proven code that has been extracted from Olympia, the world's leading virtual AI team platform, and probably one of the biggest and most successful AI chat projects written completely in Ruby.

Understanding how to use discrete AI components in otherwise normal code is key to productively leveraging Raix, and the subject of a book written by Raix's author Obie Fernandez, titled Patterns of Application Development Using AI. You can easily support the ongoing development of this project by buying the book at Leanpub.

Raix 2.0 is powered by RubyLLM, giving you unified access to OpenAI, Anthropic, Google Gemini, and dozens of other providers through OpenRouter. Note that you can use Raix to add AI capabilities to non-Rails applications as long as you include ActiveSupport as a dependency.

Chat Completions

Raix consists of three modules that can be mixed in to Ruby classes to give them AI powers. The first (and mandatory) module is ChatCompletion, which provides transcript and chat_completion methods.

class MeaningOfLife
  include Raix::ChatCompletion
end

>> ai = MeaningOfLife.new
>> ai.transcript << { user: "What is the meaning of life?" }
>> ai.chat_completion

=> "The question of the meaning of life is one of the most profound and enduring inquiries in philosophy, religion, and science.
    Different perspectives offer various answers..."

By default, Raix will automatically add the AI's response to the transcript. This behavior can be controlled with the save_response parameter, which defaults to true. You may want to set it to false when making multiple chat completion calls during the lifecycle of a single object (whether sequentially or in parallel) and want to manage the transcript updates yourself:

>> ai.chat_completion(save_response: false)

Transcript Format

The transcript accepts both abbreviated and standard OpenAI message hash formats. The abbreviated format, suitable for system, assistant, and user messages is simply a mapping of role => content, as shown in the example above.

transcript << { user: "What is the meaning of life?" }

As mentioned, Raix also understands standard OpenAI messages hashes. The previous example could be written as:

transcript << { role: "user", content: "What is the meaning of life?" }

One of the advantages of OpenRouter and the reason that it is used by default by this library is that it handles mapping message formats from the OpenAI standard to whatever other model you're wanting to use (Anthropic, Cohere, etc.)

Note that it's possible to override the current object's transcript by passing a messages array to chat_completion. This allows for multiple threads to share a single conversation context in parallel, by deferring when they write their responses back to the transcript.

chat_completion(openai: "gpt-4.1-nano", messages: [{ user: "What is the meaning of life?" }])

Predicted Outputs

Raix supports Predicted Outputs with the prediction parameter for OpenAI.

>> ai.chat_completion(openai: "gpt-4o", params: { prediction: })

Prompt Caching

Raix supports Anthropic-style prompt caching when using Anthropic's Claude family of models. You can specify a cache_at parameter when doing a chat completion. If the character count for the content of a particular message is longer than the cache_at parameter, it will be sent to Anthropic as a multipart message with a cache control "breakpoint" set to "ephemeral".

Note that there is a limit of four breakpoints, and the cache will expire within five minutes. Therefore, it is recommended to reserve the cache breakpoints for large bodies of text, such as character cards, CSV data, RAG data, book chapters, etc. Raix does not enforce a limit on the number of breakpoints, which means that you might get an error if you try to cache too many messages.

>> my_class.chat_completion(params: { cache_at: 1000 })
=> {
  "messages": [
    {
      "role": "system",
      "content": [
        {
          "type": "text",
          "text": "HUGE TEXT BODY LONGER THAN 1000 CHARACTERS",
          "cache_control": {
            "type": "ephemeral"
          }
        }
      ]
    },

JSON Mode

Raix supports JSON mode for chat completions, which ensures that the AI model's response is valid JSON. This is particularly useful when you need structured data from the model.

When using JSON mode with OpenAI models, Raix will automatically set the response_format parameter on requests accordingly, and attempt to parse the entire response body as JSON. When using JSON mode with other models (e.g. Anthropic) that don't support response_format, Raix will look for JSON content inside of <json> XML tags in the response, before falling back to parsing the entire response body. Make sure you tell the AI to reply with JSON inside of XML tags.

>> my_class.chat_completion(json: true)
=> { "key": "value" }

When using JSON mode with non-OpenAI providers, Raix automatically sets the require_parameters flag to ensure proper JSON formatting. You can also combine JSON mode with other parameters:

>> my_class.chat_completion(json: true, openai: "gpt-4o")
=> { "key": "value" }

before_completion Hook

The before_completion hook lets you intercept and modify chat completion requests before they're sent to the AI provider. This is useful for dynamic parameter resolution, logging, content filtering, PII redaction, and more.

Configuration Levels

Hooks can be configured at three levels, with later levels overriding earlier ones:

# Global level - applies to all chat completions
Raix.configure do |config|
  config.before_completion = ->(context) {
    # Return a hash of params to merge, or modify context.messages directly
    { temperature: 0.7 }
  }
end

# Class level - applies to all instances of a class
class MyAssistant
  include Raix::ChatCompletion

  configure do |config|
    config.before_completion = ->(context) { { model: "gpt-4o" } }
  end
end

# Instance level - applies to a single instance
assistant = MyAssistant.new
assistant.before_completion = ->(context) { { max_tokens: 500 } }

When hooks exist at multiple levels, they're called in order (global → class → instance), with returned params merged together. Later hooks override earlier ones for the same parameter.

The CompletionContext Object

Hooks receive a CompletionContext object with access to:

context.chat_completion       # The ChatCompletion instance
context.messages              # Array of messages (mutable, in OpenAI format)
context.params                # Hash of params (mutable)
context.transcript            # The instance's transcript
context.current_model         # Currently configured model
context.chat_completion_class # The class including ChatCompletion
context.configuration         # The instance's configuration

Use Cases

Dynamic model selection from database:

Raix.configure do |config|
  config.before_completion = ->(context) {
    settings = TenantSettings.find_by(tenant: Current.tenant)
    {
      model: settings.preferred_model,
      temperature: settings.temperature,
      max_tokens: settings.max_tokens
    }
  }
end

PII redaction:

class SecureAssistant
  include Raix::ChatCompletion

  before_completion = ->(context) {
    context.messages.each do |msg|
      next unless msg[:content].is_a?(String)
      # Redact SSN patterns
      msg[:content] = msg[:content].gsub(/\d{3}-\d{2}-\d{4}/, "[SSN REDACTED]")
      # Redact email addresses
      msg[:content] = msg[:content].gsub(/[\w.-]+@[\w.-]+\.\w+/, "[EMAIL REDACTED]")
    end
    {} # Return empty hash if not modifying params
  }
end

Request logging:

Raix.configure do |config|
  config.before_completion = ->(context) {
    Rails.logger.info({
      event: "chat_completion_request",
      model: context.current_model,
      message_count: context.messages.length,
      params: context.params.except(:messages)
    }.to_json)
    {} # Return empty hash, just logging
  }
end

Adding system prompts:

assistant.before_completion = ->(context) {
  context.messages.unshift({
    role: "system",
    content: "Always be helpful and respectful."
  })
  {}
}

A/B testing models:

Raix.configure do |config|
  config.before_completion = ->(context) {
    if Flipper.enabled?(:new_model, Current.user)
      { model: "gpt-4o" }
    else
      { model: "gpt-4o-mini" }
    end
  }
end

Hooks can also be any object that responds to #call:

class CostTracker
  def call(context)
    # Track estimated cost based on message length
    estimated_tokens = context.messages.sum { |m| m[:content].to_s.length / 4 }
    StatsD.gauge("ai.estimated_input_tokens", estimated_tokens)
    {}
  end
end

Raix.configure do |config|
  config.before_completion = CostTracker.new
end

Use of Tools/Functions

The second (optional) module that you can add to your Ruby classes after ChatCompletion is FunctionDispatch. It lets you declare and implement functions to be called at the AI's discretion in a declarative, Rails-like "DSL" fashion.

When the AI responds with tool function calls instead of a text message, Raix automatically:

  1. Executes the requested tool functions
  2. Adds the function results to the conversation transcript
  3. Sends the updated tra
View on GitHub
GitHub Stars310
CategoryDevelopment
Updated1d ago
Forks28

Languages

Ruby

Security Score

90/100

Audited on Mar 25, 2026

No findings