Nous utilisons des cookies. Les cookies et le stockage du navigateur vous gardent connecté et mémorisent vos préférences. Avec votre accord, nous mesurons aussi l’utilisation du site. Vous pouvez à tout moment. Consultez notre politique relative aux cookies.

Ruby

Guides SDK

Ruby

Utilisez ruby-openai avec api14 : uri_base, chat, streaming, appel d'outils, sorties structurées, embeddings, gestion des erreurs et nouvelles tentatives.

Utilisez la gem ruby-openai avec api14. Définissez uri_base et utilisez des identifiants de modèle comme openai/gpt-5-mini.

Installation

gem install ruby-openai
export API14_API_KEY="sk-api14-..."

Configuration

ruby-openai ajoute sa version d'API (v1) à uri_base ; la base est donc https://api14.com/api et les requêtes vont vers https://api14.com/api/v1/....

require "openai"

client = OpenAI::Client.new(
  access_token: ENV.fetch("API14_API_KEY"),
  uri_base: "https://api14.com/api",
  request_timeout: 120
)

Dans une application Rails, configurez la gem une fois dans un initialiseur et créez les clients sans argument :

# config/initializers/openai.rb
OpenAI.configure do |config|
  config.access_token = ENV.fetch("API14_API_KEY")
  config.uri_base = "https://api14.com/api"
  config.request_timeout = 120 # secondes, 120 par défaut
end

client = OpenAI::Client.new

Chat

response = client.chat(
  parameters: {
    model: "openai/gpt-5-mini",
    messages: [{ role: "user", content: "Explain vector databases in two sentences." }]
  }
)
puts response.dig("choices", 0, "message", "content")

Streaming

Passez un proc comme stream ; il est appelé une fois par fragment :

client.chat(
  parameters: {
    model: "anthropic/claude-sonnet-4.5",
    messages: [{ role: "user", content: "Write a haiku about APIs." }],
    stream: proc { |chunk, _bytesize| print chunk.dig("choices", 0, "delta", "content") }
  }
)

Voir Streaming.

Appel d'outils

require "json"

tools = [{
  type: "function",
  function: {
    name: "get_weather",
    description: "Current weather for a city",
    parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] }
  }
}]
messages = [{ role: "user", content: "What's the weather in Istanbul?" }]

loop do
  response = client.chat(parameters: { model: "openai/gpt-5-mini", messages: messages, tools: tools })
  message = response.dig("choices", 0, "message")
  unless message["tool_calls"]
    puts message["content"]
    break
  end

  messages << message
  message["tool_calls"].each do |call|
    args = JSON.parse(call.dig("function", "arguments"))
    messages << { role: "tool", tool_call_id: call["id"], content: get_weather(args["city"]).to_json } # votre méthode
  end
end

Voir Appel d'outils.

Sortie structurée

response = client.chat(
  parameters: {
    model: "openai/gpt-5-mini",
    messages: [{ role: "user", content: "Extract: Ada Lovelace, born 1815 in London." }],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "person",
        strict: true,
        schema: {
          type: "object",
          properties: { name: { type: "string" }, born: { type: "integer" }, city: { type: "string" } },
          required: %w[name born city],
          additionalProperties: false
        }
      }
    }
  }
)
person = JSON.parse(response.dig("choices", 0, "message", "content"))

Voir Sorties structurées.

Image en entrée

require "base64"

image = Base64.strict_encode64(File.binread("photo.png"))

client.chat(
  parameters: {
    model: "google/gemini-2.5-flash",
    messages: [{
      role: "user",
      content: [
        { type: "text", text: "What is in this picture?" },
        { type: "image_url", image_url: { url: "data:image/png;base64,#{image}" } }
      ]
    }]
  }
)

Voir Vision et fichiers.

Paramètres de la passerelle

La gem transmet parameters tel quel : les champs de la passerelle fonctionnent donc directement :

client.chat(
  parameters: {
    model: "openai/gpt-5-mini",
    models: ["anthropic/claude-sonnet-4.5"], # solutions de secours
    provider: { sort: "throughput" },
    reasoning: { effort: "low" },
    messages: [{ role: "user", content: "Hi" }]
  }
)

Embeddings

response = client.embeddings(
  parameters: { model: "openai/text-embedding-3-small", input: ["The quick brown fox", "Lorem ipsum"] }
)
puts response.dig("data", 0, "embedding").length

Erreurs

Les erreurs HTTP sont levées sous forme de Faraday::Error ; le hash de réponse contient le statut, les en-têtes et le corps au format OpenAI :

begin
  client.chat(parameters: { model: "openai/gpt-5-mini", messages: [{ role: "user", content: "Hi" }] })
rescue Faraday::Error => e
  case e.response&.dig(:status)
  when 402 then warn "Balance too low, add funds (insufficient_balance)"
  when 429 then warn "Rate limited, retry after #{e.response[:headers]['retry-after']}s"
  else raise
  end
end

Voir Erreurs pour tous les codes.

Nouvelles tentatives avec backoff

ruby-openai ne réessaie pas par défaut. Un petit wrapper réessaie rate_limited, les 5xx et les délais dépassés, et laisse tranquilles daily_limit_reached et insufficient_balance :

def with_retry(attempts: 5)
  attempts.times do |i|
    return yield
  rescue Faraday::TimeoutError, Faraday::ConnectionFailed
    raise if i == attempts - 1
    sleep([2**i, 60].min + rand)
  rescue Faraday::Error => e
    status = e.response&.dig(:status).to_i
    retryable = status >= 500 || (status == 429 && !e.response[:body].to_s.include?("daily_limit_reached"))
    raise if !retryable || i == attempts - 1
    wait = e.response.dig(:headers, "retry-after")&.to_i || 2**i
    sleep([wait, 60].min + rand)
  end
end

with_retry { client.chat(parameters: { model: "openai/gpt-5-mini", messages: [{ role: "user", content: "Hi" }] }) }

Voir Limites de débit.

Toutes les capacités

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api14.com/api/v1",
  apiKey: process.env.API14_API_KEY,
});

const result = await client.chat.completions.create({
  "model": "openai/gpt-5-mini",
  "messages": [
    {
      "role": "user",
      "content": "Explain vector databases in two sentences."
    }
  ]
});
console.log(result.choices[0].message.content);