Class: RobotLab::Budget::Ledger
- Inherits:
-
Object
- Object
- RobotLab::Budget::Ledger
- Defined in:
- lib/robot_lab/budget/ledger.rb
Overview
Thread-safe reserve/reconcile ledger for tracking consumption against per-dimension limits (e.g. :tokens, :cost).
Call reserve! before doing billable work, so an already-exhausted
budget is caught before the work is attempted rather than after.
Once the work completes, reconcile! replaces the reservation with
the actual amount consumed (which may be more or less than what was
reserved — LLM call sizes aren't known in advance). release! drops
an unused reservation without recording any consumption.
A dimension with no configured limit is treated as unlimited:
reserve! never raises for it and remaining returns
Float::INFINITY.
Instance Attribute Summary collapse
-
#consumed ⇒ Object
readonly
Returns the value of attribute consumed.
-
#limits ⇒ Hash{Symbol=>Numeric}
readonly
Per-dimension ceilings.
Instance Method Summary collapse
-
#initialize(limits: {}, consumed: {}) ⇒ Ledger
constructor
A new instance of Ledger.
-
#reconcile!(key, reserved_amount, actual_amount) ⇒ void
Replaces a prior reservation with the actual amount consumed.
-
#release!(key, amount) ⇒ void
Drops a reservation without recording any consumption (e.g. the reserved work was skipped).
-
#remaining(key) ⇒ Numeric
Remaining budget for
key, floored at 0;Float::INFINITYwhen unlimited. -
#reserve!(key, amount) ⇒ void
Reserves
amountagainst +key+'s remaining budget.
Constructor Details
#initialize(limits: {}, consumed: {}) ⇒ Ledger
Returns a new instance of Ledger.
34 35 36 37 38 39 |
# File 'lib/robot_lab/budget/ledger.rb', line 34 def initialize(limits: {}, consumed: {}) @mutex = Mutex.new @limits = limits @consumed = Hash.new(0).merge(consumed) @reserved = Hash.new(0) end |
Instance Attribute Details
#consumed ⇒ Object (readonly)
Returns the value of attribute consumed.
29 |
# File 'lib/robot_lab/budget/ledger.rb', line 29 attr_reader :limits, :consumed |
#limits ⇒ Hash{Symbol=>Numeric} (readonly)
Returns per-dimension ceilings.
29 30 31 |
# File 'lib/robot_lab/budget/ledger.rb', line 29 def limits @limits end |
Instance Method Details
#reconcile!(key, reserved_amount, actual_amount) ⇒ void
This method returns an undefined value.
Replaces a prior reservation with the actual amount consumed.
69 70 71 72 73 74 |
# File 'lib/robot_lab/budget/ledger.rb', line 69 def reconcile!(key, reserved_amount, actual_amount) @mutex.synchronize do @reserved[key] = [0, @reserved[key] - reserved_amount].max @consumed[key] += actual_amount end end |
#release!(key, amount) ⇒ void
This method returns an undefined value.
Drops a reservation without recording any consumption (e.g. the reserved work was skipped).
82 83 84 |
# File 'lib/robot_lab/budget/ledger.rb', line 82 def release!(key, amount) @mutex.synchronize { @reserved[key] = [0, @reserved[key] - amount].max } end |
#remaining(key) ⇒ Numeric
Returns remaining budget for key, floored at 0; Float::INFINITY when unlimited.
88 89 90 91 92 93 94 95 |
# File 'lib/robot_lab/budget/ledger.rb', line 88 def remaining(key) @mutex.synchronize do limit = @limits[key] next Float::INFINITY unless limit [limit - @consumed[key] - @reserved[key], 0].max end end |
#reserve!(key, amount) ⇒ void
This method returns an undefined value.
Reserves amount against +key+'s remaining budget.
A no-op (never raises) when key has no configured limit.
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/robot_lab/budget/ledger.rb', line 49 def reserve!(key, amount) @mutex.synchronize do limit = @limits[key] next unless limit committed = @consumed[key] + @reserved[key] if committed + amount > limit raise BudgetExceeded, "budget exceeded for #{key}: #{committed + amount} > #{limit}" end @reserved[key] += amount end end |