class RubyLLM::Browser::InMemoryOperationStore

Thread-safe, in-process {OperationStore}.

This is a fixture for tests and single-process demos. It is not production ready: state disappears on restart and cannot synchronize across workers or hosts. Use a durable store for anything long-running.

Public Class Methods

new(clock: nil) click to toggle source
Calls superclass method
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 11
def initialize(clock: nil)
  super()
  @mutex = Mutex.new
  @operations = {}
  @clock = clock
end

Public Instance Methods

all() click to toggle source

Snapshot for tests and diagnostics.

# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 132
def all
  synchronize { @operations.values.dup }
end
attach_or_reserve(operation) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 29
def attach_or_reserve(operation)
  synchronize do
    existing = find_by_idempotency_key(operation.actor_id, operation.run_id, operation.idempotency_key)
    if existing
      raise InvalidInput, "idempotency key belongs to a different request" unless existing.request_digest == operation.request_digest

      return existing
    end

    reserve_locked(operation)
  end
end
cancel(id, actor_id:, run_id:, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 104
def cancel(id, actor_id:, run_id:, now:)
  synchronize do
    operation = @operations.fetch(id) { raise InvalidInput, "unknown operation" }
    assert_actor!(operation, actor_id, run_id)
    return operation if operation.terminal? || operation.cancel_requested?

    @operations[id] = operation.with_fence(operation.fence + 1, now: now).request_cancel(now: now)
  end
end
claim_start(operation, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 71
def claim_start(operation, now:)
  synchronize do
    stored = @operations.fetch(operation.id)
    operation.validate_update!(stored)
    raise InvalidInput, "operation was already started" unless stored.state == :reserved
    raise BudgetExhausted, "operation wall-clock budget exhausted" if stored.expired?(now)

    @operations[operation.id] = stored.advance(:starting, now: now)
  end
end
confirm_closed(operation, session_id:, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 93
def confirm_closed(operation, session_id:, now:)
  synchronize do
    stored = @operations.fetch(operation.id)
    assert_actor!(stored, operation.actor_id, operation.run_id)
    raise InvalidInput, "operation session cannot change" unless stored.session_id == session_id

    target = stored.cancel_requested? ? :cancelled : :closed
    @operations[operation.id] = stored.advance(target, now: now)
  end
end
fetch(id) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 42
def fetch(id)
  synchronize { @operations[id] }
end
fetch!(id) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 46
def fetch!(id)
  fetch(id) || raise(InvalidInput, "unknown operation #{id.inspect}")
end
lease(id, actor_id:, run_id:, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 50
def lease(id, actor_id:, run_id:, now:)
  synchronize do
    operation = @operations.fetch(id) { raise InvalidInput, "unknown operation #{id.inspect}" }
    assert_actor!(operation, actor_id, run_id)
    if operation.terminal?
      raise InvalidInput, "operation #{id} is already #{operation.state}"
    end

    leased = operation.with_fence(operation.fence + 1, now: now)
    @operations[id] = leased
  end
end
mark_cleanup_pending(id, actor_id:, run_id:, error_code: nil, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 114
def mark_cleanup_pending(id, actor_id:, run_id:, error_code: nil, now:)
  synchronize do
    operation = @operations.fetch(id) { raise InvalidInput, "unknown operation #{id.inspect}" }
    assert_actor!(operation, actor_id, run_id)
    updated = operation.advance(:cleanup_pending, now: now, error_code: error_code)
    @operations[id] = updated
  end
end
open_for(actor_id) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 123
def open_for(actor_id)
  synchronize { @operations.values.select { |operation| operation.actor_id == actor_id && operation.open? } }
end
open_operations() click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 127
def open_operations
  synchronize { @operations.values.select(&:open?) }
end
record_session(operation, session_id:, now:) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 82
def record_session(operation, session_id:, now:)
  synchronize do
    stored = @operations.fetch(operation.id)
    assert_actor!(stored, operation.actor_id, operation.run_id)
    raise InvalidInput, "operation session cannot change" if stored.session_id && stored.session_id != session_id

    target = stored.fence == operation.fence ? :active : :cleanup_pending
    @operations[operation.id] = stored.advance(target, session_id: session_id, now: now)
  end
end
reserve(operation) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 18
def reserve(operation)
  synchronize do
    if @operations.key?(operation.id)
      raise InvalidInput, "operation #{operation.id} is already reserved"
    end

    enforce_concurrency!(operation)
    @operations[operation.id] = operation
  end
end
update(operation) click to toggle source
# File lib/ruby_llm/browser/in_memory_operation_store.rb, line 63
def update(operation)
  synchronize do
    stored = @operations.fetch(operation.id) { raise InvalidInput, "unknown operation #{operation.id.inspect}" }
    operation.validate_update!(stored)
    @operations[operation.id] = operation
  end
end