Class: RubyLlmMesh::Cache::RedisStore

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

Overview

Redis-backed store for distributed semantic cache entries. Requires the optional redis gem — soft-loaded only when configured.

Constant Summary collapse

INDEX_KEY =
"ruby_llm_mesh:semantic_cache:index"
ENTRY_PREFIX =
"ruby_llm_mesh:semantic_cache:entry:"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url:) ⇒ RedisStore

Returns a new instance of RedisStore.



25
26
27
28
29
30
31
32
33
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 25

def initialize(url:)
  unless self.class.redis_available?
    raise ConfigurationError,
          "semantic cache backend :redis requires the optional `redis` gem — " \
          "add `gem \"redis\"` to your Gemfile or omit redis_url to use memory store"
  end

  @redis = ::Redis.new(url: url)
end

Class Method Details

.redis_available?Boolean

Returns:

  • (Boolean)


14
15
16
17
18
19
20
21
22
23
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 14

def self.redis_available?
  return @redis_available if defined?(@redis_available) && !@redis_available.nil?

  begin
    require "redis"
    @redis_available = true
  rescue LoadError
    @redis_available = false
  end
end

Instance Method Details

#allObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 35

def all
  ids = @redis.smembers(INDEX_KEY)
  stale = []
  entries = ids.filter_map do |id|
    raw = @redis.get("#{ENTRY_PREFIX}#{id}")
    unless raw
      stale << id
      next
    end

    data = JSON.parse(raw)
    MemoryStore::Entry.new(
      id: id,
      prompt: data["prompt"],
      embedding: data["embedding"],
      payload: data["payload"],
      expires_at: data["expires_at"] ? Time.at(data["expires_at"]) : nil
    )
  rescue JSON::ParserError
    stale << id
    nil
  end
  cleanup_stale_index!(stale)
  entries
end

#clear!Object



84
85
86
87
88
89
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 84

def clear!
  ids = @redis.smembers(INDEX_KEY)
  keys = ids.map { |id| "#{ENTRY_PREFIX}#{id}" }
  @redis.del(*keys) if keys.any?
  @redis.del(INDEX_KEY)
end

#sizeObject



91
92
93
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 91

def size
  all.length
end

#write(id:, prompt:, embedding:, payload:, ttl:) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ruby_llm_mesh/cache/redis_store.rb', line 61

def write(id:, prompt:, embedding:, payload:, ttl:)
  id ||= SecureRandom.uuid
  expires_at = ttl && ttl.positive? ? Time.now.to_i + ttl : nil
  data = JSON.generate(
    "prompt" => prompt,
    "embedding" => embedding,
    "payload" => payload,
    "expires_at" => expires_at
  )
  key = "#{ENTRY_PREFIX}#{id}"
  if ttl && ttl.positive?
    @redis.setex(key, ttl, data)
  else
    @redis.set(key, data)
  end
  begin
    @redis.sadd(INDEX_KEY, [id])
  rescue ArgumentError, TypeError, Redis::CommandError
    @redis.sadd(INDEX_KEY, id)
  end
  true
end