lex-mesh 0.4.5 → 0.4.7

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: 0bd1055e60b5e6c6d2e63c08f7aab32c7f03b294b8251da6b583516060e3fdec
4
- data.tar.gz: aaee0b1ecd57d2e8673706de83ac842476b5aba0570a38ab57bce3ca6c17cee7
3
+ metadata.gz: cd84b8f23017907b298f8f567e7bfeeda0a0fdcc4a8105dcb8b0228e57042e4b
4
+ data.tar.gz: f207ee8b98551427165a196528377b6b57270ef1afeda079941a13918c4c907f
5
5
  SHA512:
6
- metadata.gz: 7e350395459a7499684bdc844fbd81b4a628f58e2656c2d68031e4ac3688ad954be2486e83023dbe54d2da0eca88e748133b187d6c9d6a9efc431564a0d9e4c5
7
- data.tar.gz: 1de8d318e631a5e31c2babb44a01424a0371332c05678dce832a55ac1e895963a6a571a3e7a78cf5b65d951c3f81f5b1ddd63fbf801edb078d46d13df5de6552
6
+ metadata.gz: 8d374bd0d78453bc8a0b1f3cfcde49a781fd1d43baf948ad56bbe0319b537329a19e0f397e76efc9341822d9d5fa57d8f528a710c7669f46e9d3eb407bede0ee
7
+ data.tar.gz: 4eeea99fe27e30c68febc9b5456ffaf2f1ed0637476362985ce5e6ec58526e9e9d967436b965135296d2997fea7b1547ea556742e81f078360cf3fa9daa651e4
@@ -6,7 +6,7 @@ module Legion
6
6
  module Extensions
7
7
  module Mesh
8
8
  module Actor
9
- class Gossip < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
9
+ class Gossip < Legion::Extensions::Actors::Every
10
10
  def runner_class
11
11
  Legion::Extensions::Mesh::Runners::Mesh
12
12
  end
@@ -6,7 +6,7 @@ module Legion
6
6
  module Extensions
7
7
  module Mesh
8
8
  module Actor
9
- class Heartbeat < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
9
+ class Heartbeat < Legion::Extensions::Actors::Every
10
10
  def runner_class
11
11
  Legion::Extensions::Mesh::Runners::Mesh
12
12
  end
@@ -6,7 +6,7 @@ module Legion
6
6
  module Extensions
7
7
  module Mesh
8
8
  module Actor
9
- class PendingExpiry < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
9
+ class PendingExpiry < Legion::Extensions::Actors::Every
10
10
  def runner_class
11
11
  Legion::Extensions::Mesh::Runners::Preferences
12
12
  end
@@ -6,7 +6,7 @@ module Legion
6
6
  module Extensions
7
7
  module Mesh
8
8
  module Actor
9
- class SilenceWatchdog < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
9
+ class SilenceWatchdog < Legion::Extensions::Actors::Every
10
10
  def runner_class
11
11
  Legion::Extensions::Mesh::Runners::Mesh
12
12
  end
@@ -103,7 +103,34 @@ module Legion
103
103
  def clear_preferences(owner_id:, source: nil)
104
104
  return { cleared: false, reason: :memory_not_available } unless memory_available?
105
105
 
106
- { cleared: true, owner_id: owner_id, source: source }
106
+ runner = memory_runner
107
+ result = runner.retrieve_by_domain(domain_tag: "owner:#{owner_id}")
108
+ traces = result[:traces] || []
109
+ traces.each { |t| runner.delete_trace(trace_id: t[:trace_id]) }
110
+ { cleared: true, owner_id: owner_id, source: source, count: traces.size }
111
+ rescue StandardError => e
112
+ { cleared: false, reason: :error, message: e.message }
113
+ end
114
+
115
+ def erase_partner!(identity:)
116
+ key = identity.to_s
117
+ @observation_counts&.delete(key) # rubocop:disable ThreadSafety/ClassInstanceVariable
118
+ @observation_signals&.delete(key) # rubocop:disable ThreadSafety/ClassInstanceVariable
119
+ @inferred_preferences&.delete(key) # rubocop:disable ThreadSafety/ClassInstanceVariable
120
+ clear_mesh_cache(agent_id: key)
121
+
122
+ traces_deleted = 0
123
+ if memory_available?
124
+ runner = memory_runner
125
+ result = runner.retrieve_by_domain(domain_tag: "owner:#{key}")
126
+ traces = result[:traces] || []
127
+ traces.each { |t| runner.delete_trace(trace_id: t[:trace_id]) }
128
+ traces_deleted = traces.size
129
+ end
130
+
131
+ { erased: true, identity: key, traces_deleted: traces_deleted }
132
+ rescue StandardError => e
133
+ { erased: false, identity: key, reason: :error, message: e.message }
107
134
  end
