Class: Engram::UseCases::Recall

Inherits:
Object
  • Object
show all
Defined in:
lib/engram/use_cases/recall.rb

Overview

Embed a query and fetch the most relevant memories for a scope.

By default this is pure vector similarity (the store’s own ordering). When importance_weight or recency_weight are non-zero, it fetches a larger candidate pool and re-ranks by a composite score: similarity + importance + recency. With both weights at zero (the default) behaviour is identical to plain similarity search.

Constant Summary collapse

DEFAULT_HALFLIFE =

30 days, in seconds

30 * 24 * 60 * 60
DEFAULT_POOL_FACTOR =
4

Instance Method Summary collapse

Constructor Details

#initialize(store:, embedder:, importance_weight: 0.0, recency_weight: 0.0, recency_halflife: DEFAULT_HALFLIFE, pool_factor: DEFAULT_POOL_FACTOR, touch: false) ⇒ Recall

Returns a new instance of Recall.



15
16
17
18
19
20
21
22
23
24
# File 'lib/engram/use_cases/recall.rb', line 15

def initialize(store:, embedder:, importance_weight: 0.0, recency_weight: 0.0,
  recency_halflife: DEFAULT_HALFLIFE, pool_factor: DEFAULT_POOL_FACTOR, touch: false)
  @store = store
  @embedder = embedder
  @importance_weight = importance_weight.to_f
  @recency_weight = recency_weight.to_f
  @recency_halflife = recency_halflife.to_f
  @pool_factor = pool_factor
  @touch = touch
end

Instance Method Details

#call(query, scope:, limit: Engram.config.default_limit) ⇒ Object

Returns Array<Record>, most relevant first.

Raises:

  • (ArgumentError)


27
28
29
30
31
32
33
34
35
36
37
# File 'lib/engram/use_cases/recall.rb', line 27

def call(query, scope:, limit: Engram.config.default_limit)
  raise ArgumentError, "query must be a non-empty string" if query.to_s.strip.empty?

  embedding = @embedder.embed(query)
  pool_limit = reranking? ? limit * @pool_factor : limit
  pool = @store.search(embedding: embedding, scope: scope, limit: pool_limit)

  results = (reranking? ? rerank(pool, embedding) : pool).first(limit)
  touch(results) if @touch
  results
end