lex-cognitive-mycelium 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/CLAUDE.md +101 -0
- data/README.md +50 -0
- data/lex-cognitive-mycelium.gemspec +36 -0
- data/lib/legion/extensions/cognitive_mycelium/client.rb +15 -0
- data/lib/legion/extensions/cognitive_mycelium/helpers/constants.rb +48 -0
- data/lib/legion/extensions/cognitive_mycelium/helpers/fruiting_body.rb +46 -0
- data/lib/legion/extensions/cognitive_mycelium/helpers/hypha.rb +72 -0
- data/lib/legion/extensions/cognitive_mycelium/helpers/mycelial_node.rb +68 -0
- data/lib/legion/extensions/cognitive_mycelium/helpers/mycelium_engine.rb +147 -0
- data/lib/legion/extensions/cognitive_mycelium/runners/cognitive_mycelium.rb +69 -0
- data/lib/legion/extensions/cognitive_mycelium/version.rb +9 -0
- data/lib/legion/extensions/cognitive_mycelium.rb +20 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9bb3fcb205f37ec89847ee5b27fc927063cde42b67e1c5c300504f8494b40af4
|
|
4
|
+
data.tar.gz: 40a70575d4362fd4a29c649228082e4e9fd27253f6e17726f4cfbb594afc208a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0615f7d3098282b7b4147e2c8509d4528a182173913f77553ef787fb473cf5482d90b66a68c0cb3d76ebb819f43fbe7d564d6457ec62dc163c696cc1d61ee8cd
|
|
7
|
+
data.tar.gz: d4106d376468094d6b86abbeed50b74444f87660db27912a350be75369f9e0db2aa294581d51cd90c93cb802531065f1b8b9ba6064915597eb9ce044770cb77f
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# lex-cognitive-mycelium
|
|
2
|
+
|
|
3
|
+
**Level 3 Leaf Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-agentic/CLAUDE.md`
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Fungal mycelium network metaphor for distributed knowledge. Nodes (knowledge clusters, insight nodes, memory nodes, etc.) are connected by hyphae (connections with nutrient types and strength states). Nutrients transfer through hyphae at an efficiency rate. When a node accumulates sufficient nutrients it becomes ready to fruit, producing a fruiting body (insight, breakthrough, synthesis, etc.). Models how knowledge fragments strengthen through use, dormant connections exist, and concentrated knowledge produces emergent insights.
|
|
9
|
+
|
|
10
|
+
## Gem Info
|
|
11
|
+
|
|
12
|
+
- **Gem name**: `lex-cognitive-mycelium`
|
|
13
|
+
- **Module**: `Legion::Extensions::CognitiveMycelium`
|
|
14
|
+
- **Version**: `0.1.0`
|
|
15
|
+
- **Ruby**: `>= 3.4`
|
|
16
|
+
- **License**: MIT
|
|
17
|
+
|
|
18
|
+
## File Structure
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
lib/legion/extensions/cognitive_mycelium/
|
|
22
|
+
version.rb
|
|
23
|
+
client.rb
|
|
24
|
+
helpers/
|
|
25
|
+
constants.rb
|
|
26
|
+
mycelial_node.rb
|
|
27
|
+
hypha.rb
|
|
28
|
+
runners/
|
|
29
|
+
cognitive_mycelium.rb
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Key Constants
|
|
33
|
+
|
|
34
|
+
| Constant | Value | Purpose |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `MAX_NODES` | `200` | Per-engine node capacity |
|
|
37
|
+
| `MAX_HYPHAE` | `500` | Per-engine hypha capacity |
|
|
38
|
+
| `MAX_FRUITING_BODIES` | `100` | Fruiting body ring buffer size |
|
|
39
|
+
| `NUTRIENT_DECAY` | `0.03` | Per-cycle node nutrient decay |
|
|
40
|
+
| `TRANSFER_EFFICIENCY` | `0.8` | Fraction of nutrients that transfer through a hypha |
|
|
41
|
+
| `FRUITING_THRESHOLD` | `0.7` | Nutrient level at which a node can fruit |
|
|
42
|
+
| `NODE_TYPES` | `%i[knowledge_cluster insight_node memory_node skill_node pattern_node creative_node]` | Valid types |
|
|
43
|
+
| `NUTRIENT_TYPES` | `%i[information energy pattern association context emotion]` | Valid nutrient categories |
|
|
44
|
+
| `HYPHA_STATES` | `%i[growing mature dormant decaying]` | Hypha lifecycle states |
|
|
45
|
+
| `FRUITING_TYPES` | `%i[insight breakthrough synthesis connection revelation]` | Fruiting body types |
|
|
46
|
+
| `NETWORK_HEALTH_LABELS` | range hash | Network health classification |
|
|
47
|
+
|
|
48
|
+
## Helpers
|
|
49
|
+
|
|
50
|
+
### `Helpers::MycelialNode`
|
|
51
|
+
Individual network node. Has `id`, `label`, `node_type`, `domain`, `nutrient_level`, `connections` (hypha IDs), and `fruiting_bodies` (array).
|
|
52
|
+
|
|
53
|
+
- `absorb!(amount, nutrient_type:)` — increases nutrient level (clamped to 1.0)
|
|
54
|
+
- `deplete!(amount)` — decreases nutrient level (floor 0)
|
|
55
|
+
- `fruiting_ready?` — `nutrient_level >= FRUITING_THRESHOLD`
|
|
56
|
+
- `starving?` — `nutrient_level <= 0.1`
|
|
57
|
+
|
|
58
|
+
### `Helpers::Hypha`
|
|
59
|
+
Connection between two nodes. Has `id`, `from_node_id`, `to_node_id`, `nutrient_type`, `strength`, and `state`.
|
|
60
|
+
|
|
61
|
+
- `reinforce!(amount)` — increases strength; transitions to `:mature` when >= 0.6
|
|
62
|
+
- `decay!(amount)` — decreases strength; transitions to `:dormant` when < 0.4, `:decaying` when < 0.2
|
|
63
|
+
- `transfer_capacity` — `strength * TRANSFER_EFFICIENCY` (actual nutrients transferred)
|
|
64
|
+
- `active?` — state is `:growing` or `:mature`
|
|
65
|
+
|
|
66
|
+
### Engine (inline in runner/client)
|
|
67
|
+
- `create_node(label:, node_type:, domain:)` → node
|
|
68
|
+
- `connect(from_id:, to_id:, nutrient_type:)` → hypha
|
|
69
|
+
- `transfer_nutrients(hypha_id:, amount:)` → transfer result
|
|
70
|
+
- `fruit(node_id:, fruiting_type:)` → fruiting body or `:not_ready`
|
|
71
|
+
- Network health calculations
|
|
72
|
+
|
|
73
|
+
## Runners
|
|
74
|
+
|
|
75
|
+
Module: `Runners::CognitiveMycelium`
|
|
76
|
+
|
|
77
|
+
| Runner Method | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `create_node(label:, node_type:, domain:)` | Register a new node |
|
|
80
|
+
| `connect(from_id:, to_id:, nutrient_type:)` | Create a hypha between nodes |
|
|
81
|
+
| `transfer_nutrients(hypha_id:, amount:)` | Transfer nutrients through a hypha |
|
|
82
|
+
| `fruit(node_id:, fruiting_type:)` | Produce a fruiting body from a ready node |
|
|
83
|
+
| `network_status` | Full network health report |
|
|
84
|
+
|
|
85
|
+
All runners return `{success: true/false, ...}` hashes.
|
|
86
|
+
|
|
87
|
+
## Integration Points
|
|
88
|
+
|
|
89
|
+
- `lex-memory`: mycelial nodes parallel memory traces; hyphae parallel Hebbian links
|
|
90
|
+
- `lex-dream` `association_walk` phase: walk the mycelium network to find nutrient-rich clusters
|
|
91
|
+
- Fruiting bodies (insights) can be injected into `lex-memory` as high-confidence semantic traces
|
|
92
|
+
- Nutrient transfer models information propagation through `lex-mesh` between agents
|
|
93
|
+
- `TRANSFER_EFFICIENCY = 0.8` means each hop loses 20% — models signal degradation across hops
|
|
94
|
+
|
|
95
|
+
## Development Notes
|
|
96
|
+
|
|
97
|
+
- `Client` instantiates the engine (memoized as `@default_engine`)
|
|
98
|
+
- Hypha state transitions are: `growing` → `mature` (reinforce to >= 0.6) → `dormant` (decay < 0.4) → `decaying` (decay < 0.2)
|
|
99
|
+
- `fruit` requires `fruiting_ready?` to return true; otherwise returns `{success: false, error: :not_ready}`
|
|
100
|
+
- `MAX_FRUITING_BODIES = 100` is a ring buffer — oldest fruiting bodies are dropped when full
|
|
101
|
+
- `NUTRIENT_DECAY = 0.03` per cycle means a fully charged node (1.0) reaches `FRUITING_THRESHOLD` (0.7) floor in ~10 cycles without reinforcement
|
data/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# lex-cognitive-mycelium
|
|
2
|
+
|
|
3
|
+
Fungal mycelium network model for LegionIO cognitive agents. Nodes hold nutrients; hyphae transfer nutrients between nodes at an efficiency rate. When a node accumulates enough nutrients it can fruit, producing an emergent insight or breakthrough.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
- Six node types: `knowledge_cluster`, `insight_node`, `memory_node`, `skill_node`, `pattern_node`, `creative_node`
|
|
8
|
+
- Six nutrient types: `information`, `energy`, `pattern`, `association`, `context`, `emotion`
|
|
9
|
+
- Hyphae have states: `growing`, `mature`, `dormant`, `decaying`
|
|
10
|
+
- Nutrient transfer efficiency: 80% per hop (configurable)
|
|
11
|
+
- Fruiting threshold: 0.7 nutrient level triggers readiness
|
|
12
|
+
- Five fruiting body types: `insight`, `breakthrough`, `synthesis`, `connection`, `revelation`
|
|
13
|
+
- Network health tracking across all nodes and connections
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
# Create nodes
|
|
19
|
+
a = runner.create_node(label: 'pattern_library', node_type: :knowledge_cluster, domain: :architecture)
|
|
20
|
+
b = runner.create_node(label: 'current_problem', node_type: :insight_node, domain: :architecture)
|
|
21
|
+
|
|
22
|
+
# Connect them
|
|
23
|
+
h = runner.connect(from_id: a[:node][:id], to_id: b[:node][:id], nutrient_type: :pattern)
|
|
24
|
+
|
|
25
|
+
# Transfer nutrients
|
|
26
|
+
runner.transfer_nutrients(hypha_id: h[:hypha][:id], amount: 0.5)
|
|
27
|
+
# => { success: true, transferred: 0.4, to_node_level: 0.4, ... }
|
|
28
|
+
|
|
29
|
+
# Keep feeding until ready
|
|
30
|
+
# ... repeat transfers ...
|
|
31
|
+
|
|
32
|
+
# Fruit when ready
|
|
33
|
+
runner.fruit(node_id: b[:node][:id], fruiting_type: :insight)
|
|
34
|
+
# => { success: true, fruiting_body: { type: :insight, content: '...', ... } }
|
|
35
|
+
|
|
36
|
+
# Network status
|
|
37
|
+
runner.network_status
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bundle install
|
|
44
|
+
bundle exec rspec
|
|
45
|
+
bundle exec rubocop
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/cognitive_mycelium/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-cognitive-mycelium'
|
|
7
|
+
spec.version = Legion::Extensions::CognitiveMycelium::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
spec.license = 'MIT'
|
|
11
|
+
|
|
12
|
+
spec.summary = 'Cognitive mycelium LEX — underground knowledge network ' \
|
|
13
|
+
'connecting disparate cognitive domains'
|
|
14
|
+
spec.description = 'Models a mycelial network beneath conscious awareness: ' \
|
|
15
|
+
'hyphae connect disparate knowledge domains, nutrients ' \
|
|
16
|
+
'(insights) flow through the network, and fruiting bodies ' \
|
|
17
|
+
'(creative breakthroughs) emerge at the surface.'
|
|
18
|
+
spec.homepage = 'https://github.com/LegionIO/lex-cognitive-mycelium'
|
|
19
|
+
|
|
20
|
+
spec.required_ruby_version = '>= 3.4'
|
|
21
|
+
|
|
22
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
23
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
24
|
+
spec.metadata['documentation_uri'] = "#{spec.homepage}#readme"
|
|
25
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
26
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
27
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
28
|
+
|
|
29
|
+
spec.files = Dir.chdir(__dir__) do
|
|
30
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
31
|
+
f.start_with?('spec/', '.', 'Gemfile')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
spec.require_paths = ['lib']
|
|
35
|
+
spec.add_development_dependency 'legion-gaia'
|
|
36
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::CognitiveMycelium
|
|
8
|
+
|
|
9
|
+
def initialize(engine: nil)
|
|
10
|
+
@default_engine = engine || Helpers::MyceliumEngine.new
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_NODES = 200
|
|
9
|
+
MAX_HYPHAE = 500
|
|
10
|
+
MAX_FRUITING_BODIES = 100
|
|
11
|
+
NUTRIENT_DECAY = 0.03
|
|
12
|
+
TRANSFER_EFFICIENCY = 0.8
|
|
13
|
+
FRUITING_THRESHOLD = 0.7
|
|
14
|
+
|
|
15
|
+
NODE_TYPES = %i[
|
|
16
|
+
knowledge_cluster insight_node memory_node
|
|
17
|
+
skill_node pattern_node creative_node
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
NUTRIENT_TYPES = %i[
|
|
21
|
+
information experience emotional_energy
|
|
22
|
+
novelty pattern_recognition creative_potential
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
HYPHA_STATES = %i[growing mature dormant decaying].freeze
|
|
26
|
+
|
|
27
|
+
FRUITING_TYPES = %i[
|
|
28
|
+
insight breakthrough connection analogy
|
|
29
|
+
synthesis innovation
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
NETWORK_HEALTH_LABELS = [
|
|
33
|
+
[0.8..1.0, :thriving],
|
|
34
|
+
[0.6..0.8, :healthy],
|
|
35
|
+
[0.4..0.6, :stable],
|
|
36
|
+
[0.2..0.4, :stressed],
|
|
37
|
+
[0.0..0.2, :depleted]
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
def self.label_for(table, value)
|
|
41
|
+
table.each { |range, label| return label if range.cover?(value) }
|
|
42
|
+
table.last.last
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Helpers
|
|
7
|
+
class FruitingBody
|
|
8
|
+
attr_reader :id, :fruiting_type, :source_node_id,
|
|
9
|
+
:content, :potency, :emerged_at
|
|
10
|
+
|
|
11
|
+
def initialize(fruiting_type:, source_node_id:,
|
|
12
|
+
content:, potency: 0.5)
|
|
13
|
+
validate_type!(fruiting_type)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@fruiting_type = fruiting_type.to_sym
|
|
16
|
+
@source_node_id = source_node_id
|
|
17
|
+
@content = content.to_s
|
|
18
|
+
@potency = potency.to_f.clamp(0.0, 1.0).round(10)
|
|
19
|
+
@emerged_at = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_h
|
|
23
|
+
{
|
|
24
|
+
id: @id,
|
|
25
|
+
fruiting_type: @fruiting_type,
|
|
26
|
+
source_node_id: @source_node_id,
|
|
27
|
+
content: @content,
|
|
28
|
+
potency: @potency,
|
|
29
|
+
emerged_at: @emerged_at
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def validate_type!(val)
|
|
36
|
+
return if Constants::FRUITING_TYPES.include?(val.to_sym)
|
|
37
|
+
|
|
38
|
+
raise ArgumentError,
|
|
39
|
+
"unknown fruiting type: #{val.inspect}; " \
|
|
40
|
+
"must be one of #{Constants::FRUITING_TYPES.inspect}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Helpers
|
|
7
|
+
class Hypha
|
|
8
|
+
attr_reader :id, :source_id, :target_id, :nutrient_type,
|
|
9
|
+
:created_at
|
|
10
|
+
attr_accessor :strength, :state
|
|
11
|
+
|
|
12
|
+
def initialize(source_id:, target_id:, nutrient_type:,
|
|
13
|
+
strength: 0.5)
|
|
14
|
+
validate_nutrient!(nutrient_type)
|
|
15
|
+
@id = SecureRandom.uuid
|
|
16
|
+
@source_id = source_id
|
|
17
|
+
@target_id = target_id
|
|
18
|
+
@nutrient_type = nutrient_type.to_sym
|
|
19
|
+
@strength = strength.to_f.clamp(0.0, 1.0).round(10)
|
|
20
|
+
@state = :growing
|
|
21
|
+
@created_at = Time.now.utc
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reinforce!(amount: 0.1)
|
|
25
|
+
@strength = (@strength + amount.abs).clamp(0.0, 1.0).round(10)
|
|
26
|
+
@state = :mature if @strength >= 0.6
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def decay!(rate: Constants::NUTRIENT_DECAY)
|
|
31
|
+
@strength = (@strength - rate.abs).clamp(0.0, 1.0).round(10)
|
|
32
|
+
@state = :decaying if @strength < 0.2
|
|
33
|
+
@state = :dormant if @strength < 0.4 && @state == :mature
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def transfer_capacity
|
|
38
|
+
(@strength * Constants::TRANSFER_EFFICIENCY).round(10)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def active?
|
|
42
|
+
%i[growing mature].include?(@state)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_h
|
|
46
|
+
{
|
|
47
|
+
id: @id,
|
|
48
|
+
source_id: @source_id,
|
|
49
|
+
target_id: @target_id,
|
|
50
|
+
nutrient_type: @nutrient_type,
|
|
51
|
+
strength: @strength,
|
|
52
|
+
state: @state,
|
|
53
|
+
transfer_capacity: transfer_capacity,
|
|
54
|
+
active: active?,
|
|
55
|
+
created_at: @created_at
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def validate_nutrient!(val)
|
|
62
|
+
return if Constants::NUTRIENT_TYPES.include?(val.to_sym)
|
|
63
|
+
|
|
64
|
+
raise ArgumentError,
|
|
65
|
+
"unknown nutrient type: #{val.inspect}; " \
|
|
66
|
+
"must be one of #{Constants::NUTRIENT_TYPES.inspect}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Helpers
|
|
7
|
+
class MycelialNode
|
|
8
|
+
attr_reader :id, :node_type, :domain, :content, :created_at
|
|
9
|
+
attr_accessor :nutrient_level, :connections_count
|
|
10
|
+
|
|
11
|
+
def initialize(node_type:, domain:, content:,
|
|
12
|
+
nutrient_level: 0.5)
|
|
13
|
+
validate_type!(node_type)
|
|
14
|
+
@id = SecureRandom.uuid
|
|
15
|
+
@node_type = node_type.to_sym
|
|
16
|
+
@domain = domain.to_sym
|
|
17
|
+
@content = content.to_s
|
|
18
|
+
@nutrient_level = nutrient_level.to_f.clamp(0.0, 1.0).round(10)
|
|
19
|
+
@connections_count = 0
|
|
20
|
+
@created_at = Time.now.utc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def absorb!(amount)
|
|
24
|
+
@nutrient_level = (@nutrient_level + amount.abs).clamp(0.0, 1.0).round(10)
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def deplete!(amount)
|
|
29
|
+
@nutrient_level = (@nutrient_level - amount.abs).clamp(0.0, 1.0).round(10)
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def fruiting_ready?
|
|
34
|
+
@nutrient_level >= Constants::FRUITING_THRESHOLD
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def starving?
|
|
38
|
+
@nutrient_level < 0.1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def to_h
|
|
42
|
+
{
|
|
43
|
+
id: @id,
|
|
44
|
+
node_type: @node_type,
|
|
45
|
+
domain: @domain,
|
|
46
|
+
content: @content,
|
|
47
|
+
nutrient_level: @nutrient_level,
|
|
48
|
+
connections_count: @connections_count,
|
|
49
|
+
fruiting_ready: fruiting_ready?,
|
|
50
|
+
starving: starving?,
|
|
51
|
+
created_at: @created_at
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def validate_type!(val)
|
|
58
|
+
return if Constants::NODE_TYPES.include?(val.to_sym)
|
|
59
|
+
|
|
60
|
+
raise ArgumentError,
|
|
61
|
+
"unknown node type: #{val.inspect}; " \
|
|
62
|
+
"must be one of #{Constants::NODE_TYPES.inspect}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Helpers
|
|
7
|
+
class MyceliumEngine
|
|
8
|
+
def initialize
|
|
9
|
+
@nodes = {}
|
|
10
|
+
@hyphae = {}
|
|
11
|
+
@fruiting_bodies = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_node(node_type:, domain:, content:,
|
|
15
|
+
nutrient_level: 0.5)
|
|
16
|
+
validate_capacity!(@nodes, Constants::MAX_NODES, 'nodes')
|
|
17
|
+
node = MycelialNode.new(
|
|
18
|
+
node_type: node_type, domain: domain,
|
|
19
|
+
content: content, nutrient_level: nutrient_level
|
|
20
|
+
)
|
|
21
|
+
@nodes[node.id] = node
|
|
22
|
+
node
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def connect(source_id:, target_id:, nutrient_type:,
|
|
26
|
+
strength: 0.5)
|
|
27
|
+
validate_capacity!(@hyphae, Constants::MAX_HYPHAE, 'hyphae')
|
|
28
|
+
fetch_node!(source_id)
|
|
29
|
+
fetch_node!(target_id)
|
|
30
|
+
hypha = Hypha.new(
|
|
31
|
+
source_id: source_id, target_id: target_id,
|
|
32
|
+
nutrient_type: nutrient_type, strength: strength
|
|
33
|
+
)
|
|
34
|
+
@hyphae[hypha.id] = hypha
|
|
35
|
+
increment_connections(source_id, target_id)
|
|
36
|
+
hypha
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def transfer_nutrients(hypha_id:)
|
|
40
|
+
hypha = fetch_hypha!(hypha_id)
|
|
41
|
+
source = fetch_node!(hypha.source_id)
|
|
42
|
+
target = fetch_node!(hypha.target_id)
|
|
43
|
+
amount = [hypha.transfer_capacity, source.nutrient_level].min
|
|
44
|
+
source.deplete!(amount)
|
|
45
|
+
target.absorb!(amount)
|
|
46
|
+
{ transferred: amount, source: source.to_h, target: target.to_h }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fruit!(node_id:, fruiting_type:, content:)
|
|
50
|
+
validate_capacity!(@fruiting_bodies,
|
|
51
|
+
Constants::MAX_FRUITING_BODIES, 'fruiting bodies')
|
|
52
|
+
node = fetch_node!(node_id)
|
|
53
|
+
raise ArgumentError, 'node not ready for fruiting' unless node.fruiting_ready?
|
|
54
|
+
|
|
55
|
+
body = FruitingBody.new(
|
|
56
|
+
fruiting_type: fruiting_type, source_node_id: node_id,
|
|
57
|
+
content: content, potency: node.nutrient_level
|
|
58
|
+
)
|
|
59
|
+
node.deplete!(0.3)
|
|
60
|
+
@fruiting_bodies[body.id] = body
|
|
61
|
+
body
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def decay_network!(rate: Constants::NUTRIENT_DECAY)
|
|
65
|
+
@hyphae.each_value { |h| h.decay!(rate: rate) }
|
|
66
|
+
@nodes.each_value { |n| n.deplete!(rate * 0.5) }
|
|
67
|
+
prune_dead!
|
|
68
|
+
{ nodes: @nodes.size, hyphae: @hyphae.size }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def all_nodes
|
|
72
|
+
@nodes.values
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def all_hyphae
|
|
76
|
+
@hyphae.values
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def all_fruiting_bodies
|
|
80
|
+
@fruiting_bodies.values
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def connections_for(node_id)
|
|
84
|
+
@hyphae.values.select do |h|
|
|
85
|
+
h.source_id == node_id || h.target_id == node_id
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def network_report
|
|
90
|
+
{
|
|
91
|
+
total_nodes: @nodes.size,
|
|
92
|
+
total_hyphae: @hyphae.size,
|
|
93
|
+
total_fruiting: @fruiting_bodies.size,
|
|
94
|
+
active_hyphae: @hyphae.values.count(&:active?),
|
|
95
|
+
avg_nutrient: avg_field(@nodes, :nutrient_level),
|
|
96
|
+
avg_strength: avg_field(@hyphae, :strength),
|
|
97
|
+
fruiting_ready: @nodes.values.count(&:fruiting_ready?),
|
|
98
|
+
starving_nodes: @nodes.values.count(&:starving?),
|
|
99
|
+
network_health: network_health_label
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def fetch_node!(id)
|
|
106
|
+
@nodes.fetch(id) do
|
|
107
|
+
raise ArgumentError, "node not found: #{id.inspect}"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def fetch_hypha!(id)
|
|
112
|
+
@hyphae.fetch(id) do
|
|
113
|
+
raise ArgumentError, "hypha not found: #{id.inspect}"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def validate_capacity!(coll, max, name)
|
|
118
|
+
return if coll.size < max
|
|
119
|
+
|
|
120
|
+
raise ArgumentError,
|
|
121
|
+
"#{name} capacity reached (max #{max})"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def increment_connections(source_id, target_id)
|
|
125
|
+
@nodes[source_id].connections_count += 1
|
|
126
|
+
@nodes[target_id].connections_count += 1
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def prune_dead!
|
|
130
|
+
@hyphae.delete_if { |_, h| h.strength <= 0.0 }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def avg_field(coll, field)
|
|
134
|
+
return 0.0 if coll.empty?
|
|
135
|
+
|
|
136
|
+
(coll.values.sum(&field) / coll.size).round(10)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def network_health_label
|
|
140
|
+
avg = avg_field(@nodes, :nutrient_level)
|
|
141
|
+
Constants.label_for(Constants::NETWORK_HEALTH_LABELS, avg)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module CognitiveMycelium
|
|
6
|
+
module Runners
|
|
7
|
+
module CognitiveMycelium
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
def create_node(node_type:, domain:, content:,
|
|
11
|
+
nutrient_level: 0.5, engine: nil, **)
|
|
12
|
+
eng = resolve_engine(engine)
|
|
13
|
+
node = eng.create_node(node_type: node_type, domain: domain,
|
|
14
|
+
content: content,
|
|
15
|
+
nutrient_level: nutrient_level)
|
|
16
|
+
{ success: true, node: node.to_h }
|
|
17
|
+
rescue ArgumentError => e
|
|
18
|
+
{ success: false, error: e.message }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def connect(source_id:, target_id:, nutrient_type:,
|
|
22
|
+
strength: 0.5, engine: nil, **)
|
|
23
|
+
eng = resolve_engine(engine)
|
|
24
|
+
hypha = eng.connect(source_id: source_id, target_id: target_id,
|
|
25
|
+
nutrient_type: nutrient_type,
|
|
26
|
+
strength: strength)
|
|
27
|
+
{ success: true, hypha: hypha.to_h }
|
|
28
|
+
rescue ArgumentError => e
|
|
29
|
+
{ success: false, error: e.message }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def transfer_nutrients(hypha_id:, engine: nil, **)
|
|
33
|
+
eng = resolve_engine(engine)
|
|
34
|
+
result = eng.transfer_nutrients(hypha_id: hypha_id)
|
|
35
|
+
{ success: true }.merge(result)
|
|
36
|
+
rescue ArgumentError => e
|
|
37
|
+
{ success: false, error: e.message }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fruit(node_id:, fruiting_type:, content:, engine: nil, **)
|
|
41
|
+
eng = resolve_engine(engine)
|
|
42
|
+
body = eng.fruit!(node_id: node_id, fruiting_type: fruiting_type,
|
|
43
|
+
content: content)
|
|
44
|
+
{ success: true, fruiting_body: body.to_h }
|
|
45
|
+
rescue ArgumentError => e
|
|
46
|
+
{ success: false, error: e.message }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def network_status(engine: nil, **)
|
|
50
|
+
eng = resolve_engine(engine)
|
|
51
|
+
{ success: true, report: eng.network_report }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def resolve_engine(engine)
|
|
59
|
+
engine || default_engine
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def default_engine
|
|
63
|
+
@default_engine ||= Helpers::MyceliumEngine.new
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
require_relative 'cognitive_mycelium/version'
|
|
6
|
+
require_relative 'cognitive_mycelium/helpers/constants'
|
|
7
|
+
require_relative 'cognitive_mycelium/helpers/mycelial_node'
|
|
8
|
+
require_relative 'cognitive_mycelium/helpers/hypha'
|
|
9
|
+
require_relative 'cognitive_mycelium/helpers/fruiting_body'
|
|
10
|
+
require_relative 'cognitive_mycelium/helpers/mycelium_engine'
|
|
11
|
+
require_relative 'cognitive_mycelium/runners/cognitive_mycelium'
|
|
12
|
+
require_relative 'cognitive_mycelium/client'
|
|
13
|
+
|
|
14
|
+
module Legion
|
|
15
|
+
module Extensions
|
|
16
|
+
module CognitiveMycelium
|
|
17
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-cognitive-mycelium
|
|
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 a mycelial network beneath conscious awareness: hyphae connect
|
|
27
|
+
disparate knowledge domains, nutrients (insights) flow through the network, and
|
|
28
|
+
fruiting bodies (creative breakthroughs) emerge at the surface.'
|
|
29
|
+
email:
|
|
30
|
+
- matthewdiverson@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- CLAUDE.md
|
|
36
|
+
- README.md
|
|
37
|
+
- lex-cognitive-mycelium.gemspec
|
|
38
|
+
- lib/legion/extensions/cognitive_mycelium.rb
|
|
39
|
+
- lib/legion/extensions/cognitive_mycelium/client.rb
|
|
40
|
+
- lib/legion/extensions/cognitive_mycelium/helpers/constants.rb
|
|
41
|
+
- lib/legion/extensions/cognitive_mycelium/helpers/fruiting_body.rb
|
|
42
|
+
- lib/legion/extensions/cognitive_mycelium/helpers/hypha.rb
|
|
43
|
+
- lib/legion/extensions/cognitive_mycelium/helpers/mycelial_node.rb
|
|
44
|
+
- lib/legion/extensions/cognitive_mycelium/helpers/mycelium_engine.rb
|
|
45
|
+
- lib/legion/extensions/cognitive_mycelium/runners/cognitive_mycelium.rb
|
|
46
|
+
- lib/legion/extensions/cognitive_mycelium/version.rb
|
|
47
|
+
homepage: https://github.com/LegionIO/lex-cognitive-mycelium
|
|
48
|
+
licenses:
|
|
49
|
+
- MIT
|
|
50
|
+
metadata:
|
|
51
|
+
homepage_uri: https://github.com/LegionIO/lex-cognitive-mycelium
|
|
52
|
+
source_code_uri: https://github.com/LegionIO/lex-cognitive-mycelium
|
|
53
|
+
documentation_uri: https://github.com/LegionIO/lex-cognitive-mycelium#readme
|
|
54
|
+
changelog_uri: https://github.com/LegionIO/lex-cognitive-mycelium/blob/main/CHANGELOG.md
|
|
55
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-cognitive-mycelium/issues
|
|
56
|
+
rubygems_mfa_required: 'true'
|
|
57
|
+
rdoc_options: []
|
|
58
|
+
require_paths:
|
|
59
|
+
- lib
|
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '3.4'
|
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
requirements: []
|
|
71
|
+
rubygems_version: 3.6.9
|
|
72
|
+
specification_version: 4
|
|
73
|
+
summary: Cognitive mycelium LEX — underground knowledge network connecting disparate
|
|
74
|
+
cognitive domains
|
|
75
|
+
test_files: []
|