swarm_memory 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4216c2f9b10d9accb547d94709305359009d8b8a7440c3023c97d49cc66b5cd4
4
- data.tar.gz: 670b1af5ec7880caa86fbceed37a030537cd863ec6a32155c144f706f1f96172
3
+ metadata.gz: 6217058c4241029650e057e4f28c75eb1c6105f797657eb7c039eb307fe9bcab
4
+ data.tar.gz: a9f5d039fe4dd776dff184fce3a539bf40e3bfe9f150698b26847e2953bc5e46
5
5
  SHA512:
6
- metadata.gz: 747e338d2569036d590c783c674895fdb9420af52655e34a412605f4085ced7c0bc198bd70698df2c84689211846095e381622f6bbb139376c70e3d71b3e6871
7
- data.tar.gz: bf9c87e64f5e149d7dbb00157abd6ba39f005e16cf29304382235c1ede975a13a76d1afc72ce12e83dfcec072610536a53e7abc457d8e0a7dff0061892e29759
6
+ metadata.gz: 883afdf0f09de8425226e1ef1138b9f657de2a92b9efbb2b2262315479c3bc2a83542151a73d484ee632d6e5e5921e1f2232d7361d6c55457d530d982569e681
7
+ data.tar.gz: 963e4ba618cde5bdeb496815044b9c791fb68c9fb28f7543fcba75d93f77d095638d05417338132143b57dc0c6b845b7fad4f8853b433b845545272c36aa2e37
@@ -181,6 +181,9 @@ module SwarmMemory
181
181
 
182
182
  # Calculate hybrid scores combining semantic similarity and keyword matching
183
183
  #
184
+ # When keyword_score is 0 (no tag matches), falls back to pure semantic scoring
185
+ # to avoid penalizing results that have excellent semantic matches but no tag overlap.
186
+ #
184
187
  # @param results [Array<Hash>] Results with semantic :similarity scores
185
188
  # @param query_keywords [Array<String>] Keywords from query
186
189
  # @return [Array<Hash>] Results with updated :similarity (hybrid score) and debug info
@@ -189,8 +192,13 @@ module SwarmMemory
189
192
  semantic_score = result[:similarity]
190
193
  keyword_score = calculate_keyword_score(result, query_keywords)
191
194
 
192
- # Hybrid score: weighted combination
193
- hybrid_score = (@semantic_weight * semantic_score) + (@keyword_weight * keyword_score)
195
+ # Fallback to pure semantic when no keyword matches
196
+ # This prevents penalizing results with excellent semantic matches but no tag overlap
197
+ hybrid_score = if keyword_score.zero?
198
+ semantic_score
199
+ else
200
+ (@semantic_weight * semantic_score) + (@keyword_weight * keyword_score)
201
+ end
194
202
 
195
203
  # Update result with hybrid score and debug info