108
135
 
109
136
  def preference_instructions(profile:)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Mesh
6
+ module Transport
7
+ module Exchanges
8
+ class Fanout < Legion::Transport::Exchange
9
+ def exchange_name
10
+ 'amq.fanout'
11
+ end
12
+
13
+ def default_type
14
+ 'fanout'
15
+ end
16
+
17
+ def default_options
18
+ hash = super
19
+ hash[:passive] = true
20
+ hash
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -7,7 +7,7 @@ module Legion
7
7
  module Messages
8
8
  class Gossip < Legion::Transport::Message
9
9
  def exchange
10
- Legion::Transport::Exchanges::Node
10
+ Legion::Extensions::Mesh::Transport::Exchanges::Fanout
11
11
  end
12
12
 
13
13
  def routing_key
@@ -7,7 +7,7 @@ module Legion
7
7
  module Messages
8
8
  class MeshConflict < Legion::Transport::Message
9
9
  def exchange
10
- Legion::Transport::Exchanges::Node
10
+ Legion::Extensions::Mesh::Transport::Exchanges::Fanout
11
11
  end
12
12
 
13
13
  def routing_key
@@ -7,7 +7,7 @@ module Legion
7
7
  module Messages
8
8
  class MeshDeparture < Legion::Transport::Message
9
9
  def exchange
10
- Legion::Transport::Exchanges::Node
10
+ Legion::Extensions::Mesh::Transport::Exchanges::Fanout
11
11
  end
12
12
 
13
13
  def routing_key
@@ -11,11 +11,7 @@ module Legion
11
11
  end
12
12
 
13
13
  def exchange
14
- Legion::Transport::Exchanges::Node
15
- end
16
-
17
- def routing_key
18
- 'mesh.gossip'
14
+ Legion::Extensions::Mesh::Transport::Exchanges::Fanout
19
15
  end
20
16
  end
21
17
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Mesh
6
- VERSION = '0.4.5'
6
+ VERSION = '0.4.7'
7
7
  end
8
8
  end
9
9
  end
@@ -14,6 +14,7 @@ require 'legion/extensions/mesh/runners/delegation'
14
14
  require 'legion/extensions/mesh/runners/task_request'
15
15
 
16
16
  if Legion.const_defined?(:Transport, false)
17
+ require 'legion/extensions/mesh/transport/exchanges/fanout'
17
18
  require 'legion/extensions/mesh/transport/messages/preference_query'
18
19
  require 'legion/extensions/mesh/transport/messages/preference_response'
19
20
  require 'legion/extensions/mesh/transport/messages/mesh_departure'
@@ -12,12 +12,16 @@ unless defined?(Legion::Extensions::Agentic::Memory::Trace::Runners::Traces)
12
12
  module Runners
13
13
  module Traces
14
14
  def retrieve_by_domain(**)
15
- []
15
+ { count: 0, traces: [] }
16
16
  end
17
17
 
18
18
  def store_trace(**)
19
19
  { stored: true }
20
20
  end
21
+
22
+ def delete_trace(trace_id:, **)
23
+ { deleted: true, trace_id: trace_id }
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -107,6 +111,90 @@ RSpec.describe Legion::Extensions::Mesh::Helpers::PreferenceProfile do
107
111
  result = profile_mod.clear_preferences(owner_id: 'user1', source: 'explicit')
