LlmProviders

Gem Version License: MIT

複数の LLM プロバイダーに対応した軽量な統一インターフェース。依存は faraday のみ — ActiveSupport 不要。

English README

特徴

  • 軽量 — 依存は faraday のみ、ActiveSupport 不要
  • 統一インターフェース — 全プロバイダーで同じ API
  • ストリーミング — ブロック構文でリアルタイムトークンストリーミング
  • ツール呼び出し — プロバイダー間で一貫したツール/関数呼び出し
  • トークン追跡 — 全レスポンスに使用量統計(入力、出力、キャッシュ)

対応プロバイダー

プロバイダー 環境変数 デフォルトモデル
anthropic ANTHROPIC_API_KEY claude-sonnet-4-5-20250929
openai OPENAI_API_KEY gpt-5-mini
google GOOGLE_API_KEY gemini-2.5-flash
openrouter OPENROUTER_API_KEY anthropic/claude-sonnet-4.5

インストール

Gemfile に追加:

gem "llm_providers"

クイックスタート

require "llm_providers"

provider = LlmProviders::Providers.build(:anthropic)

# 同期
result = provider.chat(
  messages: [{ role: "user", content: "こんにちは!" }],
  system: "あなたは親切なアシスタントです。"
)
puts result[:content]

# ストリーミング
provider.chat(messages: [{ role: "user", content: "こんにちは!" }]) do |chunk|
  print chunk[:content]
end

使い方

設定

LlmProviders.configure do |config|
  config.logger = Rails.logger  # 任意の Logger インスタンス
end

プロバイダーオプション

provider = LlmProviders::Providers.build(
  :openai,
  model: "gpt-4.1",
  temperature: 0.7,
  max_tokens: 4096
)

ツール呼び出し

tools = [
  {
    name: "get_weather",
    description: "現在の天気を取得",
    parameters: {
      type: "object",
      properties: {
        location: { type: "string", description: "都市名" }
      },
      required: ["location"]
    }
  }
]

result = provider.chat(
  messages: [{ role: "user", content: "東京の天気は?" }],
  tools: tools
)

result[:tool_calls].each do |tc|
  puts "#{tc[:name]}: #{tc[:input]}"
end

レスポンス形式

chat は常に以下のハッシュを返します:

{
  content: "レスポンステキスト",
  tool_calls: [
    { id: "...", name: "...", input: {...} }
  ],
  usage: {
    input: 100,           # 入力トークン数
    output: 50,           # 出力トークン数
    cached_input: 80      # キャッシュされた入力トークン数(Anthropicのみ)
  },
  latency_ms: 1234,
  raw_response: {...}
}

OpenRouter

OpenRouter を使えば、単一の API で 300 以上のモデルにアクセスできます。この gem が直接サポートしていないプロバイダー(DeepSeek、Meta Llama、Mistral、Qwen など)に最適です。

provider = LlmProviders::Providers.build(
  :openrouter,
  model: "deepseek/deepseek-chat",
  app_name: "MyApp",
  app_url: "https://myapp.example.com",
  provider: { order: ["DeepSeek", "Together"], allow_fallbacks: true }
)

# 利用可能なモデル一覧
models = LlmProviders::Providers::Openrouter.models
models.each { |m| puts "#{m[:id]} (ctx: #{m[:context_length]})" }

エラーハンドリング

begin
  result = provider.chat(messages: messages)
rescue LlmProviders::ProviderError => e
  puts "エラー: #{e.message}"  # OpenRouter: "[DeepSeek] Model is unavailable"
  puts "コード: #{e.code}"     # 例: "anthropic_error", "openrouter_error"
end

サンプル

# 対話チャット
ANTHROPIC_API_KEY=your-key ruby examples/simple_chat.rb

# ワンショット
ANTHROPIC_API_KEY=your-key ruby examples/one_shot.rb "Hello!"

# ツール呼び出し
ANTHROPIC_API_KEY=your-key ruby examples/with_tools.rb

# 他のプロバイダー
OPENAI_API_KEY=your-key ruby examples/simple_chat.rb openai
GOOGLE_API_KEY=your-key ruby examples/simple_chat.rb google

# OpenRouter (DeepSeek, Llama, Mistral など)
OPENROUTER_API_KEY=your-key ruby examples/simple_chat.rb openrouter deepseek/deepseek-chat
OPENROUTER_API_KEY=your-key ruby examples/with_tools.rb openrouter meta-llama/llama-3.3-70b-instruct

ライセンス

MIT