Module: RubyLLM::Agents::Pricing::DataStore

Extended by:
DataStore
Included in:
DataStore
Defined in:
lib/ruby_llm/agents/pricing/data_store.rb

Overview

Centralized HTTP fetch + two-layer cache for all pricing sources.

Replaces the duplicated fetch_from_url / litellm_data / cache_expired? code previously copy-pasted across TranscriptionPricing, SpeechPricing, and ImageGenerator::Pricing.

Two-layer cache:

Layer 1: In-memory (per-process, instant)
Layer 2: Rails.cache (cross-process, survives restarts)

Thread-safety: All cache writes are protected by a Mutex.

Examples:

Fetch LiteLLM data

DataStore.litellm_data # => Hash of all models

Fetch Portkey data for a specific model

DataStore.portkey_data("openai", "gpt-4o") # => Hash

Refresh all caches

DataStore.refresh!

Constant Summary collapse

DEFAULT_CACHE_TTL =

24 hours

24 * 60 * 60
LITELLM_URL =
"https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
OPENROUTER_URL =
"https://openrouter.ai/api/v1/models"
HELICONE_URL =
"https://www.helicone.ai/api/llm-costs"
PORTKEY_BASE_URL =
"https://api.portkey.ai/model-configs/pricing"
LLMPRICING_BASE_URL =
"https://llmpricing.ai/api"

Instance Method Summary collapse

Instance Method Details

#cache_statsHash

Returns Cache statistics for each source.

Returns:

  • (Hash)

    Cache statistics for each source



126
127
128
129
130
131
132
133
134
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 126

def cache_stats
  {
    litellm: bulk_stats(:litellm),
    openrouter: bulk_stats(:openrouter),
    helicone: bulk_stats(:helicone),
    portkey: per_model_stats("portkey:"),
    llmpricing: per_model_stats("llmpricing:")
  }
end

#helicone_dataArray<Hash>

Returns Array of cost entries.

Returns:

  • (Array<Hash>)

    Array of cost entries



61
62
63
64
65
66
67
68
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 61

def helicone_data
  return nil unless source_enabled?(:helicone)

  fetch_bulk(:helicone, helicone_url) do |body|
    parsed = JSON.parse(body)
    parsed.is_a?(Array) ? parsed : (parsed["data"] || parsed["costs"] || [])
  end
end

#litellm_dataHash

Returns model_id => { pricing fields }.

Returns:

  • (Hash)

    model_id => { pricing fields }



46
47
48
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 46

def litellm_data
  fetch_bulk(:litellm, litellm_url) { |body| JSON.parse(body) }
end

#llmpricing_data(provider, model, input_tokens, output_tokens) ⇒ Hash?

Returns Pricing data.

Parameters:

  • provider (String)

    e.g., “OpenAI”

  • model (String)

    e.g., “gpt-4o”

  • input_tokens (Integer)

    Token count for cost calculation

  • output_tokens (Integer)

    Token count for cost calculation

Returns:

  • (Hash, nil)

    Pricing data



89
90
91
92
93
94
95
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 89

def llmpricing_data(provider, model, input_tokens, output_tokens)
  return nil unless source_enabled?(:llmpricing)

  cache_key = "llmpricing:#{provider}/#{model}"
  url = "#{llmpricing_base_url}/prices?provider=#{uri_encode(provider)}&model=#{uri_encode(model)}&input_tokens=#{input_tokens}&output_tokens=#{output_tokens}"
  fetch_per_model(cache_key, url)
end

#openrouter_dataArray<Hash>

Returns Array of model entries with pricing.

Returns:

  • (Array<Hash>)

    Array of model entries with pricing



51
52
53
54
55
56
57
58
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 51

def openrouter_data
  return nil unless source_enabled?(:openrouter)

  fetch_bulk(:openrouter, openrouter_url) do |body|
    parsed = JSON.parse(body)
    parsed.is_a?(Hash) ? (parsed["data"] || []) : parsed
  end
end

#portkey_data(provider, model) ⇒ Hash?

Returns Pricing data for this model.

Parameters:

  • provider (String)

    e.g., “openai”

  • model (String)

    e.g., “gpt-4o”

Returns:

  • (Hash, nil)

    Pricing data for this model



77
78
79
80
81
82
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 77

def portkey_data(provider, model)
  return nil unless source_enabled?(:portkey)

  cache_key = "portkey:#{provider}/#{model}"
  fetch_per_model(cache_key, "#{portkey_base_url}/#{provider}/#{model}")
end

#refresh!(source = :all) ⇒ Object

Clear caches and optionally re-fetch

Parameters:

  • source (Symbol) (defaults to: :all)

    :all, :litellm, :openrouter, :helicone, :portkey, :llmpricing



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/ruby_llm/agents/pricing/data_store.rb', line 104

def refresh!(source = :all)
  mutex.synchronize do
    case source
    when :all
      @bulk_cache = {}
      @bulk_fetched_at = {}
      @per_model_cache = {}
      @per_model_fetched_at = {}
    when :litellm, :openrouter, :helicone
      @bulk_cache&.delete(source)
      @bulk_fetched_at&.delete(source)
    when :portkey
      @per_model_cache&.reject! { |k, _| k.start_with?("portkey:") }
      @per_model_fetched_at&.reject! { |k, _| k.start_with?("portkey:") }
    when :llmpricing
      @per_model_cache&.reject! { |k, _| k.start_with?("llmpricing:") }
      @per_model_fetched_at&.reject! { |k, _| k.start_with?("llmpricing:") }
    end
  end
end