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.

[View source]

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: {} }]

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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)
[View source]

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

[View source]

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

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

#list(*args) ⇒ Object

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

def update
end