class RubyLLM::AgUI::Store::MemoryStore
An in-process run store.
It is correct for a single process and a reasonable default in development and tests. It is not shared between processes, so a host that runs more than one Puma worker, or retries on a different worker, must use a durable store (the optional Rails adapter’s ActiveRecord store) instead. Nothing here pretends otherwise.
Every public method holds the store’s lock for its whole body, so the reservation and the interrupt redemption are atomic: the first caller to answer an interrupt is the only one that records a decision. The private helpers assume the lock is already held and never take it themselves.
Public Class Methods
new(clock: nil)
click to toggle source
Calls superclass method
RubyLLM::AgUI::Store::Base::new
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 19 def initialize(clock: nil) super @records = {} @threads = {} @lock = Mutex.new end
Public Instance Methods
find(scope:, thread_id:, run_id:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 42 def find(scope:, thread_id:, run_id:) key = key_for(scope, thread_id, run_id) @lock.synchronize { @records[key] } end
find_interrupt(scope:, thread_id:, interrupt_id:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 67 def find_interrupt(scope:, thread_id:, interrupt_id:) raise InvalidInputError, 'interrupt_id is required.' if blank?(interrupt_id) @lock.synchronize { record_for_interrupt(scope, thread_id, interrupt_id)&.interrupt(interrupt_id) } end
latest_interrupted(scope:, thread_id:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 51 def latest_interrupted(scope:, thread_id:) @lock.synchronize { records_for(scope, thread_id).reverse.find(&:interrupted?) } end
latest_run(scope:, thread_id:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 47 def latest_run(scope:, thread_id:) @lock.synchronize { records_for(scope, thread_id).last } end
redeem_interrupt(scope:, thread_id:, interrupt_id:, decision:, now:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 73 def redeem_interrupt(scope:, thread_id:, interrupt_id:, decision:, now:) unless Interrupt::DECISIONS.include?(decision) raise ConfigurationError, "decision must be one of #{Interrupt::DECISIONS.join(', ')}" end @lock.synchronize { redeem(scope, thread_id, interrupt_id, decision, now) } end
reserve(scope:, thread_id:, run_id:, digest:)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 26 def reserve(scope:, thread_id:, run_id:, digest:) key = key_for(scope, thread_id, run_id) @lock.synchronize do existing = @records[key] next attach(existing, digest) if existing record = RunRecord.new( scope: scope, thread_id: thread_id, run_id: run_id, digest: digest, created_at: now ) @records[key] = record thread = thread_key(scope, thread_id) @threads[thread] = (@threads[thread] || []) + [run_id] Reservation.new(record: record, status: Reservation::CREATED) end end
save(record)
click to toggle source
# File lib/ruby_llm/ag_ui/store/memory_store.rb, line 55 def save(record) key = key_for(record.scope, record.thread_id, record.run_id) @lock.synchronize do stored = @records[key] raise InvalidRunIdentityError, "run #{record.run_id} was never reserved" if stored.nil? raise RunConflictError, conflict_message(record) if stored.digest != record.digest @records[key] = record end record end