Module: Engram::EmbeddingMetadata
- Defined in:
- lib/engram/embedding_metadata.rb
Overview
Helpers for storing and validating embedding provenance under Engram's reserved metadata namespace.
Constant Summary collapse
- RESERVED_KEY =
"_engram"- EMBEDDING_KEY =
"embedding"
Class Method Summary collapse
- .attach(record, embedder: nil, embedding_metadata: nil) ⇒ Object
- .build(adapter:, dimensions:, model: nil, provider: nil) ⇒ Object
- .extract(metadata) ⇒ Object
- .fingerprint(metadata) ⇒ Object
- .for_embedder(embedder, embedding: nil) ⇒ Object
- .merge(metadata, embedding_metadata) ⇒ Object
- .search(store, embedding:, embedding_metadata:, scope:, limit:, kinds: nil) ⇒ Object
- .stringify_keys(value) ⇒ Object
- .unknown_embedding_metadata_keyword?(error) ⇒ Boolean
- .validate_query!(embedding, embedding_metadata) ⇒ Object
- .validate_record!(record, query_embedding, query_metadata) ⇒ Object
Class Method Details
.attach(record, embedder: nil, embedding_metadata: nil) ⇒ Object
37 38 39 40 41 42 |
# File 'lib/engram/embedding_metadata.rb', line 37 def attach(record, embedder: nil, embedding_metadata: nil) = stringify_keys( || (, embedding: record.)) || {} return record if .empty? record.with(metadata: merge(record., )) end |
.build(adapter:, dimensions:, model: nil, provider: nil) ⇒ Object
14 15 16 17 18 19 20 21 22 23 |
# File 'lib/engram/embedding_metadata.rb', line 14 def build(adapter:, dimensions:, model: nil, provider: nil) data = { "adapter" => adapter.to_s, "dimensions" => dimensions } data["provider"] = provider.to_s if provider data["model"] = model.to_s if model data["fingerprint"] = fingerprint(data) data end |
.extract(metadata) ⇒ Object
44 45 46 47 48 49 50 |
# File 'lib/engram/embedding_metadata.rb', line 44 def extract() = stringify_keys( || {}) reserved = [RESERVED_KEY] return {} unless reserved.is_a?(Hash) stringify_keys(reserved[EMBEDDING_KEY] || {}) end |
.fingerprint(metadata) ⇒ Object
114 115 116 117 |
# File 'lib/engram/embedding_metadata.rb', line 114 def fingerprint() relevant = stringify_keys().values_at("adapter", "provider", "model", "dimensions") Digest::SHA256.hexdigest(relevant.map { |value| value.nil? ? "" : value.to_s }.join("\0")) end |
.for_embedder(embedder, embedding: nil) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/engram/embedding_metadata.rb', line 25 def (, embedding: nil) return nil unless .respond_to?(:embedding_metadata) = stringify_keys(. || {}) return nil if .empty? dimensions = .respond_to?(:length) ? .length : ["dimensions"] = .merge("dimensions" => dimensions) if dimensions ["fingerprint"] = fingerprint() end |
.merge(metadata, embedding_metadata) ⇒ Object
52 53 54 55 56 |
# File 'lib/engram/embedding_metadata.rb', line 52 def merge(, ) Engram::ReservedMetadata.attach( , EMBEDDING_KEY, || {}, namespace_description: "embedding metadata" ) end |
.search(store, embedding:, embedding_metadata:, scope:, limit:, kinds: nil) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/engram/embedding_metadata.rb', line 58 def search(store, embedding:, embedding_metadata:, scope:, limit:, kinds: nil) store.search( embedding: , embedding_metadata: , scope: scope, limit: limit, kinds: kinds ) rescue ArgumentError => error raise unless (error) store.search(embedding: , scope: scope, limit: limit, kinds: kinds) end |
.stringify_keys(value) ⇒ Object
119 120 121 122 123 124 125 126 127 128 |
# File 'lib/engram/embedding_metadata.rb', line 119 def stringify_keys(value) case value when Hash value.each_with_object({}) do |(key, nested), out| out[key.to_s] = stringify_keys(nested) end else value end end |
.unknown_embedding_metadata_keyword?(error) ⇒ Boolean
130 131 132 133 |
# File 'lib/engram/embedding_metadata.rb', line 130 def (error) error..include?("unknown keyword: :embedding_metadata") || error..include?("unknown keyword: \"embedding_metadata\"") end |
.validate_query!(embedding, embedding_metadata) ⇒ Object
72 73 74 75 76 77 78 79 80 81 |
# File 'lib/engram/embedding_metadata.rb', line 72 def validate_query!(, ) = stringify_keys( || {}) return if .empty? expected = ["dimensions"] return unless expected && .respond_to?(:length) && .length != expected.to_i raise Engram::Error, "embedding dimension mismatch: query vector has #{.length} dimensions, metadata declares #{expected}" end |
.validate_record!(record, query_embedding, query_metadata) ⇒ Object
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 |
# File 'lib/engram/embedding_metadata.rb', line 83 def validate_record!(record, , ) if .respond_to?(:length) && record..respond_to?(:length) && .length != record..length raise Engram::Error, "embedding dimension mismatch: query vector has #{.length} dimensions, " \ "record #{record.id.inspect} has #{record..length}" end stored = extract(record.) return if stored.empty? stored_dimensions = stored["dimensions"] if stored_dimensions && record..respond_to?(:length) && record..length != stored_dimensions.to_i raise Engram::Error, "embedding metadata mismatch: record #{record.id.inspect} has #{record..length} dimensions, " \ "metadata declares #{stored_dimensions}" end = stringify_keys( || {}) return if .empty? conflicting_key = %w[adapter provider model dimensions fingerprint].find do |key| stored.key?(key) && .key?(key) && stored[key].to_s != [key].to_s end return unless conflicting_key raise Engram::Error, "embedding metadata mismatch for record #{record.id.inspect}: #{conflicting_key} " \ "#{stored[conflicting_key].inspect} does not match query #{[conflicting_key].inspect}" end |