Raix (Ruby AI eXtensions)

repository·main·Indexed 43 days ago

https://github.com/olympiaai/raix

A Ruby library for advanced AI integrations providing chat completions, prompt caching, and JSON mode. It features a declarative DSL for AI tools via FunctionDispatch, prompt chaining with PromptDeclarations, and support for Model Context Protocol (MCP) servers. Raix uses RubyLLM as its backend to route requests to OpenAI and OpenRouter.

Tokens
13K
Snippets
47
Records
57
Agent score
55%

What's inside Raix

  1. Intercept requests with the before_completion hook

    main

    The before_completion hook allows you to modify requests before they reach the AI provider. This is ideal for PII redaction, dynamic model selection, or logging.

    Hooks can be configured at three levels, applied in order (Global $\rightarrow$ Class $\rightarrow$ Instance). Later levels override earlier ones for the same parameter.

    Hook Return Values:

    • Return a Hash of parameters to merge into the request.
    • Or, modify the context.messages array directly (e.g., for redaction or adding system prompts).

    The CompletionContext Object: Hooks receive a context object providing access to:

    • context.messages: The mutable array of messages (OpenAI format).
    • context.params: The mutable hash of request parameters.
    • context.current_model: The currently configured model.
    • context.transcript: The instance's transcript.
    • context.chat_completion: The ChatCompletion instance.
    • context.configuration: The instance's configuration.
    # Global level
    Raix.configure do |config|
      config.before_completion = ->(context) { { temperature: 0.7 } }
    end
    
    # Class level
    class MyAssistant
      include Raix::ChatCompletion
      configure do |config|
        config.before_completion = ->(context) { { model: "gpt-4o" } }
      end
    end
    
    # Instance level
    assistant = MyAssistant.new
    assistant.before_completion = ->(context) { { max_tokens: 500 } }
  2. Integrate remote MCP servers with Raix::MCP

    main

    The Raix::MCP module (experimental) allows connecting to remote Model Context Protocol (MCP) servers. When included, it automatically fetches tools from the server via tools/list, registers them as OpenAI-compatible function schemas, and provides proxy methods to call them via tools/call.

    To use it, include Raix::MCP along with Raix::ChatCompletion and Raix::FunctionDispatch.

    You can filter tools using the only: or except: options in the mcp DSL.

    class FilteredMcpConsumer
      include Raix::ChatCompletion
      include Raix::FunctionDispatch
      include Raix::MCP
    
      # Only include specific tools
      mcp "https://server.example.com/sse", only: [:tool_one, :tool_two]
    
      # Or exclude specific tools
      mcp "https://server.example.com/sse", except: [:tool_to_exclude]
    end
  3. Implement Function Dispatch with Raix::FunctionDispatch

    main

    Include Raix::FunctionDispatch to allow the AI to call specific Ruby functions during a conversation.

    • Use the function keyword to declare a function name, description, and argument schema.
    • Use chat_completion(loop: true) to enable an autonomous loop where the AI can call multiple functions sequentially until it reaches a final text response.
    • To manually terminate the loop from within a function, call stop_tool_calls_and_respond!.
    class WhatIsTheWeather
      include Raix::ChatCompletion
      include Raix::FunctionDispatch
    
      function :check_weather, "Check the weather for a location", location: { type: "string" } do |args|
        "The weather in #{args[:location]} is hot and sunny"
      end
    end
    
    # To trigger the loop:
    # weather_ai.chat_completion(loop: true)
  4. Use the Raix::Predicate module for yes/no/maybe questions

    main

    The Raix::Predicate module allows you to handle AI responses to questions that can be answered with 'Yes', 'No', or 'Maybe'. You define handlers using yes?, no?, or maybe? blocks. Each block receives the full AI response (including the explanation) as an argument.

    Requirements:

    • You must define at least one handler.
    • If no handlers are defined, ask will raise a RuntimeError.
    • The AI response is expected to start with "Yes, ", "No, ", or "Maybe, ".
    class Question
      include Raix::Predicate
    
      yes? do |explanation|
        puts "Affirmative: #{explanation}"
      end
    
      no? do |explanation|
        puts "Negative: #{explanation}"
      end
    
      maybe? do |explanation|
        puts "Uncertain: #{explanation}"
      end
    end
    
    question = Question.new
    question.ask("Is Ruby a programming language?")
    # => Affirmative: Yes, Ruby is a dynamic, object-oriented programming language...
  5. Configure Raix provider

    main

    Configure your AI provider (OpenAI or OpenRouter) using the Raix.configure block. This is typically done in an initializer file like config/initializers/raix.rb.

    # config/initializers/raix.rb
    Raix.configure do |config|
      config.openrouter_client = OpenRouter::Client.new
    end
  6. Configure LLM providers via RubyLLM

    main

    Raix 2.0 uses RubyLLM as its backend. You must configure your API keys through RubyLLM.configure. Raix automatically routes models starting with gpt- or o1 to OpenAI, while all other models are routed through OpenRouter.

    # config/initializers/raix.rb
    RubyLLM.configure do |config|
      config.openrouter_api_key = ENV["OPENROUTER_API_KEY"]
      config.openai_api_key = ENV["OPENAI_API_KEY"]
    end
    RubyLLM.configure do |config|
      config.openrouter_api_key = ENV["OPENROUTER_API_KEY"]
      config.openai_api_key = ENV["OPENAI_API_KEY"]
    end
  7. Install Raix

    main

    To use Raix in your Ruby project, add the gem to your Gemfile or install it via the command line. Raix supports OpenAI and OpenRouter. It can be used in non-Rails applications as long as ActiveSupport is included.

    # Add to Gemfile
    gem "raix"
    
    # Or run via terminal
    gem install raix
  8. Use Raix::Predicate for LLM-based boolean checks

    main

    The Raix::Predicate module allows you to perform yes/no/maybe questions using AI chat completion. When included in a class, it provides DSL methods to define handlers for different response types.

    Responses are evaluated based on whether they start with yes, or no, . Any response that does not match these patterns is treated as a maybe.

    To use it, include the module, define your response blocks using yes?, no?, or maybe?, and then call ask(question) on an instance of the class.

    class Question
      include Raix::Predicate
    
      yes? do |explanation|
        puts "Yes: #{explanation}"
      end
    
      no? do |explanation|
        puts "No: #{explanation}"
      end
    
      maybe? do |explanation|
        puts "Maybe: #{explanation}"
      end
    end
    
    question = Question.new
    question.ask("Is Ruby a programming language?")
  9. Use the before_completion hook for request interception

    main

    The before_completion configuration option accepts a callable object (like a Proc) that executes before every chat completion request. This hook is useful for:

    • Dynamic parameter resolution
    • Logging request metadata
    • Content filtering
    • PII redaction (e.g., redacting SSN patterns)

    The hook receives a CompletionContext object, which allows you to either modify the context.messages directly or return a hash of parameters to be merged into the request.

  10. Convert between Raix and RubyLLM message formats

    main

    The TranscriptAdapter handles the translation between two distinct message representations:

    1. Raix Abbreviated Format: Used for easy construction. It uses the role as a key: { role: "content" } (e.g., { user: "hi" }).
    2. Standard/RubyLLM Format: Used for actual API calls. It uses the standard { role: "...", content: "..." } structure.

    Tool Messages: Note that messages involving tool_calls or tool_call_id are treated as special cases and maintain their full structure (including role, content, and tool_calls/tool_call_id keys) to ensure compatibility with LLM tool-calling requirements.

  11. Use CompletionContext in before_completion hooks

    main

    The Raix::CompletionContext object is passed to before_completion hooks. It provides access to the chat completion instance, the message history, and the request parameters.

    Crucially, the @messages and @params attributes are mutable, allowing hooks to perform tasks such as content filtering, PII redaction (e.g., SSN patterns), or modifying request parameters before the completion is generated. You can also access the conversation transcript, the current_model, and the underlying configuration through the context.

    # Example of how a hook might use the context to redact or modify data
    # (Note: The hook signature depends on how Raix implements the hook call)
    
    Raix.configure do |config|
      config.before_completion do |context|
        # Access messages to redact PII
        context.messages.each do |msg|
          msg[:content] = redact_ssn(msg[:content]) if msg[:role] == 'user'
        end
    
        # Access or modify parameters
        context.params[:temperature] = 0.5
    
        # Access transcript or model
        puts "Processing request for model: #{context.current_model}"
        puts "Transcript: #{context.transcript}"
      end
    end