Class: RubyLLM::SemanticCache::Scoped
- Inherits:
-
Object
- Object
- RubyLLM::SemanticCache::Scoped
- Defined in:
- lib/ruby_llm/semantic_cache/scoped.rb
Overview
Scoped cache wrapper for multi-tenant scenarios Each scoped instance maintains its own stores for true isolation
Defined Under Namespace
Classes: ScopedConfig
Instance Attribute Summary collapse
-
#namespace ⇒ Object
readonly
Returns the value of attribute namespace.
Instance Method Summary collapse
- #clear! ⇒ Object
- #delete(query, threshold: nil) ⇒ Object
- #exists?(query, threshold: nil) ⇒ Boolean
- #fetch(query, threshold: nil, ttl: nil, &block) ⇒ Object
-
#initialize(namespace:) ⇒ Scoped
constructor
A new instance of Scoped.
- #invalidate(query, threshold: nil, limit: 100) ⇒ Object
- #search(query, limit: 5) ⇒ Object
- #stats ⇒ Object
- #store(query:, response:, embedding: nil, metadata: {}, ttl: nil) ⇒ Object
- #wrap(chat, threshold: nil, ttl: nil, on_cache_hit: nil, max_messages: nil) ⇒ Object
Constructor Details
#initialize(namespace:) ⇒ Scoped
18 19 20 21 22 23 24 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 18 def initialize(namespace:) @namespace = namespace @vector_store = nil @cache_store = nil @hits = 0 @misses = 0 end |
Instance Attribute Details
#namespace ⇒ Object (readonly)
Returns the value of attribute namespace.
16 17 18 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 16 def namespace @namespace end |
Instance Method Details
#clear! ⇒ Object
119 120 121 122 123 124 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 119 def clear! vector_store.clear! cache_store.clear! @hits = 0 @misses = 0 end |
#delete(query, threshold: nil) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 89 def delete(query, threshold: nil) threshold ||= config.similarity_threshold = .generate(query) matches = vector_store.search(, limit: 1) return false unless matches.any? && matches.first[:similarity] >= threshold id = matches.first[:id] vector_store.delete(id) cache_store.delete(id) true end |
#exists?(query, threshold: nil) ⇒ Boolean
82 83 84 85 86 87 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 82 def exists?(query, threshold: nil) threshold ||= config.similarity_threshold = .generate(query) matches = vector_store.search(, limit: 1) matches.any? && matches.first[:similarity] >= threshold end |
#fetch(query, threshold: nil, ttl: nil, &block) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 26 def fetch(query, threshold: nil, ttl: nil, &block) raise ArgumentError, "Block required" unless block_given? threshold ||= config.similarity_threshold ttl ||= config.ttl_seconds = .generate(query) matches = vector_store.search(, limit: 1) if matches.any? && matches.first[:similarity] >= threshold @hits += 1 entry_data = cache_store.get(matches.first[:id]) return Serializer.deserialize(entry_data[:response]) if entry_data end @misses += 1 response = block.call store(query: query, response: response, embedding: , ttl: ttl) response end |
#invalidate(query, threshold: nil, limit: 100) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 102 def invalidate(query, threshold: nil, limit: 100) threshold ||= config.similarity_threshold = .generate(query) matches = vector_store.search(, limit: limit) count = 0 matches.each do |match| next unless match[:similarity] >= threshold vector_store.delete(match[:id]) cache_store.delete(match[:id]) count += 1 end count end |
#search(query, limit: 5) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 65 def search(query, limit: 5) = .generate(query) matches = vector_store.search(, limit: limit) matches.filter_map do |match| entry_data = cache_store.get(match[:id]) next unless entry_data { query: entry_data[:query], response: Serializer.deserialize(entry_data[:response]), similarity: match[:similarity], metadata: entry_data[:metadata] } end end |
#stats ⇒ Object
126 127 128 129 130 131 132 133 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 126 def stats { hits: @hits, misses: @misses, hit_rate: hit_rate, entries: cache_store.size } end |
#store(query:, response:, embedding: nil, metadata: {}, ttl: nil) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 48 def store(query:, response:, embedding: nil, metadata: {}, ttl: nil) ||= .generate(query) ttl ||= config.ttl_seconds entry = Entry.new( query: query, response: Serializer.serialize(response), embedding: , metadata: ) vector_store.add(entry.id, ) cache_store.set(entry.id, entry.to_h, ttl: ttl) entry end |
#wrap(chat, threshold: nil, ttl: nil, on_cache_hit: nil, max_messages: nil) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/ruby_llm/semantic_cache/scoped.rb', line 135 def wrap(chat, threshold: nil, ttl: nil, on_cache_hit: nil, max_messages: nil) # For scoped wrap, we create a middleware that uses this scoped instance ScopedMiddleware.new( self, chat, threshold: threshold, ttl: ttl, on_cache_hit: on_cache_hit, max_messages: ) end |