108
112
  expect(result[:cleared]).to be true
109
113
  end
114
+
115
+ it 'actually deletes traces via memory runner when memory is available' do
116
+ trace = { trace_id: 'abc-123', domain_tags: ['preference', 'owner:user1'], confidence: 0.9 }
117
+ runner_double = double('memory_runner')
118
+ allow(profile_mod).to receive(:memory_available?).and_return(true)
119
+ allow(profile_mod).to receive(:memory_runner).and_return(runner_double)
120
+ allow(runner_double).to receive(:retrieve_by_domain).and_return({ traces: [trace] })
121
+ allow(runner_double).to receive(:delete_trace).and_return({ deleted: true })
122
+
123
+ result = profile_mod.clear_preferences(owner_id: 'user1')
124
+
125
+ expect(runner_double).to have_received(:retrieve_by_domain).with(hash_including(domain_tag: 'owner:user1'))
126
+ expect(runner_double).to have_received(:delete_trace).with(hash_including(trace_id: 'abc-123'))
127
+ expect(result[:cleared]).to be true
128
+ expect(result[:count]).to eq(1)
129
+ end
130
+
131
+ it 'returns not_available when memory is unavailable' do
132
+ allow(profile_mod).to receive(:memory_available?).and_return(false)
133
+ result = profile_mod.clear_preferences(owner_id: 'user1')
134
+ expect(result[:cleared]).to be false
135
+ expect(result[:reason]).to eq(:memory_not_available)
136
+ end
137
+ end
138
+
139
+ describe '.erase_partner!' do
140
+ before do
141
+ described_class.clear_observations
142
+ described_class.clear_mesh_cache
143
+ end
144
+
145
+ it 'removes observation counts for the identity' do
146
+ 5.times { described_class.update_from_observation(owner_id: 'partner-x', signals: { channel: :cli, direct_address: false }) }
147
+ described_class.erase_partner!(identity: 'partner-x')
148
+ expect(described_class.observation_counts['partner-x']).to be_nil
149
+ end
150
+
151
+ it 'removes inferred preferences for the identity' do
152
+ 20.times { described_class.update_from_observation(owner_id: 'partner-x', signals: { channel: :cli, direct_address: false }) }
153
+ described_class.erase_partner!(identity: 'partner-x')
154
+ expect(described_class.inferred_preferences('partner-x')).to be_empty
155
+ end
156
+
157
+ it 'removes mesh cache entries for the identity' do
158
+ described_class.store_mesh_profile(
159
+ agent_id: 'partner-x',
160
+ profile: { verbosity: :concise },
161
+ source_agent_id: 'partner-x'
162
+ )
163
+ described_class.erase_partner!(identity: 'partner-x')
164
+ expect(described_class.cached_mesh_profile(agent_id: 'partner-x')).to be_nil
165
+ end
166
+
167
+ it 'deletes memory traces tagged with the identity' do
168
+ trace = { trace_id: 'xyz-789', domain_tags: ['preference', 'owner:partner-x'], confidence: 0.8 }
169
+ runner_double = double('memory_runner')
170
+ allow(described_class).to receive(:memory_available?).and_return(true)
171
+ allow(described_class).to receive(:memory_runner).and_return(runner_double)
172
+ allow(runner_double).to receive(:retrieve_by_domain).and_return({ traces: [trace] })
173
+ allow(runner_double).to receive(:delete_trace).and_return({ deleted: true })
174
+
175
+ described_class.erase_partner!(identity: 'partner-x')
176
+
177
+ expect(runner_double).to have_received(:delete_trace).with(hash_including(trace_id: 'xyz-789'))
178
+ end
179
+
180
+ it 'leaves data for other identities untouched' do
181
+ 3.times { described_class.update_from_observation(owner_id: 'partner-y', signals: { channel: :cli, direct_address: false }) }
182
+ described_class.store_mesh_profile(
183
+ agent_id: 'partner-y',
184
+ profile: { verbosity: :detailed },
185
+ source_agent_id: 'partner-y'
186
+ )
187
+ described_class.erase_partner!(identity: 'partner-x')
188
+ expect(described_class.observation_counts['partner-y']).to eq(3)
189
+ expect(described_class.cached_mesh_profile(agent_id: 'partner-y')).not_to be_nil
190
+ end
191
+
192
+ it 'returns a result hash with erased identity and counts' do
193
+ result = described_class.erase_partner!(identity: 'partner-x')
194
+ expect(result).to be_a(Hash)
195
+ expect(result[:erased]).to be true
196
+ expect(result[:identity]).to eq('partner-x')
197
+ end
110
198
  end
