AI Lite

AI Lite is a pure Ruby, dependency-light client for simple OpenAI API calls.

It is built for Rails apps and plain Ruby projects where installing a full OpenAI SDK is too heavy, incompatible, or unnecessary. It is especially useful in legacy Rails apps where Rails, ActiveSupport, Ruby, or HTTP-client dependency constraints make larger client libraries difficult to add.

Use AI Lite when you want a small OpenAI client that works without tying your app to a specific Rails version or a larger dependency stack.

This gem is intentionally small:

  • No Rails dependency
  • No official OpenAI gem dependency
  • No Faraday, HTTParty, ActiveSupport, or connection pool dependency
  • Uses only Ruby stdlib: Net::HTTP, URI, JSON, and Base64

It is not meant to replace the official OpenAI SDK. It is a small wrapper for projects that only need a few clean interfaces:

ai.chat("Say hello")
ai.moderate("User submitted text")

Usage

require "ai_lite"

ai = AiLite.new
result = ai.chat("Say hello")

puts result["content"]

By default, the client looks for an API key in OPENAI_API_KEY, then falls back to OPEN_AI_TOKEN.

You can also pass the key directly:

ai = AiLite.new(api_key: "sk-...")

Configuration

In Rails, configure the default client from an initializer:

# config/initializers/ai_lite.rb
AiLite.configure do |config|
  config.api_key = ENV["OPENAI_API_KEY"]
  config.model = "gpt-5.5"
  config.moderation_model = "omni-moderation-latest"
  config.timeout = 120
  config.max_output_tokens = 2000
end

Then use the configured singleton-style client:

result = AiLite.chat("Say hello")

You can still instantiate a separate client for another token:

client = AiLite.new(api_key: "sk-other-token")
result = client.chat("Say hello")

Chat

result = ai.chat(
  "Return JSON confirming whether this is valid.",
  instructions: "Return only minified JSON.",
  max_output_tokens: 500,
  options: {
    text: { verbosity: "low" }
  }
)

chat sends a POST request to /v1/responses with:

  • model
  • input
  • max_output_tokens
  • optional instructions
  • optional debug
  • optional extra options

The default model is gpt-5.5.

The OpenAI API URL is fixed to https://api.openai.com/v1/responses.

Moderation

Use moderate to classify text or images for potentially harmful content:

result = ai.moderate("User submitted text")

if result["content"]["flagged"]
  # hide, block, or route to review
end

moderate sends a POST request to /v1/moderations with:

  • model
  • input
  • optional extra options

The default moderation model is omni-moderation-latest.

You can pass an image URL:

result = ai.moderate(
  text: "Profile caption",
  image_url: "https://example.com/image.png"
)

Or a local image path:

result = ai.moderate(image_path: "tmp/upload.png")

Local images are read, base64 encoded, and sent as JSON data URLs. They do not use multipart uploads. Supported local image extensions are .gif, .jpeg, .jpg, .png, and .webp.

You can also pass OpenAI's raw moderation input shape directly:

result = ai.moderate([
  { type: "text", text: "Caption" },
  {
    type: "image_url",
    image_url: {
      url: "https://example.com/image.png"
    }
  }
])

Multi-Turn Chat

Responses include a response_id that can be passed back through options as previous_response_id:

first = ai.chat("Tell me a short joke.")

follow_up = ai.chat(
  "Explain why that is funny.",
  options: {
    previous_response_id: first["response_id"]
  }
)

puts follow_up["content"]

Return Shape

Methods return a hash envelope.

Text output:

{
  "content" => "Hello!",
  "response_id" => "resp_...",
  "status" => 200,
  "error" => nil,
  "raw" => nil
}

JSON-looking model output:

{
  "content" => { "valid" => true },
  "response_id" => "resp_...",
  "status" => 200,
  "error" => nil,
  "raw" => nil
}

Moderation output:

{
  "content" => {
    "flagged" => false,
    "categories" => { ... },
    "category_scores" => { ... }
  },
  "response_id" => "modr_...",
  "status" => 200,
  "error" => nil,
  "raw" => nil
}

Failure:

{
  "content" => nil,
  "response_id" => nil,
  "status" => 401,
  "error" => "Invalid API key",
  "raw" => nil
}

Pass debug: true to include the raw OpenAI response:

result = ai.chat("Say hello", debug: true)

{
  "content" => "Hello!",
  "response_id" => "resp_...",
  "status" => 200,
  "error" => nil,
  "raw" => { ... }
}

Development

Run the test suite:

ruby -Ilib:test test/ai_lite_test.rb