lex-semantic-memory 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/Gemfile +11 -0
- data/lex-semantic-memory.gemspec +31 -0
- data/lib/legion/extensions/semantic_memory/actors/decay.rb +41 -0
- data/lib/legion/extensions/semantic_memory/client.rb +24 -0
- data/lib/legion/extensions/semantic_memory/helpers/concept.rb +97 -0
- data/lib/legion/extensions/semantic_memory/helpers/constants.rb +47 -0
- data/lib/legion/extensions/semantic_memory/helpers/knowledge_store.rb +141 -0
- data/lib/legion/extensions/semantic_memory/runners/semantic_memory.rb +90 -0
- data/lib/legion/extensions/semantic_memory/version.rb +9 -0
- data/lib/legion/extensions/semantic_memory.rb +17 -0
- data/spec/legion/extensions/semantic_memory/client_spec.rb +35 -0
- data/spec/legion/extensions/semantic_memory/helpers/concept_spec.rb +119 -0
- data/spec/legion/extensions/semantic_memory/helpers/knowledge_store_spec.rb +140 -0
- data/spec/legion/extensions/semantic_memory/runners/semantic_memory_spec.rb +103 -0
- data/spec/spec_helper.rb +20 -0
- metadata +77 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '078f5ae8f0283c2644b9713a80bb865bdd750ec02a60a6e3aa2f980a81541bc9'
|
|
4
|
+
data.tar.gz: af9949496a45f4c85e10a47754a939b5b746be2892dd206394fc72fd521e3c58
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 62b9c20fc08fdae67e52da0283126eb26c940307973e4bb1a91f14cda435f940b58b3eeb372d6137220bedff52ec6388044140c5d786cf86d1cf7de713fc1b2c
|
|
7
|
+
data.tar.gz: f677725f1951be5496ca865485835f1e94ade8d30e1eba60dc064ea619242a5238e2ae68651e1abfb82cf61ed881e2f63f2323763152db85b75be9d810d5fe8f
|
data/Gemfile
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/semantic_memory/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-semantic-memory'
|
|
7
|
+
spec.version = Legion::Extensions::SemanticMemory::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Semantic Memory'
|
|
12
|
+
spec.description = 'Tulving semantic memory store for brain-modeled agentic AI — concept storage, ' \
|
|
13
|
+
'taxonomic relations (is_a, has_a, part_of), spreading activation retrieval, ' \
|
|
14
|
+
'and knowledge consolidation with confidence-based decay.'
|
|
15
|
+
spec.homepage = 'https://github.com/LegionIO/lex-semantic-memory'
|
|
16
|
+
spec.license = 'MIT'
|
|
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-semantic-memory'
|
|
21
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-semantic-memory'
|
|
22
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-semantic-memory'
|
|
23
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-semantic-memory/issues'
|
|
24
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
25
|
+
|
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
27
|
+
Dir.glob('{lib,spec}/**/*') + %w[lex-semantic-memory.gemspec Gemfile]
|
|
28
|
+
end
|
|
29
|
+
spec.require_paths = ['lib']
|
|
30
|
+
spec.add_development_dependency 'legion-gaia'
|
|
31
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/actors/every'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module SemanticMemory
|
|
8
|
+
module Actor
|
|
9
|
+
class Decay < Legion::Extensions::Actors::Every
|
|
10
|
+
def runner_class
|
|
11
|
+
Legion::Extensions::SemanticMemory::Runners::SemanticMemory
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def runner_function
|
|
15
|
+
'update_semantic_memory'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def time
|
|
19
|
+
300
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run_now?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def use_runner?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_subtask?
|
|
31
|
+
false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_task?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/semantic_memory/helpers/constants'
|
|
4
|
+
require 'legion/extensions/semantic_memory/helpers/concept'
|
|
5
|
+
require 'legion/extensions/semantic_memory/helpers/knowledge_store'
|
|
6
|
+
require 'legion/extensions/semantic_memory/runners/semantic_memory'
|
|
7
|
+
|
|
8
|
+
module Legion
|
|
9
|
+
module Extensions
|
|
10
|
+
module SemanticMemory
|
|
11
|
+
class Client
|
|
12
|
+
include Runners::SemanticMemory
|
|
13
|
+
|
|
14
|
+
def initialize(knowledge_store: nil, **)
|
|
15
|
+
@knowledge_store = knowledge_store || Helpers::KnowledgeStore.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :knowledge_store
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module SemanticMemory
|
|
6
|
+
module Helpers
|
|
7
|
+
class Concept
|
|
8
|
+
attr_reader :id, :name, :domain, :properties, :relations, :created_at
|
|
9
|
+
attr_accessor :confidence, :access_count
|
|
10
|
+
|
|
11
|
+
def initialize(name:, domain: :general, confidence: nil, properties: {})
|
|
12
|
+
@id = SecureRandom.uuid
|
|
13
|
+
@name = name
|
|
14
|
+
@domain = domain
|
|
15
|
+
@confidence = (confidence || Constants::DEFAULT_CONFIDENCE).clamp(0.0, 1.0)
|
|
16
|
+
@properties = properties.dup
|
|
17
|
+
@relations = []
|
|
18
|
+
@access_count = 0
|
|
19
|
+
@created_at = Time.now.utc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_relation(type:, target_name:, confidence: nil)
|
|
23
|
+
return false unless Constants::RELATION_TYPES.include?(type)
|
|
24
|
+
|
|
25
|
+
existing = @relations.find { |r| r[:type] == type && r[:target] == target_name }
|
|
26
|
+
if existing
|
|
27
|
+
existing[:confidence] = [existing[:confidence] + Constants::ACCESS_BOOST, 1.0].min
|
|
28
|
+
return existing
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
trim_relations if @relations.size >= Constants::MAX_RELATIONS_PER_CONCEPT
|
|
32
|
+
rel = { type: type, target: target_name, confidence: (confidence || Constants::DEFAULT_CONFIDENCE).clamp(0.0, 1.0) }
|
|
33
|
+
@relations << rel
|
|
34
|
+
rel
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def relations_of_type(type)
|
|
38
|
+
@relations.select { |r| r[:type] == type }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def related_concepts
|
|
42
|
+
@relations.map { |r| r[:target] }.uniq
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def set_property(key, value)
|
|
46
|
+
@properties[key] = value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def get_property(key)
|
|
50
|
+
@properties[key]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def access
|
|
54
|
+
@access_count += 1
|
|
55
|
+
@confidence = [@confidence + Constants::ACCESS_BOOST, 1.0].min
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def decay
|
|
59
|
+
@confidence = [@confidence - Constants::CONFIDENCE_DECAY, Constants::CONFIDENCE_FLOOR].max
|
|
60
|
+
@relations.each { |r| r[:confidence] = [r[:confidence] - Constants::CONFIDENCE_DECAY, Constants::CONFIDENCE_FLOOR].max }
|
|
61
|
+
@relations.reject! { |r| r[:confidence] <= Constants::CONFIDENCE_FLOOR }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def faded?
|
|
65
|
+
@confidence <= Constants::CONFIDENCE_FLOOR
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def label
|
|
69
|
+
Constants::CONFIDENCE_LABELS.each { |range, lbl| return lbl if range.cover?(@confidence) }
|
|
70
|
+
:uncertain
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def to_h
|
|
74
|
+
{
|
|
75
|
+
id: @id,
|
|
76
|
+
name: @name,
|
|
77
|
+
domain: @domain,
|
|
78
|
+
confidence: @confidence,
|
|
79
|
+
properties: @properties,
|
|
80
|
+
relations: @relations,
|
|
81
|
+
access_count: @access_count,
|
|
82
|
+
label: label,
|
|
83
|
+
created_at: @created_at
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def trim_relations
|
|
90
|
+
@relations.sort_by! { |r| r[:confidence] }
|
|
91
|
+
@relations.shift
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module SemanticMemory
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_CONCEPTS = 500
|
|
9
|
+
MAX_RELATIONS_PER_CONCEPT = 50
|
|
10
|
+
MAX_HISTORY = 200
|
|
11
|
+
|
|
12
|
+
# Relation types between concepts
|
|
13
|
+
RELATION_TYPES = %i[
|
|
14
|
+
is_a has_a part_of
|
|
15
|
+
property_of used_for
|
|
16
|
+
causes prevents
|
|
17
|
+
similar_to opposite_of
|
|
18
|
+
instance_of category_of
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
# Confidence thresholds
|
|
22
|
+
DEFAULT_CONFIDENCE = 0.5
|
|
23
|
+
CONFIDENCE_FLOOR = 0.05
|
|
24
|
+
CONFIDENCE_DECAY = 0.005
|
|
25
|
+
CONFIDENCE_ALPHA = 0.12
|
|
26
|
+
|
|
27
|
+
# Access frequency tracking
|
|
28
|
+
ACCESS_BOOST = 0.05
|
|
29
|
+
ACCESS_DECAY = 0.01
|
|
30
|
+
|
|
31
|
+
# Retrieval spreading activation
|
|
32
|
+
SPREAD_FACTOR = 0.6
|
|
33
|
+
MAX_SPREAD_HOPS = 3
|
|
34
|
+
SPREAD_THRESHOLD = 0.1
|
|
35
|
+
|
|
36
|
+
CONFIDENCE_LABELS = {
|
|
37
|
+
(0.8..) => :established,
|
|
38
|
+
(0.6...0.8) => :reliable,
|
|
39
|
+
(0.4...0.6) => :provisional,
|
|
40
|
+
(0.2...0.4) => :tentative,
|
|
41
|
+
(..0.2) => :uncertain
|
|
42
|
+
}.freeze
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module SemanticMemory
|
|
6
|
+
module Helpers
|
|
7
|
+
class KnowledgeStore
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :concepts, :retrieval_history
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@concepts = {}
|
|
14
|
+
@retrieval_history = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def store(name:, domain: :general, confidence: nil, properties: {})
|
|
18
|
+
if @concepts.key?(name)
|
|
19
|
+
existing = @concepts[name]
|
|
20
|
+
existing.access
|
|
21
|
+
properties.each { |k, v| existing.set_property(k, v) }
|
|
22
|
+
return existing
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ensure_capacity
|
|
26
|
+
concept = Concept.new(name: name, domain: domain, confidence: confidence, properties: properties)
|
|
27
|
+
@concepts[name] = concept
|
|
28
|
+
concept
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def relate(source:, target:, type:, confidence: nil)
|
|
32
|
+
store(name: source) unless @concepts.key?(source)
|
|
33
|
+
store(name: target) unless @concepts.key?(target)
|
|
34
|
+
@concepts[source].add_relation(type: type, target_name: target, confidence: confidence)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def retrieve(name:)
|
|
38
|
+
concept = @concepts[name]
|
|
39
|
+
return nil unless concept
|
|
40
|
+
|
|
41
|
+
concept.access
|
|
42
|
+
record_retrieval(name)
|
|
43
|
+
concept
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def query_relations(name:, type: nil)
|
|
47
|
+
concept = @concepts[name]
|
|
48
|
+
return [] unless concept
|
|
49
|
+
|
|
50
|
+
concept.access
|
|
51
|
+
if type
|
|
52
|
+
concept.relations_of_type(type)
|
|
53
|
+
else
|
|
54
|
+
concept.relations
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_is_a(concept_name, category_name)
|
|
59
|
+
rels = query_relations(name: concept_name, type: :is_a)
|
|
60
|
+
rels.any? { |r| r[:target] == category_name }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def instances_of(category_name)
|
|
64
|
+
@concepts.values.select do |c|
|
|
65
|
+
c.relations.any? { |r| r[:type] == :is_a && r[:target] == category_name }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def spreading_activation(seed:, hops: MAX_SPREAD_HOPS)
|
|
70
|
+
activated = {}
|
|
71
|
+
queue = [[seed, 1.0]]
|
|
72
|
+
|
|
73
|
+
hops.times do
|
|
74
|
+
next_queue = []
|
|
75
|
+
queue.each do |name, strength|
|
|
76
|
+
next if strength < SPREAD_THRESHOLD
|
|
77
|
+
next if activated.key?(name) && activated[name] >= strength
|
|
78
|
+
|
|
79
|
+
activated[name] = strength
|
|
80
|
+
concept = @concepts[name]
|
|
81
|
+
next unless concept
|
|
82
|
+
|
|
83
|
+
concept.related_concepts.each do |related|
|
|
84
|
+
next_strength = strength * SPREAD_FACTOR
|
|
85
|
+
next_queue << [related, next_strength] if next_strength >= SPREAD_THRESHOLD
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
queue = next_queue
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
activated.sort_by { |_, s| -s }.to_h
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def concepts_in_domain(domain)
|
|
95
|
+
@concepts.values.select { |c| c.domain == domain }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def search(query)
|
|
99
|
+
@concepts.values.select { |c| c.name.to_s.include?(query.to_s) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def decay_all
|
|
103
|
+
@concepts.each_value(&:decay)
|
|
104
|
+
@concepts.reject! { |_, c| c.faded? }
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def concept_count
|
|
108
|
+
@concepts.size
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def relation_count
|
|
112
|
+
@concepts.values.sum { |c| c.relations.size }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def to_h
|
|
116
|
+
{
|
|
117
|
+
concepts: concept_count,
|
|
118
|
+
relations: relation_count,
|
|
119
|
+
domains: @concepts.values.map(&:domain).uniq.size,
|
|
120
|
+
history_size: @retrieval_history.size
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def ensure_capacity
|
|
127
|
+
return if @concepts.size < MAX_CONCEPTS
|
|
128
|
+
|
|
129
|
+
weakest = @concepts.min_by { |_, c| c.confidence }
|
|
130
|
+
@concepts.delete(weakest.first) if weakest
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def record_retrieval(name)
|
|
134
|
+
@retrieval_history << { name: name, at: Time.now.utc }
|
|
135
|
+
@retrieval_history.shift while @retrieval_history.size > MAX_HISTORY
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module SemanticMemory
|
|
6
|
+
module Runners
|
|
7
|
+
module SemanticMemory
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def store_concept(name:, domain: :general, confidence: nil, properties: {}, **)
|
|
12
|
+
concept = knowledge_store.store(name: name, domain: domain, confidence: confidence, properties: properties)
|
|
13
|
+
Legion::Logging.debug "[semantic_memory] store: name=#{name} domain=#{domain} conf=#{concept.confidence.round(3)}"
|
|
14
|
+
{ success: true, concept: concept.to_h }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def relate_concepts(source:, target:, type:, confidence: nil, **)
|
|
18
|
+
type_sym = type.to_sym
|
|
19
|
+
result = knowledge_store.relate(source: source, target: target, type: type_sym, confidence: confidence)
|
|
20
|
+
Legion::Logging.debug "[semantic_memory] relate: #{source} --#{type_sym}--> #{target}"
|
|
21
|
+
{ success: true, source: source, target: target, type: type_sym, relation: result }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def retrieve_concept(name:, **)
|
|
25
|
+
concept = knowledge_store.retrieve(name: name)
|
|
26
|
+
if concept
|
|
27
|
+
Legion::Logging.debug "[semantic_memory] retrieve: name=#{name} conf=#{concept.confidence.round(3)}"
|
|
28
|
+
{ success: true, found: true, concept: concept.to_h }
|
|
29
|
+
else
|
|
30
|
+
Legion::Logging.debug "[semantic_memory] retrieve: name=#{name} not_found"
|
|
31
|
+
{ success: true, found: false, name: name }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def query_concept_relations(name:, type: nil, **)
|
|
36
|
+
relations = knowledge_store.query_relations(name: name, type: type&.to_sym)
|
|
37
|
+
Legion::Logging.debug "[semantic_memory] query_relations: name=#{name} type=#{type} count=#{relations.size}"
|
|
38
|
+
{ success: true, name: name, relations: relations, count: relations.size }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def check_category(concept:, category:, **)
|
|
42
|
+
result = knowledge_store.check_is_a(concept, category)
|
|
43
|
+
Legion::Logging.debug "[semantic_memory] check_category: #{concept} is_a #{category} = #{result}"
|
|
44
|
+
{ success: true, concept: concept, category: category, is_member: result }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def find_instances(category:, **)
|
|
48
|
+
instances = knowledge_store.instances_of(category)
|
|
49
|
+
Legion::Logging.debug "[semantic_memory] instances_of: #{category} count=#{instances.size}"
|
|
50
|
+
{ success: true, category: category, instances: instances.map(&:name), count: instances.size }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def activate_spread(seed:, hops: nil, **)
|
|
54
|
+
hop_count = hops || Helpers::Constants::MAX_SPREAD_HOPS
|
|
55
|
+
activated = knowledge_store.spreading_activation(seed: seed, hops: hop_count)
|
|
56
|
+
Legion::Logging.debug "[semantic_memory] spread: seed=#{seed} hops=#{hop_count} activated=#{activated.size}"
|
|
57
|
+
{ success: true, seed: seed, activated: activated, count: activated.size }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def concepts_in(domain:, **)
|
|
61
|
+
concepts = knowledge_store.concepts_in_domain(domain)
|
|
62
|
+
Legion::Logging.debug "[semantic_memory] domain_query: domain=#{domain} count=#{concepts.size}"
|
|
63
|
+
{ success: true, domain: domain, concepts: concepts.map(&:name), count: concepts.size }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def update_semantic_memory(**)
|
|
67
|
+
knowledge_store.decay_all
|
|
68
|
+
Legion::Logging.debug "[semantic_memory] tick: concepts=#{knowledge_store.concept_count} " \
|
|
69
|
+
"relations=#{knowledge_store.relation_count}"
|
|
70
|
+
{
|
|
71
|
+
success: true,
|
|
72
|
+
concepts: knowledge_store.concept_count,
|
|
73
|
+
relations: knowledge_store.relation_count
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def semantic_memory_stats(**)
|
|
78
|
+
{ success: true, stats: knowledge_store.to_h }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def knowledge_store
|
|
84
|
+
@knowledge_store ||= Helpers::KnowledgeStore.new
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
require 'legion/extensions/semantic_memory/version'
|
|
5
|
+
require 'legion/extensions/semantic_memory/helpers/constants'
|
|
6
|
+
require 'legion/extensions/semantic_memory/helpers/concept'
|
|
7
|
+
require 'legion/extensions/semantic_memory/helpers/knowledge_store'
|
|
8
|
+
require 'legion/extensions/semantic_memory/runners/semantic_memory'
|
|
9
|
+
require 'legion/extensions/semantic_memory/client'
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module SemanticMemory
|
|
14
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::SemanticMemory::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'includes Runners::SemanticMemory' do
|
|
7
|
+
expect(described_class.ancestors).to include(Legion::Extensions::SemanticMemory::Runners::SemanticMemory)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'supports full knowledge lifecycle' do
|
|
11
|
+
# Build a taxonomy
|
|
12
|
+
client.store_concept(name: :animal, domain: :biology, properties: { kingdom: :animalia })
|
|
13
|
+
client.relate_concepts(source: :mammal, target: :animal, type: :is_a)
|
|
14
|
+
client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
15
|
+
client.relate_concepts(source: :cat, target: :mammal, type: :is_a)
|
|
16
|
+
client.relate_concepts(source: :dog, target: :tail, type: :has_a)
|
|
17
|
+
|
|
18
|
+
# Query taxonomy
|
|
19
|
+
expect(client.check_category(concept: :dog, category: :mammal)[:is_member]).to be true
|
|
20
|
+
expect(client.find_instances(category: :mammal)[:count]).to eq(2)
|
|
21
|
+
|
|
22
|
+
# Spreading activation
|
|
23
|
+
activated = client.activate_spread(seed: :dog)
|
|
24
|
+
expect(activated[:activated]).to have_key(:dog)
|
|
25
|
+
|
|
26
|
+
# Retrieve
|
|
27
|
+
concept = client.retrieve_concept(name: :dog)
|
|
28
|
+
expect(concept[:found]).to be true
|
|
29
|
+
|
|
30
|
+
# Tick
|
|
31
|
+
client.update_semantic_memory
|
|
32
|
+
stats = client.semantic_memory_stats
|
|
33
|
+
expect(stats[:stats][:concepts]).to be >= 3
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::SemanticMemory::Helpers::Concept do
|
|
4
|
+
subject(:concept) { described_class.new(name: :dog, domain: :animals) }
|
|
5
|
+
|
|
6
|
+
describe '#initialize' do
|
|
7
|
+
it 'assigns fields' do
|
|
8
|
+
expect(concept.name).to eq(:dog)
|
|
9
|
+
expect(concept.domain).to eq(:animals)
|
|
10
|
+
expect(concept.access_count).to eq(0)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'defaults confidence to DEFAULT_CONFIDENCE' do
|
|
14
|
+
expect(concept.confidence).to eq(Legion::Extensions::SemanticMemory::Helpers::Constants::DEFAULT_CONFIDENCE)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'assigns uuid and timestamp' do
|
|
18
|
+
expect(concept.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
19
|
+
expect(concept.created_at).to be_a(Time)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'accepts custom properties' do
|
|
23
|
+
c = described_class.new(name: :cat, properties: { legs: 4 })
|
|
24
|
+
expect(c.get_property(:legs)).to eq(4)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe '#add_relation' do
|
|
29
|
+
it 'adds a typed relation' do
|
|
30
|
+
rel = concept.add_relation(type: :is_a, target_name: :mammal)
|
|
31
|
+
expect(rel[:type]).to eq(:is_a)
|
|
32
|
+
expect(rel[:target]).to eq(:mammal)
|
|
33
|
+
expect(concept.relations.size).to eq(1)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'rejects invalid relation types' do
|
|
37
|
+
expect(concept.add_relation(type: :invalid, target_name: :thing)).to be false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'reinforces existing relation instead of duplicating' do
|
|
41
|
+
concept.add_relation(type: :is_a, target_name: :mammal)
|
|
42
|
+
concept.add_relation(type: :is_a, target_name: :mammal)
|
|
43
|
+
expect(concept.relations.size).to eq(1)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#relations_of_type' do
|
|
48
|
+
it 'filters by type' do
|
|
49
|
+
concept.add_relation(type: :is_a, target_name: :mammal)
|
|
50
|
+
concept.add_relation(type: :has_a, target_name: :tail)
|
|
51
|
+
expect(concept.relations_of_type(:is_a).size).to eq(1)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe '#related_concepts' do
|
|
56
|
+
it 'lists unique related concept names' do
|
|
57
|
+
concept.add_relation(type: :is_a, target_name: :mammal)
|
|
58
|
+
concept.add_relation(type: :has_a, target_name: :tail)
|
|
59
|
+
expect(concept.related_concepts).to contain_exactly(:mammal, :tail)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#access' do
|
|
64
|
+
it 'increments count and boosts confidence' do
|
|
65
|
+
before = concept.confidence
|
|
66
|
+
concept.access
|
|
67
|
+
expect(concept.access_count).to eq(1)
|
|
68
|
+
expect(concept.confidence).to be > before
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe '#decay' do
|
|
73
|
+
it 'reduces confidence' do
|
|
74
|
+
before = concept.confidence
|
|
75
|
+
concept.decay
|
|
76
|
+
expect(concept.confidence).to be < before
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'does not drop below floor' do
|
|
80
|
+
100.times { concept.decay }
|
|
81
|
+
expect(concept.confidence).to be >= Legion::Extensions::SemanticMemory::Helpers::Constants::CONFIDENCE_FLOOR
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'prunes weak relations' do
|
|
85
|
+
concept.add_relation(type: :is_a, target_name: :mammal, confidence: 0.06)
|
|
86
|
+
100.times { concept.decay }
|
|
87
|
+
expect(concept.relations).to be_empty
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#faded?' do
|
|
92
|
+
it 'returns false for healthy concept' do
|
|
93
|
+
expect(concept.faded?).to be false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'returns true at floor' do
|
|
97
|
+
concept.confidence = Legion::Extensions::SemanticMemory::Helpers::Constants::CONFIDENCE_FLOOR
|
|
98
|
+
expect(concept.faded?).to be true
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe '#label' do
|
|
103
|
+
it 'returns :provisional for default confidence' do
|
|
104
|
+
expect(concept.label).to eq(:provisional)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'returns :established for high confidence' do
|
|
108
|
+
concept.confidence = 0.9
|
|
109
|
+
expect(concept.label).to eq(:established)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#to_h' do
|
|
114
|
+
it 'returns hash with all fields' do
|
|
115
|
+
h = concept.to_h
|
|
116
|
+
expect(h).to include(:id, :name, :domain, :confidence, :properties, :relations, :access_count, :label)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::SemanticMemory::Helpers::KnowledgeStore do
|
|
4
|
+
subject(:store) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe '#store' do
|
|
7
|
+
it 'creates a new concept' do
|
|
8
|
+
concept = store.store(name: :dog, domain: :animals)
|
|
9
|
+
expect(concept.name).to eq(:dog)
|
|
10
|
+
expect(store.concept_count).to eq(1)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'updates existing concept on re-store' do
|
|
14
|
+
store.store(name: :dog, domain: :animals)
|
|
15
|
+
store.store(name: :dog, properties: { legs: 4 })
|
|
16
|
+
expect(store.concept_count).to eq(1)
|
|
17
|
+
expect(store.retrieve(name: :dog).get_property(:legs)).to eq(4)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe '#relate' do
|
|
22
|
+
it 'creates relation between concepts' do
|
|
23
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
24
|
+
rels = store.query_relations(name: :dog, type: :is_a)
|
|
25
|
+
expect(rels.size).to eq(1)
|
|
26
|
+
expect(rels.first[:target]).to eq(:mammal)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'auto-creates concepts that do not exist' do
|
|
30
|
+
store.relate(source: :sparrow, target: :bird, type: :is_a)
|
|
31
|
+
expect(store.concept_count).to eq(2)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe '#retrieve' do
|
|
36
|
+
it 'returns concept and increments access' do
|
|
37
|
+
store.store(name: :dog, domain: :animals)
|
|
38
|
+
concept = store.retrieve(name: :dog)
|
|
39
|
+
expect(concept.access_count).to eq(1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'returns nil for unknown concept' do
|
|
43
|
+
expect(store.retrieve(name: :unknown)).to be_nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'records retrieval in history' do
|
|
47
|
+
store.store(name: :dog)
|
|
48
|
+
store.retrieve(name: :dog)
|
|
49
|
+
expect(store.retrieval_history.size).to eq(1)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe '#check_is_a' do
|
|
54
|
+
it 'returns true for valid is_a relation' do
|
|
55
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
56
|
+
expect(store.check_is_a(:dog, :mammal)).to be true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns false for non-existent relation' do
|
|
60
|
+
store.store(name: :dog)
|
|
61
|
+
expect(store.check_is_a(:dog, :reptile)).to be false
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe '#instances_of' do
|
|
66
|
+
it 'finds all instances of a category' do
|
|
67
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
68
|
+
store.relate(source: :cat, target: :mammal, type: :is_a)
|
|
69
|
+
store.relate(source: :sparrow, target: :bird, type: :is_a)
|
|
70
|
+
instances = store.instances_of(:mammal)
|
|
71
|
+
expect(instances.map(&:name)).to contain_exactly(:dog, :cat)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#spreading_activation' do
|
|
76
|
+
it 'activates seed concept' do
|
|
77
|
+
store.store(name: :dog)
|
|
78
|
+
activated = store.spreading_activation(seed: :dog)
|
|
79
|
+
expect(activated).to have_key(:dog)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'spreads to related concepts' do
|
|
83
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
84
|
+
store.relate(source: :mammal, target: :animal, type: :is_a)
|
|
85
|
+
activated = store.spreading_activation(seed: :dog, hops: 3)
|
|
86
|
+
expect(activated.keys).to include(:dog, :mammal)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'diminishes activation with each hop' do
|
|
90
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
91
|
+
activated = store.spreading_activation(seed: :dog)
|
|
92
|
+
expect(activated[:dog]).to be > activated[:mammal] if activated.key?(:mammal)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '#concepts_in_domain' do
|
|
97
|
+
it 'returns concepts in specified domain' do
|
|
98
|
+
store.store(name: :dog, domain: :animals)
|
|
99
|
+
store.store(name: :ruby, domain: :programming)
|
|
100
|
+
expect(store.concepts_in_domain(:animals).map(&:name)).to eq([:dog])
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe '#search' do
|
|
105
|
+
it 'finds concepts matching query' do
|
|
106
|
+
store.store(name: :golden_retriever)
|
|
107
|
+
store.store(name: :golden_gate)
|
|
108
|
+
store.store(name: :poodle)
|
|
109
|
+
results = store.search(:golden)
|
|
110
|
+
expect(results.size).to eq(2)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe '#decay_all' do
|
|
115
|
+
it 'decays all concepts' do
|
|
116
|
+
store.store(name: :dog, confidence: 0.5)
|
|
117
|
+
store.decay_all
|
|
118
|
+
concept = store.concepts[:dog]
|
|
119
|
+
expect(concept.confidence).to be < 0.5 if concept
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'prunes faded concepts' do
|
|
123
|
+
floor = Legion::Extensions::SemanticMemory::Helpers::Constants::CONFIDENCE_FLOOR
|
|
124
|
+
store.store(name: :weak, confidence: floor + 0.004)
|
|
125
|
+
store.decay_all
|
|
126
|
+
expect(store.concept_count).to eq(0)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
describe '#to_h' do
|
|
131
|
+
it 'returns stats' do
|
|
132
|
+
store.store(name: :dog, domain: :animals)
|
|
133
|
+
store.relate(source: :dog, target: :mammal, type: :is_a)
|
|
134
|
+
h = store.to_h
|
|
135
|
+
expect(h).to include(:concepts, :relations, :domains, :history_size)
|
|
136
|
+
expect(h[:concepts]).to eq(2)
|
|
137
|
+
expect(h[:relations]).to eq(1)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::SemanticMemory::Runners::SemanticMemory do
|
|
4
|
+
let(:client) { Legion::Extensions::SemanticMemory::Client.new }
|
|
5
|
+
|
|
6
|
+
describe '#store_concept' do
|
|
7
|
+
it 'stores a concept' do
|
|
8
|
+
result = client.store_concept(name: :dog, domain: :animals)
|
|
9
|
+
expect(result[:success]).to be true
|
|
10
|
+
expect(result[:concept][:name]).to eq(:dog)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#relate_concepts' do
|
|
15
|
+
it 'creates a relation' do
|
|
16
|
+
result = client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
17
|
+
expect(result[:success]).to be true
|
|
18
|
+
expect(result[:type]).to eq(:is_a)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe '#retrieve_concept' do
|
|
23
|
+
it 'retrieves stored concept' do
|
|
24
|
+
client.store_concept(name: :dog, domain: :animals)
|
|
25
|
+
result = client.retrieve_concept(name: :dog)
|
|
26
|
+
expect(result[:found]).to be true
|
|
27
|
+
expect(result[:concept][:name]).to eq(:dog)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'returns found: false for unknown' do
|
|
31
|
+
result = client.retrieve_concept(name: :unknown)
|
|
32
|
+
expect(result[:found]).to be false
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#query_concept_relations' do
|
|
37
|
+
it 'returns relations' do
|
|
38
|
+
client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
39
|
+
result = client.query_concept_relations(name: :dog, type: :is_a)
|
|
40
|
+
expect(result[:count]).to eq(1)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe '#check_category' do
|
|
45
|
+
it 'checks is_a membership' do
|
|
46
|
+
client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
47
|
+
result = client.check_category(concept: :dog, category: :mammal)
|
|
48
|
+
expect(result[:is_member]).to be true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'returns false for non-member' do
|
|
52
|
+
client.store_concept(name: :dog)
|
|
53
|
+
result = client.check_category(concept: :dog, category: :reptile)
|
|
54
|
+
expect(result[:is_member]).to be false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#find_instances' do
|
|
59
|
+
it 'finds instances of category' do
|
|
60
|
+
client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
61
|
+
client.relate_concepts(source: :cat, target: :mammal, type: :is_a)
|
|
62
|
+
result = client.find_instances(category: :mammal)
|
|
63
|
+
expect(result[:count]).to eq(2)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#activate_spread' do
|
|
68
|
+
it 'spreads activation from seed' do
|
|
69
|
+
client.relate_concepts(source: :dog, target: :mammal, type: :is_a)
|
|
70
|
+
result = client.activate_spread(seed: :dog)
|
|
71
|
+
expect(result[:success]).to be true
|
|
72
|
+
expect(result[:activated]).to have_key(:dog)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe '#concepts_in' do
|
|
77
|
+
it 'returns concepts in domain' do
|
|
78
|
+
client.store_concept(name: :dog, domain: :animals)
|
|
79
|
+
client.store_concept(name: :ruby, domain: :programming)
|
|
80
|
+
result = client.concepts_in(domain: :animals)
|
|
81
|
+
expect(result[:count]).to eq(1)
|
|
82
|
+
expect(result[:concepts]).to eq([:dog])
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe '#update_semantic_memory' do
|
|
87
|
+
it 'decays and returns counts' do
|
|
88
|
+
client.store_concept(name: :dog)
|
|
89
|
+
result = client.update_semantic_memory
|
|
90
|
+
expect(result[:success]).to be true
|
|
91
|
+
expect(result).to have_key(:concepts)
|
|
92
|
+
expect(result).to have_key(:relations)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe '#semantic_memory_stats' do
|
|
97
|
+
it 'returns stats' do
|
|
98
|
+
result = client.semantic_memory_stats
|
|
99
|
+
expect(result[:success]).to be true
|
|
100
|
+
expect(result[:stats]).to include(:concepts, :relations, :domains)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Logging
|
|
7
|
+
def self.debug(_msg); end
|
|
8
|
+
def self.info(_msg); end
|
|
9
|
+
def self.warn(_msg); end
|
|
10
|
+
def self.error(_msg); end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
require 'legion/extensions/semantic_memory'
|
|
15
|
+
|
|
16
|
+
RSpec.configure do |config|
|
|
17
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
|
18
|
+
config.disable_monkey_patching!
|
|
19
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
|
20
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-semantic-memory
|
|
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: Tulving semantic memory store for brain-modeled agentic AI — concept
|
|
27
|
+
storage, taxonomic relations (is_a, has_a, part_of), spreading activation retrieval,
|
|
28
|
+
and knowledge consolidation with confidence-based decay.
|
|
29
|
+
email:
|
|
30
|
+
- matthewdiverson@gmail.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- Gemfile
|
|
36
|
+
- lex-semantic-memory.gemspec
|
|
37
|
+
- lib/legion/extensions/semantic_memory.rb
|
|
38
|
+
- lib/legion/extensions/semantic_memory/actors/decay.rb
|
|
39
|
+
- lib/legion/extensions/semantic_memory/client.rb
|
|
40
|
+
- lib/legion/extensions/semantic_memory/helpers/concept.rb
|
|
41
|
+
- lib/legion/extensions/semantic_memory/helpers/constants.rb
|
|
42
|
+
- lib/legion/extensions/semantic_memory/helpers/knowledge_store.rb
|
|
43
|
+
- lib/legion/extensions/semantic_memory/runners/semantic_memory.rb
|
|
44
|
+
- lib/legion/extensions/semantic_memory/version.rb
|
|
45
|
+
- spec/legion/extensions/semantic_memory/client_spec.rb
|
|
46
|
+
- spec/legion/extensions/semantic_memory/helpers/concept_spec.rb
|
|
47
|
+
- spec/legion/extensions/semantic_memory/helpers/knowledge_store_spec.rb
|
|
48
|
+
- spec/legion/extensions/semantic_memory/runners/semantic_memory_spec.rb
|
|
49
|
+
- spec/spec_helper.rb
|
|
50
|
+
homepage: https://github.com/LegionIO/lex-semantic-memory
|
|
51
|
+
licenses:
|
|
52
|
+
- MIT
|
|
53
|
+
metadata:
|
|
54
|
+
homepage_uri: https://github.com/LegionIO/lex-semantic-memory
|
|
55
|
+
source_code_uri: https://github.com/LegionIO/lex-semantic-memory
|
|
56
|
+
documentation_uri: https://github.com/LegionIO/lex-semantic-memory
|
|
57
|
+
changelog_uri: https://github.com/LegionIO/lex-semantic-memory
|
|
58
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-semantic-memory/issues
|
|
59
|
+
rubygems_mfa_required: 'true'
|
|
60
|
+
rdoc_options: []
|
|
61
|
+
require_paths:
|
|
62
|
+
- lib
|
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.4'
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
requirements: []
|
|
74
|
+
rubygems_version: 3.6.9
|
|
75
|
+
specification_version: 4
|
|
76
|
+
summary: LEX Semantic Memory
|
|
77
|
+
test_files: []
|