111
199
 
112
200
  describe '.store_mesh_profile' do
@@ -31,7 +31,7 @@ RSpec.describe Legion::Extensions::Mesh::Runners::Preferences do
31
31
  end)
32
32
  stub_const('Legion::Extensions::Mesh::Transport::Messages::PreferenceQuery',
33
33
  Class.new do
34
- def initialize(**_opts); end
34
+ def initialize(**opts); end
35
35
 
36
36
  def publish; end
37
37
  end)
@@ -14,17 +14,15 @@ unless defined?(Legion::Transport::Message)
14
14
  end
15
15
  end
16
16
 
17
- unless defined?(Legion::Transport::Exchanges::Node)
17
+ unless defined?(Legion::Transport::Exchange)
18
18
  module Legion
19
19
  module Transport
20
- module Exchanges
21
- class Node # rubocop:disable Lint/EmptyClass
22
- end
23
- end
20
+ class Exchange; end # rubocop:disable Lint/EmptyClass
24
21
  end
25
22
  end
26
23
  end
27
24
 
25
+ require 'legion/extensions/mesh/transport/exchanges/fanout'
28
26
  require 'legion/extensions/mesh/transport/messages/gossip'
29
27
 
30
28
  RSpec.describe Legion::Extensions::Mesh::Transport::Messages::Gossip do
@@ -36,8 +34,8 @@ RSpec.describe Legion::Extensions::Mesh::Transport::Messages::Gossip do
36
34
  )
37
35
  end
38
36
 
39
- it 'uses the node exchange' do
40
- expect(subject.exchange).to eq(Legion::Transport::Exchanges::Node)
37
+ it 'uses the fanout exchange' do
38
+ expect(subject.exchange).to eq(Legion::Extensions::Mesh::Transport::Exchanges::Fanout)
41
39
  end
42
40
 
43
41
  it 'routes to mesh.gossip' do
@@ -14,17 +14,15 @@ unless defined?(Legion::Transport::Message)
14
14
  end
15
15
  end
16
16
 
17
- unless defined?(Legion::Transport::Exchanges::Node)
17
+ unless defined?(Legion::Transport::Exchange)
18
18
  module Legion
19
19
  module Transport
20
- module Exchanges
21
- class Node # rubocop:disable Lint/EmptyClass
22
- end
23
- end
20
+ class Exchange; end # rubocop:disable Lint/EmptyClass
24
21
  end
25
22
  end
26
23
  end
27
24
 
25
+ require 'legion/extensions/mesh/transport/exchanges/fanout'
28
26
  require 'legion/extensions/mesh/transport/messages/mesh_conflict'
29
27
 
30
28
  RSpec.describe Legion::Extensions::Mesh::Transport::Messages::MeshConflict do
@@ -37,8 +35,8 @@ RSpec.describe Legion::Extensions::Mesh::Transport::Messages::MeshConflict do
37
35
  end
38
36
 
39
37
  describe '#exchange' do
40
- it 'returns the Node exchange' do
41
- expect(msg.exchange).to eq(Legion::Transport::Exchanges::Node)
38
+ it 'returns the Fanout exchange' do
39
+ expect(msg.exchange).to eq(Legion::Extensions::Mesh::Transport::Exchanges::Fanout)
42
40
  end
43
41
  end
44
42
 
@@ -14,17 +14,15 @@ unless defined?(Legion::Transport::Message)
14
14
  end
15
15
  end
16
16
 
17
- unless defined?(Legion::Transport::Exchanges::Node)
17
+ unless defined?(Legion::Transport::Exchange)
18
18
  module Legion
