Class: RubyLlmMesh::Budget

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_llm_mesh/budget.rb

Constant Summary collapse

DEFAULT_PRICES =
{
  input_per_1k: 0.00015,
  output_per_1k: 0.0006
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config: RubyLlmMesh.configuration) ⇒ Budget

Returns a new instance of Budget.



12
13
14
15
16
17
# File 'lib/ruby_llm_mesh/budget.rb', line 12

def initialize(config: RubyLlmMesh.configuration)
  @config = config
  @tokens_consumed = 0
  @usd_consumed = 0.0
  @mutex = Mutex.new
end

Instance Attribute Details

#tokens_consumedObject (readonly)

Returns the value of attribute tokens_consumed.



10
11
12
# File 'lib/ruby_llm_mesh/budget.rb', line 10

def tokens_consumed
  @tokens_consumed
end

#usd_consumedObject (readonly)

Returns the value of attribute usd_consumed.



10
11
12
# File 'lib/ruby_llm_mesh/budget.rb', line 10

def usd_consumed
  @usd_consumed
end

Class Method Details

.compute_cost(usage, provider:, model:, config: RubyLlmMesh.configuration) ⇒ Object



94
95
96
97
98
99
100
101
# File 'lib/ruby_llm_mesh/budget.rb', line 94

def compute_cost(usage, provider:, model:, config: RubyLlmMesh.configuration)
  prices = resolve_prices(config, model)
  prompt_tokens = usage_value(usage, :prompt_tokens, :input_tokens)
  completion_tokens = usage_value(usage, :completion_tokens, :output_tokens)
  input_cost = (prompt_tokens / 1000.0) * prices[:input_per_1k]
  output_cost = (completion_tokens / 1000.0) * prices[:output_per_1k]
  input_cost + output_cost
end

.estimate_tokens(prompt:, system: nil, max_tokens: nil) ⇒ Object



75
76
77
78
79
80
# File 'lib/ruby_llm_mesh/budget.rb', line 75

def estimate_tokens(prompt:, system: nil, max_tokens: nil)
  text = [system, prompt].compact.join("\n")
  estimated = (text.length / 4.0).ceil
  estimated += max_tokens.to_i if max_tokens
  estimated
end

.estimate_usd(tokens:, model: nil, config: RubyLlmMesh.configuration) ⇒ Object



82
83
84
85
# File 'lib/ruby_llm_mesh/budget.rb', line 82

def estimate_usd(tokens:, model: nil, config: RubyLlmMesh.configuration)
  prices = resolve_prices(config, model)
  (tokens / 1000.0) * prices[:input_per_1k]
end

.extract_tokens(usage) ⇒ Object



103
104
105
# File 'lib/ruby_llm_mesh/budget.rb', line 103

def extract_tokens(usage)
  usage_value(usage, :total_tokens, :prompt_tokens, :completion_tokens, :input_tokens, :output_tokens)
end

.instance(config: RubyLlmMesh.configuration) ⇒ Object



66
67
68
69
# File 'lib/ruby_llm_mesh/budget.rb', line 66

def instance(config: RubyLlmMesh.configuration)
  @instances ||= {}
  @instances[config.object_id] ||= new(config: config)
end

.reset!Object



71
72
73
# File 'lib/ruby_llm_mesh/budget.rb', line 71

def reset!
  @instances = {}
end

.resolve_prices(config, model) ⇒ Object



87
88
89
90
91
92
# File 'lib/ruby_llm_mesh/budget.rb', line 87

def resolve_prices(config, model)
  table = config.budget_prices || {}
  entry = model ? table[model.to_s] || table[model.to_sym] : nil
  entry ||= table[:default] || table["default"]
  DEFAULT_PRICES.merge(entry || {})
end

Instance Method Details

#check!(estimated_tokens: 0, estimated_usd: 0.0) ⇒ Object



23
24
25
26
27
28
29
30
# File 'lib/ruby_llm_mesh/budget.rb', line 23

def check!(estimated_tokens: 0, estimated_usd: 0.0)
  return unless enabled?

  @mutex.synchronize do
    raise_budget_exceeded!("token", @tokens_consumed, @config.budget_max_tokens) if token_limit? && (@tokens_consumed + estimated_tokens) > @config.budget_max_tokens
    raise_budget_exceeded!("usd", @usd_consumed, @config.budget_max_usd) if usd_limit? && (@usd_consumed + estimated_usd) > @config.budget_max_usd
  end
end

#consume!(usage:, provider:, model:) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/ruby_llm_mesh/budget.rb', line 32

def consume!(usage:, provider:, model:)
  return unless enabled?

  tokens = self.class.extract_tokens(usage)
  usd = self.class.compute_cost(usage, provider: provider, model: model, config: @config)

  @mutex.synchronize do
    @tokens_consumed += tokens
    @usd_consumed += usd
    raise_budget_exceeded!("token", @tokens_consumed, @config.budget_max_tokens) if token_limit? && @tokens_consumed > @config.budget_max_tokens
    raise_budget_exceeded!("usd", @usd_consumed, @config.budget_max_usd) if usd_limit? && @usd_consumed > @config.budget_max_usd
  end
end

#enabled?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/ruby_llm_mesh/budget.rb', line 19

def enabled?
  @config.budget_enabled
end

#reset!Object



58
59
60
61
62
63
# File 'lib/ruby_llm_mesh/budget.rb', line 58

def reset!
  @mutex.synchronize do
    @tokens_consumed = 0
    @usd_consumed = 0.0
  end
end

#statusObject



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ruby_llm_mesh/budget.rb', line 46

def status
  {
    enabled: enabled?,
    tokens_consumed: @tokens_consumed,
    tokens_max: @config.budget_max_tokens,
    tokens_remaining: tokens_remaining,
    usd_consumed: @usd_consumed.round(6),
    usd_max: @config.budget_max_usd,
    usd_remaining: usd_remaining
  }
end