Class: LlmMemory::RedisStore

Inherits:
Object
  • Object
show all
Includes:
Store
Defined in:
lib/llm_memory/stores/redis_store.rb

Instance Method Summary collapse

Methods included from Store

included

Constructor Details

#initialize(index_name: "llm_memory", content_key: "content", vector_key: "vector", metadata_key: "metadata") ⇒ RedisStore

Returns a new instance of RedisStore.



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/llm_memory/stores/redis_store.rb', line 11

def initialize(
  index_name: "llm_memory",
  content_key: "content",
  vector_key: "vector",
  metadata_key: "metadata"
)
  @index_name = index_name
  @content_key = content_key
  @vector_key = vector_key
  @metadata_key = 
  @client = Redis.new(url: LlmMemory.configuration.redis_url)
end

Instance Method Details

#add(data: []) ⇒ Object

data = [{ content: “”, content_vector: [], metadata: {} }]



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/llm_memory/stores/redis_store.rb', line 78

def add(data: [])
  result = {}
  @client.pipelined do |pipeline|
    data.each_with_index do |d, i|
      key = @index_name # index_name:create_time:metadata_timestamp:uuid
      timestamp = d.dig(:metadata, :timestamp)
      key += ":#{Time.now.strftime("%Y%m%d%H%M%S")}"
      key += ":#{timestamp}"
      key += ":#{SecureRandom.hex(8)}"

      meta_json = d[:metadata].nil? ? "" : d[:metadata].to_json # serialize
      vector_value = d[:vector].map(&:to_f).pack("f*")
      pipeline.hset(
        key,
        {
          @content_key => d[:content],
          @vector_key => vector_value,
          @metadata_key => meta_json
        }
      )
      result[key] = d[:content]
    end
  end
  result
# data.each_with_index do |d, i|
#   key = "#{@index_name}:#{i}"
#   vector_value = d[:content_vector].map(&:to_f).pack("f*")
#   pp vector_value
#   @client.hset(
#     key,
#     {
#       @content_key => d[:content],
#       @vector_key => vector_value,
#       @metadata_key => ""
#     }
#   )
# end
# rescue Redis::Pipeline::Error => e
#   # Handle the error if there is any issue with the pipeline execution
#   puts "Pipeline Error: #{e.message}"
# rescue Redis::BaseConnectionError => e
#   # Handle connection errors
#   puts "Connection Error: #{e.message}"
rescue => e
  # Handle any other errors
  puts "Unexpected Error: #{e.message}"
end

#create_index(dim: 1536, distance_metric: "COSINE") ⇒ Object

dimention: 1536 for ada-002



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/llm_memory/stores/redis_store.rb', line 51

def create_index(dim: 1536, distance_metric: "COSINE")
  # LangChain index
  # schema = (
  #   TextField(name=content_key),
  #   TextField(name=metadata_key),
  #   VectorField(
  #       vector_key,
  #       "FLAT",
  #       {
  #           "TYPE": "FLOAT32",
  #           "DIM": dim,
  #           "DISTANCE_METRIC": distance_metric,
  #       },
  #   ),
  # )
  command = [
    "FT.CREATE", @index_name, "ON", "HASH",
    "PREFIX", "1", "#{@index_name}:",
    "SCHEMA",
    @content_key, "TEXT",
    @metadata_key, "TEXT",
    @vector_key, "VECTOR", "FLAT", 6, "TYPE", "FLOAT32", "DIM", dim, "DISTANCE_METRIC", distance_metric
  ]
  @client.call(command)
end

#delete(key) ⇒ Object



126
127
128
# File 'lib/llm_memory/stores/redis_store.rb', line 126

def delete(key)
  @client.del(key) if @client.exists?(key)
end

#delete_allObject



130
131
132
133
134
# File 'lib/llm_memory/stores/redis_store.rb', line 130

def delete_all
  list.keys.each do |key|
    delete(key)
  end
end

#drop_indexObject



45
46
47
48
# File 'lib/llm_memory/stores/redis_store.rb', line 45

def drop_index
  # DD deletes all document hashes
  @client.call(["FT.DROPINDEX", @index_name, "DD"])
end

#get(key) ⇒ Object



136
137
138
# File 'lib/llm_memory/stores/redis_store.rb', line 136

def get(key)
  @client.hgetall(key)
end

#index_exists?Boolean

Returns:

  • (Boolean)


36
37
38
39
40
41
42
43
# File 'lib/llm_memory/stores/redis_store.rb', line 36

def index_exists?
  begin
    @client.call(["FT.INFO", @index_name])
  rescue
    return false
  end
  true
end

#infoObject



24
25
26
# File 'lib/llm_memory/stores/redis_store.rb', line 24

def info
  @client.call(["INFO"])
end

#list(*args) ⇒ Object



140
141
142
143
# File 'lib/llm_memory/stores/redis_store.rb', line 140

def list(*args)
  pattern = "#{@index_name}:#{args.first || "*"}"
  @client.keys(pattern)
end

#list_indexesObject



32
33
34
# File 'lib/llm_memory/stores/redis_store.rb', line 32

def list_indexes
  @client.call("FT._LIST")
end

#load_data(file_path) ⇒ Object



28
29
30
# File 'lib/llm_memory/stores/redis_store.rb', line 28

def load_data(file_path)
  CSV.read(file_path)
end

#search(query: [], k: 3) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/llm_memory/stores/redis_store.rb', line 148

def search(query: [], k: 3)
  packed_query = query.map(&:to_f).pack("f*")
  command = [
    "FT.SEARCH",
    @index_name,
    "*=>[KNN #{k} @vector $blob AS vector_score]",
    "PARAMS",
    2,
    "blob",
    packed_query,
    "SORTBY",
    "vector_score",
    "ASC",
    "LIMIT",
    0,
    k,
    "RETURN",
    3,
    "vector_score",
    @content_key,
    @metadata_key,
    "DIALECT",
    2
  ]
  response_list = @client.call(command)
  response_list.shift # the first one is the size
  # now [redis_key1, [],,, ]
  result = response_list.each_slice(2).to_h.values.map { |v|
    v.each_slice(2).to_h.transform_keys(&:to_sym)
  }
  result.each do |item|
    hash = JSON.parse(item[:metadata])
    item[:metadata] = hash.transform_keys(&:to_sym)
  end
  result
end

#updateObject



145
146
# File 'lib/llm_memory/stores/redis_store.rb', line 145

def update
end