lex-cognitive-integration 0.1.0
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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +34 -0
- data/CLAUDE.md +128 -0
- data/Gemfile +13 -0
- data/README.md +52 -0
- data/lex-cognitive-integration.gemspec +31 -0
- data/lib/legion/extensions/cognitive_integration/client.rb +15 -0
- data/lib/legion/extensions/cognitive_integration/helpers/constants.rb +58 -0
- data/lib/legion/extensions/cognitive_integration/helpers/integrated_representation.rb +102 -0
- data/lib/legion/extensions/cognitive_integration/helpers/integration_engine.rb +159 -0
- data/lib/legion/extensions/cognitive_integration/helpers/modal_signal.rb +58 -0
- data/lib/legion/extensions/cognitive_integration/runners/cognitive_integration.rb +101 -0
- data/lib/legion/extensions/cognitive_integration/version.rb +9 -0
- data/lib/legion/extensions/cognitive_integration.rb +17 -0
- metadata +78 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 11519fb839b607ed2c297ae8aa0594f90fd43cdd6e743a23f8cbb5b118e11a20
|
|
4
|
+
data.tar.gz: 179f7b4e326e3e2ef62589d73e692ed7bcdc0c51c0afcc0d0148f624bde0f496
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 61d43bb8a7c10d7b3774168fbafda1df071b7a18cbd439b8d8a6322d478d07e2a9da7e5b7a6aad1ce625d13a2e2b85e110fe28694fcf7ae6b20e762dc8d8654c
|
|
7
|
+
data.tar.gz: ffc069e410f67fa2cfc135883e02a6a2951c8bf523e93f6c10b8a7498dadcb508b737dc22f56be9c297cd6ddcf04a00f477ec7bed226a3c6c6242ae0e77c5244
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.4
|
|
4
|
+
|
|
5
|
+
Style/Documentation:
|
|
6
|
+
Enabled: false
|
|
7
|
+
|
|
8
|
+
Naming/PredicateMethod:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
11
|
+
Metrics/ClassLength:
|
|
12
|
+
Max: 150
|
|
13
|
+
|
|
14
|
+
Metrics/MethodLength:
|
|
15
|
+
Max: 25
|
|
16
|
+
|
|
17
|
+
Metrics/AbcSize:
|
|
18
|
+
Max: 25
|
|
19
|
+
|
|
20
|
+
Metrics/ParameterLists:
|
|
21
|
+
Max: 8
|
|
22
|
+
MaxOptionalParameters: 8
|
|
23
|
+
|
|
24
|
+
Layout/HashAlignment:
|
|
25
|
+
EnforcedColonStyle: table
|
|
26
|
+
EnforcedHashRocketStyle: table
|
|
27
|
+
|
|
28
|
+
Metrics/BlockLength:
|
|
29
|
+
Exclude:
|
|
30
|
+
- 'spec/**/*'
|
|
31
|
+
|
|
32
|
+
Style/OneClassPerFile:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'spec/spec_helper.rb'
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# lex-cognitive-integration
|
|
2
|
+
|
|
3
|
+
**Level 3 Leaf Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
- **Gem**: `lex-cognitive-integration`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Models multi-modal cognitive binding — the process of unifying signals from different modalities (visual, auditory, semantic, proprioceptive, etc.) into coherent integrated representations. Signals are registered with a modality type. Integration binds a set of signals into an IntegratedRepresentation with a binding strength. Representations above the coherent threshold are considered stable; below the fragmented threshold they are degraded. Passive decay reduces binding strength over time. Disruption forcibly degrades a representation.
|
|
10
|
+
|
|
11
|
+
## Gem Info
|
|
12
|
+
|
|
13
|
+
| Field | Value |
|
|
14
|
+
|---|---|
|
|
15
|
+
| Gem name | `lex-cognitive-integration` |
|
|
16
|
+
| Version | `0.1.0` |
|
|
17
|
+
| Namespace | `Legion::Extensions::CognitiveIntegration` |
|
|
18
|
+
| Ruby | `>= 3.4` |
|
|
19
|
+
| License | MIT |
|
|
20
|
+
| GitHub | https://github.com/LegionIO/lex-cognitive-integration |
|
|
21
|
+
|
|
22
|
+
## File Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
lib/legion/extensions/cognitive_integration/
|
|
26
|
+
cognitive_integration.rb # Top-level require
|
|
27
|
+
version.rb # VERSION = '0.1.0'
|
|
28
|
+
client.rb # Client class
|
|
29
|
+
helpers/
|
|
30
|
+
constants.rb # Modalities, binding thresholds, decay rate, binding/confidence/quality labels
|
|
31
|
+
modal_signal.rb # ModalSignal value object
|
|
32
|
+
integrated_representation.rb # IntegratedRepresentation value object
|
|
33
|
+
integration_engine.rb # Engine: signals, representations, bind, decay, coherence
|
|
34
|
+
runners/
|
|
35
|
+
cognitive_integration.rb # Runner module
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Key Constants
|
|
39
|
+
|
|
40
|
+
| Constant | Value | Meaning |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `MAX_SIGNALS` | 500 | Signal store cap |
|
|
43
|
+
| `MAX_REPRESENTATIONS` | 200 | Representation cap |
|
|
44
|
+
| `MIN_SIGNALS_FOR_BINDING` | 2 | Minimum signals required to create a representation |
|
|
45
|
+
| `MODALITIES` | array | `[:visual, :auditory, :semantic, :proprioceptive, :interoceptive, :temporal, :spatial, :emotional, :linguistic]` |
|
|
46
|
+
| `DEFAULT_BINDING_STRENGTH` | 0.5 | Starting binding strength for new representations |
|
|
47
|
+
| `BINDING_DECAY_RATE` | 0.03 | Binding strength decrease per decay call |
|
|
48
|
+
| `COHERENT_THRESHOLD` | 0.6 | Binding strength above this = coherent |
|
|
49
|
+
| `FRAGMENTED_THRESHOLD` | 0.3 | Binding strength below this = fragmented |
|
|
50
|
+
| `BINDING_LABELS` | hash | `bound` (0.8+) through `unbound` |
|
|
51
|
+
| `CONFIDENCE_LABELS` | hash | Labels for representation confidence |
|
|
52
|
+
| `QUALITY_LABELS` | hash | Labels for signal quality |
|
|
53
|
+
|
|
54
|
+
## Helpers
|
|
55
|
+
|
|
56
|
+
### `ModalSignal`
|
|
57
|
+
|
|
58
|
+
A single-modality cognitive input.
|
|
59
|
+
|
|
60
|
+
- `initialize(modality:, content:, domain:, quality: 0.5, signal_id: nil)`
|
|
61
|
+
- `quality`, `modality`, `domain`
|
|
62
|
+
- `to_h`
|
|
63
|
+
|
|
64
|
+
### `IntegratedRepresentation`
|
|
65
|
+
|
|
66
|
+
A multi-modal binding of several signals.
|
|
67
|
+
|
|
68
|
+
- `initialize(signal_ids:, binding_strength: DEFAULT_BINDING_STRENGTH, representation_id: nil)`
|
|
69
|
+
- `reinforce!(amount)` — increases binding_strength, cap 1.0
|
|
70
|
+
- `disrupt!(amount)` — decreases binding_strength, floor 0.0
|
|
71
|
+
- `decay!(rate)` — decreases binding_strength by `BINDING_DECAY_RATE`
|
|
72
|
+
- `coherent?` — binding_strength >= `COHERENT_THRESHOLD`
|
|
73
|
+
- `fragmented?` — binding_strength < `FRAGMENTED_THRESHOLD`
|
|
74
|
+
- `binding_label`
|
|
75
|
+
- `to_h`
|
|
76
|
+
|
|
77
|
+
### `IntegrationEngine`
|
|
78
|
+
|
|
79
|
+
- `add_signal(modality:, content:, domain:, quality: 0.5)` — returns `{ added:, signal_id:, signal: }` or capacity error
|
|
80
|
+
- `remove_signal(signal_id:)` — removes from signal store
|
|
81
|
+
- `integrate(signal_ids:, binding_strength: DEFAULT_BINDING_STRENGTH)` — requires >= MIN_SIGNALS_FOR_BINDING; creates IntegratedRepresentation; returns `{ integrated:, representation_id:, representation: }`
|
|
82
|
+
- `integrate_by_modalities(modalities:)` — integrates all signals matching given modality list
|
|
83
|
+
- `integrate_all_salient(quality_threshold: 0.6)` — auto-integrates all signals above quality threshold
|
|
84
|
+
- `reinforce(representation_id:, amount: 0.1)` — strengthens binding
|
|
85
|
+
- `disrupt(representation_id:, amount: 0.1)` — weakens binding
|
|
86
|
+
- `decay_all!(rate: BINDING_DECAY_RATE)` — decays all representations
|
|
87
|
+
- `coherent_representations(limit: 20)` — filtered and sorted
|
|
88
|
+
- `fragmented_representations(limit: 20)` — filtered and sorted
|
|
89
|
+
- `signals_by_modality` — hash of modality -> signal array
|
|
90
|
+
- `integration_report` — full stats
|
|
91
|
+
|
|
92
|
+
## Runners
|
|
93
|
+
|
|
94
|
+
**Module**: `Legion::Extensions::CognitiveIntegration::Runners::CognitiveIntegration`
|
|
95
|
+
|
|
96
|
+
| Method | Key Args | Returns |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `add_signal` | `modality:`, `content:`, `domain:`, `quality: 0.5` | `{ success:, signal_id:, signal: }` |
|
|
99
|
+
| `remove_signal` | `signal_id:` | `{ success:, removed: }` |
|
|
100
|
+
| `integrate` | `signal_ids:`, `binding_strength: DEFAULT_BINDING_STRENGTH` | `{ success:, representation_id:, representation: }` |
|
|
101
|
+
| `integrate_by_modalities` | `modalities:` | `{ success:, representation_id:, representation: }` |
|
|
102
|
+
| `integrate_all_salient` | `quality_threshold: 0.6` | `{ success:, representations: [...] }` |
|
|
103
|
+
| `reinforce` | `representation_id:`, `amount: 0.1` | `{ success:, binding_strength: }` |
|
|
104
|
+
| `disrupt` | `representation_id:`, `amount: 0.1` | `{ success:, binding_strength: }` |
|
|
105
|
+
| `decay` | — | `{ success:, decayed: N }` |
|
|
106
|
+
| `signals_by_modality` | — | `{ success:, modalities: }` |
|
|
107
|
+
| `coherent_representations` | `limit: 20` | `{ success:, representations: }` |
|
|
108
|
+
| `integration_report` | — | Full report hash |
|
|
109
|
+
| `status` | — | `{ success:, report: }` |
|
|
110
|
+
|
|
111
|
+
Private: `integration_engine` — memoized `IntegrationEngine`. Logs via `log_debug` helper.
|
|
112
|
+
|
|
113
|
+
## Integration Points
|
|
114
|
+
|
|
115
|
+
- **`lex-memory`**: Coherent integrated representations are prime candidates for memory trace storage. Fragmented representations could be stored at lower strength. `lex-memory`'s Hebbian linking can strengthen associations between signals that appear together in the same representation.
|
|
116
|
+
- **`lex-cognitive-hologram`**: Hologram reconstruction assembles fragments into a whole; integration binds multi-modal signals into a unified representation. Both model the process of assembling coherent wholes from parts.
|
|
117
|
+
- **`lex-emotion`**: Emotional signals (`:emotional` modality) can be integrated alongside semantic and linguistic signals to produce affectively grounded representations. `lex-emotion`'s valence can influence binding quality.
|
|
118
|
+
|
|
119
|
+
## Development Notes
|
|
120
|
+
|
|
121
|
+
- `integrate` requires at least `MIN_SIGNALS_FOR_BINDING` (2) signal IDs. Passing a single signal returns an error.
|
|
122
|
+
- `integrate_all_salient` groups signals by domain and integrates each domain's high-quality signals independently. This can produce many representations in one call.
|
|
123
|
+
- `decay_all!` operates on all representations regardless of coherence state. Coherent representations can decay into the fragmented range over time without periodic `reinforce` calls.
|
|
124
|
+
- In-memory only.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
**Maintained By**: Matthew Iverson (@Esity)
|
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# lex-cognitive-integration
|
|
2
|
+
|
|
3
|
+
Multi-modal cognitive binding engine for brain-modeled agentic AI in the LegionIO ecosystem.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
Models the binding of signals from different cognitive modalities (visual, auditory, semantic, proprioceptive, interoceptive, temporal, spatial, emotional, linguistic) into coherent integrated representations. Signals are registered individually; integration binds a set of them into a unified representation with a binding strength. Representations above 0.6 binding strength are considered coherent; below 0.3 they are fragmented. Passive decay reduces binding strength. Active reinforcement or disruption adjusts representations after formation.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require 'legion/extensions/cognitive_integration'
|
|
13
|
+
|
|
14
|
+
client = Legion::Extensions::CognitiveIntegration::Client.new
|
|
15
|
+
|
|
16
|
+
# Register signals from different modalities
|
|
17
|
+
visual = client.add_signal(modality: :visual, content: 'diagram of system architecture', domain: :engineering, quality: 0.8)
|
|
18
|
+
semantic = client.add_signal(modality: :semantic, content: 'microservice decomposition pattern', domain: :engineering, quality: 0.9)
|
|
19
|
+
|
|
20
|
+
# Bind them into an integrated representation
|
|
21
|
+
result = client.integrate(
|
|
22
|
+
signal_ids: [visual[:signal_id], semantic[:signal_id]],
|
|
23
|
+
binding_strength: 0.65
|
|
24
|
+
)
|
|
25
|
+
rep_id = result[:representation_id]
|
|
26
|
+
|
|
27
|
+
# Reinforce the binding
|
|
28
|
+
client.reinforce(representation_id: rep_id, amount: 0.1)
|
|
29
|
+
# => { success: true, binding_strength: 0.75 }
|
|
30
|
+
|
|
31
|
+
# Auto-integrate all high-quality signals
|
|
32
|
+
client.integrate_all_salient(quality_threshold: 0.7)
|
|
33
|
+
|
|
34
|
+
# Find coherent representations
|
|
35
|
+
client.coherent_representations(limit: 10)
|
|
36
|
+
|
|
37
|
+
# Status
|
|
38
|
+
client.status
|
|
39
|
+
# => { success: true, report: { signal_count: 2, representation_count: 2, ... } }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
bundle install
|
|
46
|
+
bundle exec rspec
|
|
47
|
+
bundle exec rubocop
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_integration/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-integration'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveIntegration::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'Cross-modal cognitive integration for LegionIO'
|
|
12
|
+
spec.description = 'Models convergence zones that bind information from different cognitive modalities ' \
|
|
13
|
+
'into unified multi-modal representations with binding strength and coherence tracking.'
|
|
14
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-integration'
|
|
15
|
+
spec.license = 'MIT'
|
|
16
|
+
|
|
17
|
+
spec.required_ruby_version = '>= 3.4'
|
|
18
|
+
|
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-cognitive-integration'
|
|
21
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-cognitive-integration/blob/master/README.md'
|
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-cognitive-integration/blob/master/CHANGELOG.md'
|
|
23
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-cognitive-integration/issues'
|
|
24
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
25
|
+
|
|
26
|
+
spec.files = Dir.chdir(__dir__) do
|
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
28
|
+
end
|
|
29
|
+
spec.require_paths = ['lib']
|
|
30
|
+
spec.add_development_dependency 'legion-gaia'
|
|
31
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveIntegration
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::CognitiveIntegration
|
|
8
|
+
|
|
9
|
+
def initialize(engine: nil)
|
|
10
|
+
@default_engine = engine || Helpers::IntegrationEngine.new
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveIntegration
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_SIGNALS = 500
|
|
9
|
+
MAX_REPRESENTATIONS = 200
|
|
10
|
+
MIN_SIGNALS_FOR_BINDING = 2
|
|
11
|
+
|
|
12
|
+
# Modality types
|
|
13
|
+
MODALITIES = %i[
|
|
14
|
+
visual auditory semantic emotional motor
|
|
15
|
+
proprioceptive temporal spatial contextual
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
# Binding dynamics
|
|
19
|
+
DEFAULT_BINDING_STRENGTH = 0.5
|
|
20
|
+
BINDING_DECAY_RATE = 0.03
|
|
21
|
+
REINFORCEMENT_RATE = 0.08
|
|
22
|
+
CONFLICT_PENALTY = 0.15
|
|
23
|
+
|
|
24
|
+
# Thresholds
|
|
25
|
+
COHERENT_THRESHOLD = 0.6
|
|
26
|
+
FRAGMENTED_THRESHOLD = 0.3
|
|
27
|
+
|
|
28
|
+
# Binding strength labels
|
|
29
|
+
BINDING_LABELS = {
|
|
30
|
+
(0.8..) => :tightly_bound,
|
|
31
|
+
(0.6...0.8) => :bound,
|
|
32
|
+
(0.4...0.6) => :loosely_bound,
|
|
33
|
+
(0.2...0.4) => :fragmentary,
|
|
34
|
+
(..0.2) => :unbound
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Signal confidence labels
|
|
38
|
+
CONFIDENCE_LABELS = {
|
|
39
|
+
(0.8..) => :certain,
|
|
40
|
+
(0.6...0.8) => :confident,
|
|
41
|
+
(0.4...0.6) => :moderate,
|
|
42
|
+
(0.2...0.4) => :low,
|
|
43
|
+
(..0.2) => :uncertain
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
# Integration quality labels
|
|
47
|
+
QUALITY_LABELS = {
|
|
48
|
+
(0.8..) => :excellent,
|
|
49
|
+
(0.6...0.8) => :good,
|
|
50
|
+
(0.4...0.6) => :adequate,
|
|
51
|
+
(0.2...0.4) => :poor,
|
|
52
|
+
(..0.2) => :failed
|
|
53
|
+
}.freeze
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveIntegration
|
|
8
|
+
module Helpers
|
|
9
|
+
class IntegratedRepresentation
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :signal_ids, :modalities, :binding_strength,
|
|
13
|
+
:coherence, :created_at
|
|
14
|
+
|
|
15
|
+
def initialize(signals:)
|
|
16
|
+
@id = SecureRandom.uuid
|
|
17
|
+
@signal_ids = signals.map(&:id)
|
|
18
|
+
@modalities = signals.map(&:modality).uniq
|
|
19
|
+
@binding_strength = compute_binding(signals)
|
|
20
|
+
@coherence = compute_coherence(signals)
|
|
21
|
+
@reinforcement_count = 0
|
|
22
|
+
@created_at = Time.now.utc
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def reinforce!
|
|
26
|
+
@binding_strength = (@binding_strength + REINFORCEMENT_RATE).clamp(0.0, 1.0).round(10)
|
|
27
|
+
@reinforcement_count += 1
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def decay!
|
|
32
|
+
@binding_strength = (@binding_strength - BINDING_DECAY_RATE).clamp(0.0, 1.0).round(10)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def disrupt!(amount: CONFLICT_PENALTY)
|
|
37
|
+
@binding_strength = (@binding_strength - amount).clamp(0.0, 1.0).round(10)
|
|
38
|
+
@coherence = (@coherence - (amount * 0.5)).clamp(0.0, 1.0).round(10)
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def modal_count = @modalities.size
|
|
43
|
+
def multi_modal? = @modalities.size > 1
|
|
44
|
+
def coherent? = @coherence >= COHERENT_THRESHOLD
|
|
45
|
+
def fragmented? = @coherence < FRAGMENTED_THRESHOLD
|
|
46
|
+
|
|
47
|
+
def binding_label
|
|
48
|
+
match = BINDING_LABELS.find { |range, _| range.cover?(@binding_strength) }
|
|
49
|
+
match ? match.last : :unbound
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def quality_label
|
|
53
|
+
score = ((@binding_strength * 0.6) + (@coherence * 0.4)).round(10)
|
|
54
|
+
match = QUALITY_LABELS.find { |range, _| range.cover?(score) }
|
|
55
|
+
match ? match.last : :failed
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def quality_score
|
|
59
|
+
((@binding_strength * 0.6) + (@coherence * 0.4)).round(10)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_h
|
|
63
|
+
{
|
|
64
|
+
id: @id,
|
|
65
|
+
signal_ids: @signal_ids,
|
|
66
|
+
modalities: @modalities,
|
|
67
|
+
modal_count: modal_count,
|
|
68
|
+
multi_modal: multi_modal?,
|
|
69
|
+
binding_strength: @binding_strength,
|
|
70
|
+
binding_label: binding_label,
|
|
71
|
+
coherence: @coherence,
|
|
72
|
+
coherent: coherent?,
|
|
73
|
+
fragmented: fragmented?,
|
|
74
|
+
quality_score: quality_score,
|
|
75
|
+
quality_label: quality_label,
|
|
76
|
+
reinforcement_count: @reinforcement_count,
|
|
77
|
+
created_at: @created_at
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def compute_binding(signals)
|
|
84
|
+
return DEFAULT_BINDING_STRENGTH if signals.empty?
|
|
85
|
+
|
|
86
|
+
weights = signals.map(&:effective_weight)
|
|
87
|
+
(weights.sum / weights.size).round(10)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def compute_coherence(signals)
|
|
91
|
+
return 0.0 if signals.size < 2
|
|
92
|
+
|
|
93
|
+
confidences = signals.map(&:confidence)
|
|
94
|
+
mean = confidences.sum / confidences.size
|
|
95
|
+
variance = confidences.sum { |c| (c - mean)**2 } / confidences.size
|
|
96
|
+
(1.0 - Math.sqrt(variance)).clamp(0.0, 1.0).round(10)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveIntegration
|
|
6
|
+
module Helpers
|
|
7
|
+
class IntegrationEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@signals = {}
|
|
12
|
+
@representations = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add_signal(modality:, content:, confidence: 0.5, salience: 0.5)
|
|
16
|
+
prune_signals_if_needed
|
|
17
|
+
signal = ModalSignal.new(modality: modality, content: content,
|
|
18
|
+
confidence: confidence, salience: salience)
|
|
19
|
+
@signals[signal.id] = signal
|
|
20
|
+
signal
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def remove_signal(signal_id:)
|
|
24
|
+
@signals.delete(signal_id)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def integrate(signal_ids:)
|
|
28
|
+
signals = signal_ids.filter_map { |sid| @signals[sid] }
|
|
29
|
+
return nil if signals.size < MIN_SIGNALS_FOR_BINDING
|
|
30
|
+
|
|
31
|
+
prune_representations_if_needed
|
|
32
|
+
rep = IntegratedRepresentation.new(signals: signals)
|
|
33
|
+
@representations[rep.id] = rep
|
|
34
|
+
rep
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def integrate_by_modalities(modalities:)
|
|
38
|
+
mods = modalities.map(&:to_sym)
|
|
39
|
+
matching = @signals.values.select { |s| mods.include?(s.modality) }
|
|
40
|
+
return nil if matching.size < MIN_SIGNALS_FOR_BINDING
|
|
41
|
+
|
|
42
|
+
integrate(signal_ids: matching.map(&:id))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def integrate_all_salient
|
|
46
|
+
salient = @signals.values.select(&:salient?)
|
|
47
|
+
return nil if salient.size < MIN_SIGNALS_FOR_BINDING
|
|
48
|
+
|
|
49
|
+
integrate(signal_ids: salient.map(&:id))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def reinforce(representation_id:)
|
|
53
|
+
rep = @representations[representation_id]
|
|
54
|
+
return nil unless rep
|
|
55
|
+
|
|
56
|
+
rep.reinforce!
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def disrupt(representation_id:, amount: CONFLICT_PENALTY)
|
|
60
|
+
rep = @representations[representation_id]
|
|
61
|
+
return nil unless rep
|
|
62
|
+
|
|
63
|
+
rep.disrupt!(amount: amount)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def decay_all!
|
|
67
|
+
@representations.each_value(&:decay!)
|
|
68
|
+
prune_weak_representations
|
|
69
|
+
{ representations_remaining: @representations.size }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def signals_by_modality(modality:)
|
|
73
|
+
m = modality.to_sym
|
|
74
|
+
@signals.values.select { |s| s.modality == m }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def multi_modal_representations
|
|
78
|
+
@representations.values.select(&:multi_modal?)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def coherent_representations
|
|
82
|
+
@representations.values.select(&:coherent?)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def fragmented_representations
|
|
86
|
+
@representations.values.select(&:fragmented?)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def strongest_representations(limit: 5)
|
|
90
|
+
@representations.values.sort_by { |r| -r.binding_strength }.first(limit)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def average_binding_strength
|
|
94
|
+
return DEFAULT_BINDING_STRENGTH if @representations.empty?
|
|
95
|
+
|
|
96
|
+
vals = @representations.values.map(&:binding_strength)
|
|
97
|
+
(vals.sum / vals.size).round(10)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def average_coherence
|
|
101
|
+
return 0.5 if @representations.empty?
|
|
102
|
+
|
|
103
|
+
vals = @representations.values.map(&:coherence)
|
|
104
|
+
(vals.sum / vals.size).round(10)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def modality_coverage
|
|
108
|
+
@signals.values.map(&:modality).uniq
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def integration_report
|
|
112
|
+
{
|
|
113
|
+
total_signals: @signals.size,
|
|
114
|
+
total_representations: @representations.size,
|
|
115
|
+
multi_modal_count: multi_modal_representations.size,
|
|
116
|
+
coherent_count: coherent_representations.size,
|
|
117
|
+
fragmented_count: fragmented_representations.size,
|
|
118
|
+
average_binding_strength: average_binding_strength,
|
|
119
|
+
average_coherence: average_coherence,
|
|
120
|
+
modality_coverage: modality_coverage,
|
|
121
|
+
strongest: strongest_representations(limit: 3).map(&:to_h)
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def to_h
|
|
126
|
+
{
|
|
127
|
+
total_signals: @signals.size,
|
|
128
|
+
total_representations: @representations.size,
|
|
129
|
+
multi_modal_count: multi_modal_representations.size,
|
|
130
|
+
coherent_count: coherent_representations.size,
|
|
131
|
+
average_binding_strength: average_binding_strength,
|
|
132
|
+
average_coherence: average_coherence
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def prune_signals_if_needed
|
|
139
|
+
return if @signals.size < MAX_SIGNALS
|
|
140
|
+
|
|
141
|
+
oldest = @signals.values.min_by(&:created_at)
|
|
142
|
+
@signals.delete(oldest.id) if oldest
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def prune_representations_if_needed
|
|
146
|
+
return if @representations.size < MAX_REPRESENTATIONS
|
|
147
|
+
|
|
148
|
+
weakest = @representations.values.min_by(&:binding_strength)
|
|
149
|
+
@representations.delete(weakest.id) if weakest
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def prune_weak_representations
|
|
153
|
+
@representations.reject! { |_, r| r.binding_strength <= 0.0 }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module CognitiveIntegration
|
|
8
|
+
module Helpers
|
|
9
|
+
class ModalSignal
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :modality, :content, :confidence, :salience,
|
|
13
|
+
:created_at
|
|
14
|
+
|
|
15
|
+
def initialize(modality:, content:, confidence: 0.5, salience: 0.5)
|
|
16
|
+
@id = SecureRandom.uuid
|
|
17
|
+
@modality = modality.to_sym
|
|
18
|
+
@content = content
|
|
19
|
+
@confidence = confidence.to_f.clamp(0.0, 1.0).round(10)
|
|
20
|
+
@salience = salience.to_f.clamp(0.0, 1.0).round(10)
|
|
21
|
+
@created_at = Time.now.utc
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def effective_weight
|
|
25
|
+
(@confidence * @salience).round(10)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def salient?
|
|
29
|
+
@salience >= 0.6
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def confident?
|
|
33
|
+
@confidence >= 0.6
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def confidence_label
|
|
37
|
+
match = CONFIDENCE_LABELS.find { |range, _| range.cover?(@confidence) }
|
|
38
|
+
match ? match.last : :uncertain
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_h
|
|
42
|
+
{
|
|
43
|
+
id: @id,
|
|
44
|
+
modality: @modality,
|
|
45
|
+
content: @content,
|
|
46
|
+
confidence: @confidence,
|
|
47
|
+
confidence_label: confidence_label,
|
|
48
|
+
salience: @salience,
|
|
49
|
+
effective_weight: effective_weight,
|
|
50
|
+
salient: salient?,
|
|
51
|
+
created_at: @created_at
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveIntegration
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveIntegration
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def add_signal(modality:, content:, confidence: 0.5, salience: 0.5, engine: nil, **)
|
|
11
|
+
eng = engine || default_engine
|
|
12
|
+
signal = eng.add_signal(modality: modality, content: content,
|
|
13
|
+
confidence: confidence, salience: salience)
|
|
14
|
+
{ success: true, signal: signal.to_h }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_signal(signal_id:, engine: nil, **)
|
|
18
|
+
eng = engine || default_engine
|
|
19
|
+
signal = eng.remove_signal(signal_id: signal_id)
|
|
20
|
+
return { success: false, error: 'signal not found' } unless signal
|
|
21
|
+
|
|
22
|
+
{ success: true, removed: signal.to_h }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def integrate(signal_ids:, engine: nil, **)
|
|
26
|
+
eng = engine || default_engine
|
|
27
|
+
rep = eng.integrate(signal_ids: signal_ids)
|
|
28
|
+
return { success: false, error: 'insufficient signals for binding' } unless rep
|
|
29
|
+
|
|
30
|
+
{ success: true, representation: rep.to_h }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def integrate_by_modalities(modalities:, engine: nil, **)
|
|
34
|
+
eng = engine || default_engine
|
|
35
|
+
rep = eng.integrate_by_modalities(modalities: modalities)
|
|
36
|
+
return { success: false, error: 'insufficient matching signals' } unless rep
|
|
37
|
+
|
|
38
|
+
{ success: true, representation: rep.to_h }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def integrate_all_salient(engine: nil, **)
|
|
42
|
+
eng = engine || default_engine
|
|
43
|
+
rep = eng.integrate_all_salient
|
|
44
|
+
return { success: false, error: 'insufficient salient signals' } unless rep
|
|
45
|
+
|
|
46
|
+
{ success: true, representation: rep.to_h }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def reinforce(representation_id:, engine: nil, **)
|
|
50
|
+
eng = engine || default_engine
|
|
51
|
+
rep = eng.reinforce(representation_id: representation_id)
|
|
52
|
+
return { success: false, error: 'representation not found' } unless rep
|
|
53
|
+
|
|
54
|
+
{ success: true, representation: rep.to_h }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def disrupt(representation_id:, amount: nil, engine: nil, **)
|
|
58
|
+
eng = engine || default_engine
|
|
59
|
+
amt = amount || Helpers::Constants::CONFLICT_PENALTY
|
|
60
|
+
rep = eng.disrupt(representation_id: representation_id, amount: amt)
|
|
61
|
+
return { success: false, error: 'representation not found' } unless rep
|
|
62
|
+
|
|
63
|
+
{ success: true, representation: rep.to_h }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def decay(engine: nil, **)
|
|
67
|
+
eng = engine || default_engine
|
|
68
|
+
result = eng.decay_all!
|
|
69
|
+
{ success: true, **result }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def signals_by_modality(modality:, engine: nil, **)
|
|
73
|
+
eng = engine || default_engine
|
|
74
|
+
{ success: true, signals: eng.signals_by_modality(modality: modality).map(&:to_h) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def coherent_representations(engine: nil, **)
|
|
78
|
+
eng = engine || default_engine
|
|
79
|
+
{ success: true, representations: eng.coherent_representations.map(&:to_h) }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def integration_report(engine: nil, **)
|
|
83
|
+
eng = engine || default_engine
|
|
84
|
+
{ success: true, report: eng.integration_report }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def status(engine: nil, **)
|
|
88
|
+
eng = engine || default_engine
|
|
89
|
+
{ success: true, **eng.to_h }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def default_engine
|
|
95
|
+
@default_engine ||= Helpers::IntegrationEngine.new
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'cognitive_integration/version'
|
|
4
|
+
require_relative 'cognitive_integration/helpers/constants'
|
|
5
|
+
require_relative 'cognitive_integration/helpers/modal_signal'
|
|
6
|
+
require_relative 'cognitive_integration/helpers/integrated_representation'
|
|
7
|
+
require_relative 'cognitive_integration/helpers/integration_engine'
|
|
8
|
+
require_relative 'cognitive_integration/runners/cognitive_integration'
|
|
9
|
+
require_relative 'cognitive_integration/client'
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module CognitiveIntegration
|
|
14
|
+
extend Legion::Extensions::Core if defined?(Legion::Extensions::Core)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-integration
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: legion-gaia
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: Models convergence zones that bind information from different cognitive
|
|
27
|
+
modalities into unified multi-modal representations with binding strength and coherence
|
|
28
|
+
tracking.
|
|
29
|
+
email:
|
|
30
|
+
- matthewdiverson@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".github/workflows/ci.yml"
|
|
36
|
+
- ".gitignore"
|
|
37
|
+
- ".rspec"
|
|
38
|
+
- ".rubocop.yml"
|
|
39
|
+
- CLAUDE.md
|
|
40
|
+
- Gemfile
|
|
41
|
+
- README.md
|
|
42
|
+
- lex-cognitive-integration.gemspec
|
|
43
|
+
- lib/legion/extensions/cognitive_integration.rb
|
|
44
|
+
- lib/legion/extensions/cognitive_integration/client.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_integration/helpers/constants.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_integration/helpers/integrated_representation.rb
|
|
47
|
+
- lib/legion/extensions/cognitive_integration/helpers/integration_engine.rb
|
|
48
|
+
- lib/legion/extensions/cognitive_integration/helpers/modal_signal.rb
|
|
49
|
+
- lib/legion/extensions/cognitive_integration/runners/cognitive_integration.rb
|
|
50
|
+
- lib/legion/extensions/cognitive_integration/version.rb
|
|
51
|
+
homepage: https://github.com/LegionIO/lex-cognitive-integration
|
|
52
|
+
licenses:
|
|
53
|
+
- MIT
|
|
54
|
+
metadata:
|
|
55
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-integration
|
|
56
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-integration
|
|
57
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-integration/blob/master/README.md
|
|
58
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-integration/blob/master/CHANGELOG.md
|
|
59
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-integration/issues
|
|
60
|
+
rubygems_mfa_required: 'true'
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.4'
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 3.6.9
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: Cross-modal cognitive integration for LegionIO
|
|
78
|
+
test_files: []
|