19
19
  module Transport
20
- module Exchanges
21
- class Node # rubocop:disable Lint/EmptyClass
22
- end
23
- end
20
+ class Exchange; end # rubocop:disable Lint/EmptyClass
24
21
  end
25
22
  end
26
23
  end
27
24
 
25
+ require 'legion/extensions/mesh/transport/exchanges/fanout'
28
26
  require 'legion/extensions/mesh/transport/messages/mesh_departure'
29
27
 
30
28
  RSpec.describe Legion::Extensions::Mesh::Transport::Messages::MeshDeparture do
@@ -36,8 +34,8 @@ RSpec.describe Legion::Extensions::Mesh::Transport::Messages::MeshDeparture do
36
34
  end
37
35
 
38
36
  describe '#exchange' do
39
- it 'returns the Node exchange' do
40
- expect(msg.exchange).to eq(Legion::Transport::Exchanges::Node)
37
+ it 'returns the Fanout exchange' do
38
+ expect(msg.exchange).to eq(Legion::Extensions::Mesh::Transport::Exchanges::Fanout)
41
39
  end
42
40
  end
43
41
 
@@ -12,17 +12,25 @@ unless defined?(Legion::Transport::Queue)
12
12
  end
13
13
  end
14
14
 
15
- unless defined?(Legion::Transport::Exchanges::Node)
15
+ unless defined?(Legion::Transport::Queue)
16
16
  module Legion
17
17
  module Transport
18
- module Exchanges
19
- class Node # rubocop:disable Lint/EmptyClass
20
- end
18
+ class Queue
19
+ def initialize(**); end
21
20
  end
22
21
  end
23
22
  end
24
23
  end
25
24
 
25
+ unless defined?(Legion::Transport::Exchange)
26
+ module Legion
27
+ module Transport
28
+ class Exchange; end # rubocop:disable Lint/EmptyClass
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'legion/extensions/mesh/transport/exchanges/fanout'
26
34
  require 'legion/extensions/mesh/transport/queues/gossip'
27
35
 
28
36
  RSpec.describe Legion::Extensions::Mesh::Transport::Queues::Gossip do
@@ -35,14 +43,8 @@ RSpec.describe Legion::Extensions::Mesh::Transport::Queues::Gossip do
35
43
  end
36
44
 
37
45
  describe '#exchange' do
38
- it 'returns Legion::Transport::Exchanges::Node' do
39
- expect(queue.exchange).to eq(Legion::Transport::Exchanges::Node)
40
- end
41
- end
42
-
43
- describe '#routing_key' do
44
- it "returns 'mesh.gossip'" do
45
- expect(queue.routing_key).to eq('mesh.gossip')
46
+ it 'returns the Fanout exchange' do
47
+ expect(queue.exchange).to eq(Legion::Extensions::Mesh::Transport::Exchanges::Fanout)
46
48
  end
47
49
  end
48
50
  end
data/spec/spec_helper.rb CHANGED
@@ -36,7 +36,7 @@ module Legion
36
36
  end
37
37
 
38
38
  Legion::Settings.loader.settings[:client] ||= {}
39
- Legion::Settings.loader.settings[:client][:name] = 'test-agent'
39
+ Legion::Settings.loader.settings[:client][:name] = 'test-agent' # rubocop:disable Legion/Llm/SettingsAccessPath
40
40
 
41
41
  require 'legion/extensions/mesh'
42
42
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-mesh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -164,6 +164,7 @@ files:
164
164
  - lib/legion/extensions/mesh/runners/mesh.rb
165
165
  - lib/legion/extensions/mesh/runners/preferences.rb
166
166
  - lib/legion/extensions/mesh/runners/task_request.rb
167
+ - lib/legion/extensions/mesh/transport/exchanges/fanout.rb
167
168
  - lib/legion/extensions/mesh/transport/messages/gossip.rb
168
169
  - lib/legion/extensions/mesh/transport/messages/mesh_conflict.rb
169
170
  - lib/legion/extensions/mesh/transport/messages/mesh_departure.rb