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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/legion/extensions/agentic/self/fingerprint/client.rb +0 -8
- data/lib/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint.rb +44 -25
- data/lib/legion/extensions/agentic/self/version.rb +1 -1
- data/spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_partner_spec.rb +85 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9353f26016962ab841ebc5b4e5d717d1ba7b94d5c6ccd9da3be8ed8c7fdeb938
|
|
4
|
+
data.tar.gz: 5544eb5d69d9394c6f6071acfc4ea54e0a651f700f0d41ede5ac2f9e85be33bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
54
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
70
|
-
sample_count:
|
|
71
|
-
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
|
|
78
|
-
|
|
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
|
data/spec/legion/extensions/agentic/self/fingerprint/runners/cognitive_fingerprint_partner_spec.rb
ADDED
|
@@ -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.
|
|
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
|