196
204
  result.merge(
@@ -18,7 +18,9 @@ module SwarmMemory
18
18
  #
19
19
  # @param adapter [Adapters::Base] Storage adapter
20
20
  # @param embedder [Embeddings::Embedder, nil] Optional embedder for semantic search
21
- def initialize(adapter:, embedder: nil)
21
+ # @param semantic_weight [Float, nil] Weight for semantic similarity in hybrid search (0.0-1.0)
22
+ # @param keyword_weight [Float, nil] Weight for keyword matching in hybrid search (0.0-1.0)
23
+ def initialize(adapter:, embedder: nil, semantic_weight: nil, keyword_weight: nil)
22
24
  raise ArgumentError, "adapter is required" unless adapter.is_a?(Adapters::Base)
23
25
 
24
26
  @adapter = adapter
@@ -26,7 +28,10 @@ module SwarmMemory
26
28
 
27
29
  # Create semantic index if embedder is provided
28
30
  @semantic_index = if embedder
29
- SemanticIndex.new(adapter: adapter, embedder: embedder)
31
+ index_options = { adapter: adapter, embedder: embedder }
32
+ index_options[:semantic_weight] = semantic_weight if semantic_weight
33
+ index_options[:keyword_weight] = keyword_weight if keyword_weight
34
+ SemanticIndex.new(**index_options)
30
35
  end
31
36
  end
32
37
 
@@ -84,6 +84,43 @@ module SwarmMemory
84
84
  @mode = value.to_sym
85
85
  end
86
86
 
87
+ # DSL method to set/get semantic weight for hybrid search
88
+ #
89
+ # Controls how much semantic (embedding) similarity affects search results.
90
+ # Default is 0.5 (50%). Set to 1.0 for pure semantic search.
91
+ #
92
+ # @param value [Float, nil] Weight between 0.0 and 1.0
93
+ # @return [Float, nil] Current semantic weight
94
+ #
95
+ # @example Pure semantic search (no keyword penalty)
96
+ # semantic_weight 1.0
97
+ # keyword_weight 0.0
98
+ def semantic_weight(value = nil)
99
+ if value.nil?
100
+ @adapter_options[:semantic_weight]
101
+ else
102
+ @adapter_options[:semantic_weight] = value.to_f
103
+ end
104
+ end
105
+
106
+ # DSL method to set/get keyword weight for hybrid search
107
+ #
108
+ # Controls how much keyword (tag) matching affects search results.
109
+ # Default is 0.5 (50%). Set to 0.0 to disable keyword matching.
110
+ #
111
+ # @param value [Float, nil] Weight between 0.0 and 1.0
112
+ # @return [Float, nil] Current keyword weight
113
+ #
114
+ # @example Disable keyword matching
115
+ # keyword_weight 0.0
116
+ def keyword_weight(value = nil)
117
+ if value.nil?
118
+ @adapter_options[:keyword_weight]
119
+ else
120
+ @adapter_options[:keyword_weight] = value.to_f
121
+ end
122
+ end
123
+
87
124
  # Check if memory is enabled
88
125
  #
89
126
  # @return [Boolean] True if adapter is configured with required options
@@ -110,10 +110,12 @@ module SwarmMemory
110
110
  # MemoryConfig object (from DSL)
111
111
  [config.adapter_type, config.adapter_options]
112
112
  elsif config.is_a?(Hash)
113
- # Hash (from YAML)
113
+ # Hash (from YAML) - symbolize keys for adapter compatibility
114
114
  adapter = (config[:adapter] || config["adapter"] || :filesystem).to_sym
115
- options = config.reject { |k, _v| k == :adapter || k == "adapter" || k == :mode || k == "mode" }
116
- [adapter, options]
115
+ options = config.reject { |k, _v| [:adapter, "adapter", :mode, "mode"].include?(k) }
116
+ # Symbolize keys so adapter receives keyword arguments correctly
117
+ symbolized_options = options.transform_keys { |k| k.to_s.to_sym }
118
+ [adapter, symbolized_options]
117
119
  else
118
120
  raise SwarmSDK::ConfigurationError, "Invalid memory configuration for #{agent_name}"
119
121
  end
@@ -125,7 +127,17 @@ module SwarmMemory
125
127
  raise SwarmSDK::ConfigurationError, "#{e.message} for agent #{agent_name}"
126
128
  end
127
129
 
128
- # Instantiate adapter with options
130
+ # Extract hybrid search weights and other SDK-level config (before passing to adapter)
131
+ # Keys are already symbolized at this point
132
+ semantic_weight = adapter_options.delete(:semantic_weight)
133
+ keyword_weight = adapter_options.delete(:keyword_weight)
134
+
135
+ # Remove other SDK-level threshold configs that shouldn't go to adapter
136
+ adapter_options.delete(:discovery_threshold)
137
+ adapter_options.delete(:discovery_threshold_short)
138
+ adapter_options.delete(:adaptive_word_cutoff)
139
+
140
+ # Instantiate adapter with options (weights removed, adapter doesn't need them)
129
141
  # Note: Adapter is responsible for validating its own requirements
130
142
  begin
131
143
  adapter = adapter_class.new(**adapter_options)
@@ -137,8 +149,13 @@ module SwarmMemory
137
149
  # Create embedder for semantic search
138
150
  embedder = Embeddings::InformersEmbedder.new
139
151
 
140
- # Create storage with embedder (enables semantic features)
141
- Core::Storage.new(adapter: adapter, embedder: embedder)
152
+ # Create storage with embedder and hybrid search weights
153
+ Core::Storage.new(
154
+ adapter: adapter,
155
+ embedder: embedder,
156
+ semantic_weight: semantic_weight,
157
+ keyword_weight: keyword_weight,
158
+ )
142
159
  end
143
160
 
144
161
  # Parse memory configuration
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SwarmMemory
4
- VERSION = "2.2.2"
4
+ VERSION = "2.2.3"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swarm_memory
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda