lex-agentic-self 0.1.14 → 0.1.15

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: 53c28592aa4721c2cc7901452451b9836cad8d3ded37f83c2a8dc04aac3ba1f8
4
- data.tar.gz: 29a423e61504f68cf11fd8c234f29b0d4ff654e33ea5651fd2d7739656cfb9ab
3
+ metadata.gz: 9353f26016962ab841ebc5b4e5d717d1ba7b94d5c6ccd9da3be8ed8c7fdeb938
4
+ data.tar.gz: 5544eb5d69d9394c6f6071acfc4ea54e0a651f700f0d41ede5ac2f9e85be33bb
5
5
  SHA512:
6
- metadata.gz: 38e5b0225a70847a4fb4a8d2f323f1bf1174004f16643deca0fca3567970f16fb5ee722e5e974051b42aff17c7dbb4ccdf7d1c403651c9d4228f3dec4433f7ca
7
- data.tar.gz: 3bc0d255fb156a9ce05ac926ecd6c5ca15c57a0729c57de9b679ebb684748143e004bbb209433056b628b4de78f7fa0437393d428d810069b69cd3f0fa787ff0
6
+ metadata.gz: 125c49e963e0ed3832a81a277a4f71fe815fa7fbfbc8b264775205ea6b06085313fdfc716383b4364afcff1bdb9b2d5367904eeac1a20a93254dbbb1232b004d
7
+ data.tar.gz: c5c64a7caf5080f6b0a335985ece8aefeaacdb2fccd4096408e1902ba0ab73e1c3cc0beb4658f9d3fbd72a5ccb795aa3597df0928b768f06856163edac366486
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.15] - 2026-07-16
4
+ ### Added
5
+ - Per-partner identity fingerprint models: `CognitiveFingerprint` runner now accepts `partner_identity:` on all methods, routing each partner to an isolated `FingerprintEngine` instance via an in-memory registry.
6
+ - `erase_partner!(identity:)` removes the fingerprint model for a given partner identity; returns `{erased: true/false, identity:}`.
7
+ - `tracked_partners` returns the list of partner identities currently held in the registry.
8
+
3
9
  ## [0.1.14] - 2026-05-07
4
10
  ### Fixed
5
11
  - Identity fingerprint save now warns and returns false if local persistence disconnects after previously being available.
@@ -12,14 +12,6 @@ module Legion
12
12
  module Fingerprint
13
13
  class Client
14
14
  include Runners::CognitiveFingerprint
15
-
16
- def initialize(**)
17
- @fingerprint_engine = Helpers::FingerprintEngine.new
18
- end
19
-
20
- private
21
-
22
- attr_reader :fingerprint_engine
23
15
  end
24
16
  end
25
17
  end
@@ -10,26 +10,26 @@ module Legion
10
10
  include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
11
11
  Legion::Extensions::Helpers.const_defined?(:Lex, false)
12
12
 
13
- def record_observation(category:, value:, **)
13
+ def record_observation(category:, value:, partner_identity: nil, **)
14
14
  category = category.to_sym
15
- result = fingerprint_engine.record_observation(category: category, value: value.to_f)
15
+ result = engine_for(partner_identity).record_observation(category: category, value: value.to_f)
16
16
  log.debug "[cognitive_fingerprint] record category=#{category} " \
17
17
  "baseline=#{result[:baseline]&.round(4)} samples=#{result[:samples]}"
18
18
  result
19
19
  end
20
20
 
21
- def verify_identity(observations:, **)
21
+ def verify_identity(observations:, partner_identity: nil, **)
22
22
  parsed = Array(observations).map do |obs|
23
23
  { category: obs[:category].to_sym, value: obs[:value].to_f }
24
24
  end
25
- result = fingerprint_engine.verify_identity(observations: parsed)
25
+ result = engine_for(partner_identity).verify_identity(observations: parsed)
26
26
  log.info "[cognitive_fingerprint] verify score=#{result[:match_score]&.round(4)} " \
27
27
  "verdict=#{result[:verdict]}"
28
28
  result
29
29
  end
30
30
 
31
- def anomaly_check(category:, value:, **)
32
- result = fingerprint_engine.anomaly_check(category: category.to_sym, value: value.to_f)
31
+ def anomaly_check(category:, value:, partner_identity: nil, **)
32
+ result = engine_for(partner_identity).anomaly_check(category: category.to_sym, value: value.to_f)
33
33
  if result[:anomaly]
34
34
  log.warn "[cognitive_fingerprint] anomaly category=#{category} " \
35
35
  "deviation=#{result[:deviation]&.round(4)}"
@@ -37,45 +37,64 @@ module Legion
37
37
  result
38
38
  end
39
39
 
40
- def trait_profile(**)
41
- { profile: fingerprint_engine.trait_profile }
40
+ def trait_profile(partner_identity: nil, **)
41
+ { profile: engine_for(partner_identity).trait_profile }
42
42
  end
43
43
 
44
- def strongest_traits(top_n: 3, **)
45
- { traits: fingerprint_engine.strongest_traits(top_n.to_i) }
44
+ def strongest_traits(top_n: 3, partner_identity: nil, **)
45
+ { traits: engine_for(partner_identity).strongest_traits(top_n.to_i) }
46
46
  end
47
47
 
48
- def weakest_traits(top_n: 3, **)
49
- { traits: fingerprint_engine.weakest_traits(top_n.to_i) }
48
+ def weakest_traits(top_n: 3, partner_identity: nil, **)
49
+ { traits: engine_for(partner_identity).weakest_traits(top_n.to_i) }
50
50
  end
51
51
 
52
- def identity_confidence(**)
53
- confidence = fingerprint_engine.identity_confidence
54
- label = fingerprint_engine.identity_label
52
+ def identity_confidence(partner_identity: nil, **)
53
+ engine = engine_for(partner_identity)
54
+ confidence = engine.identity_confidence
55
+ label = engine.identity_label
55
56
  log.debug "[cognitive_fingerprint] confidence=#{confidence.round(4)} label=#{label}"
56
57
  { confidence: confidence, label: label }
57
58
  end
58
59
 
59
- def fingerprint_hash(**)
60
- { fingerprint_hash: fingerprint_engine.fingerprint_hash }
60
+ def fingerprint_hash(partner_identity: nil, **)
61
+ { fingerprint_hash: engine_for(partner_identity).fingerprint_hash }
61
62
  end
62
63
 
63
- def fingerprint_report(**)
64
- fingerprint_engine.fingerprint_report
64
+ def fingerprint_report(partner_identity: nil, **)
65
+ engine_for(partner_identity).fingerprint_report
65
66
  end
66
67
 
67
- def fingerprint_status(**)
68
+ def fingerprint_status(partner_identity: nil, **)
69
+ engine = engine_for(partner_identity)
68
70
  {
69
- trait_count: fingerprint_engine.trait_count,
70
- sample_count: fingerprint_engine.sample_count,
71
- label: fingerprint_engine.identity_label
71
+ trait_count: engine.trait_count,
72
+ sample_count: engine.sample_count,
73
+ label: engine.identity_label
72
74
  }
73
75
  end
74
76
 
77
+ def erase_partner!(identity:, **)
78
+ registry = fingerprint_registry
79
+ return { erased: false, identity: identity } unless registry.key?(identity)
80
+
81
+ registry.delete(identity)
82
+ log.info "[cognitive_fingerprint] erased partner identity=#{identity}"
83
+ { erased: true, identity: identity }
84
+ end
85
+
86
+ def tracked_partners
87
+ fingerprint_registry.keys.compact
88
+ end
89
+
75
90
  private
76
91
 
77
- def fingerprint_engine
78
- @fingerprint_engine ||= Helpers::FingerprintEngine.new
92
+ def engine_for(partner_identity)
93
+ fingerprint_registry[partner_identity] ||= Helpers::FingerprintEngine.new
94
+ end
95
+
96
+ def fingerprint_registry
97
+ @fingerprint_registry ||= {}
79
98
  end
80
99
  end
81
100
  end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Agentic
6
6
  module Self
7
- VERSION = '0.1.14'
7
+ VERSION = '0.1.15'
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/self/fingerprint/client'
4
+
5
+ RSpec.describe 'CognitiveFingerprint per-partner identity scoping' do
6
+ let(:client) { Legion::Extensions::Agentic::Self::Fingerprint::Client.new }
7
+
8
+ def seed(partner, category, value, count = 10)
9
+ count.times { client.record_observation(category: category, value: value, partner_identity: partner) }
10
+ end
11
+
12
+ describe 'isolation between partners' do
13
+ it 'observations for partner X are not visible to partner Y' do
14
+ seed('alice', :accuracy, 0.9)
15
+ result = client.fingerprint_status(partner_identity: 'bob')
16
+ expect(result[:trait_count]).to eq(0)
17
+ end
18
+
19
+ it 'partner X traits are visible when queried for X' do
20
+ seed('alice', :accuracy, 0.9)
21
+ result = client.fingerprint_status(partner_identity: 'alice')
22
+ expect(result[:trait_count]).to eq(1)
23
+ end
24
+
25
+ it 'two partners maintain independent baselines' do
26
+ seed('alice', :accuracy, 0.9)
27
+ seed('bob', :accuracy, 0.1)
28
+ alice_profile = client.trait_profile(partner_identity: 'alice')[:profile]
29
+ bob_profile = client.trait_profile(partner_identity: 'bob')[:profile]
30
+ expect(alice_profile[:accuracy]).to be > 0.5
31
+ expect(bob_profile[:accuracy]).to be < 0.5
32
+ end
33
+
34
+ it 'verify_identity for X never sees Y data' do
35
+ seed('alice', :accuracy, 0.9, 20)
36
+ result = client.verify_identity(observations: [{ category: :accuracy, value: 0.9 }],
37
+ partner_identity: 'bob')
38
+ expect(result[:verdict]).to eq(:insufficient_data)
39
+ end
40
+
41
+ it 'anomaly_check for X never uses Y baseline' do
42
+ seed('alice', :accuracy, 0.9, 10)
43
+ result = client.anomaly_check(category: :accuracy, value: 0.9, partner_identity: 'bob')
44
+ expect(result[:reason]).to eq(:no_baseline)
45
+ end
46
+ end
47
+
48
+ describe '#erase_partner!' do
49
+ before { seed('alice', :accuracy, 0.9) }
50
+
51
+ it 'removes all data for the named partner' do
52
+ client.erase_partner!(identity: 'alice')
53
+ result = client.fingerprint_status(partner_identity: 'alice')
54
+ expect(result[:trait_count]).to eq(0)
55
+ end
56
+
57
+ it 'leaves other partners untouched' do
58
+ seed('bob', :accuracy, 0.5)
59
+ client.erase_partner!(identity: 'alice')
60
+ result = client.fingerprint_status(partner_identity: 'bob')
61
+ expect(result[:trait_count]).to eq(1)
62
+ end
63
+
64
+ it 'returns a success hash' do
65
+ result = client.erase_partner!(identity: 'alice')
66
+ expect(result[:erased]).to eq(true)
67
+ expect(result[:identity]).to eq('alice')
68
+ end
69
+
70
+ it 'is a no-op for unknown partner and returns erased: false' do
71
+ result = client.erase_partner!(identity: 'nobody')
72
+ expect(result[:erased]).to eq(false)
73
+ end
74
+ end
75
+
76
+ describe 'registry introspection' do
77
+ it 'returns list of tracked partner identities' do
78
+ seed('alice', :accuracy, 0.9)
79
+ seed('bob', :creativity, 0.5)
80
+ partners = client.tracked_partners
81
+ expect(partners).to include('alice')
82
+ expect(partners).to include('bob')
83
+ end
84
+ end
85
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-agentic-self
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -367,6 +367,7 @@ files:
367
367
  - spec/legion/extensions/agentic/self/fingerprint/helpers/cognitive_trait_spec.rb
368
368
  - spec/legion/extensions/agentic/self/fingerprint/helpers/constants_spec.rb
369
369
  - spec/legion/extensions/agentic/self/fingerprint/helpers/fingerprint_engine_spec.rb
370
+ - spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_partner_spec.rb
370
371
  - spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_spec.rb
371
372
  - spec/legion/extensions/agentic/self/identity/actors/orphan_check_spec.rb
372
373
  - spec/legion/extensions/agentic/self/identity/client_spec.rb