lex-conceptual-metaphor 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 +13 -0
- data/lex-conceptual-metaphor.gemspec +30 -0
- data/lib/legion/extensions/conceptual_metaphor/client.rb +15 -0
- data/lib/legion/extensions/conceptual_metaphor/helpers/constants.rb +45 -0
- data/lib/legion/extensions/conceptual_metaphor/helpers/metaphor.rb +105 -0
- data/lib/legion/extensions/conceptual_metaphor/helpers/metaphor_engine.rb +150 -0
- data/lib/legion/extensions/conceptual_metaphor/runners/conceptual_metaphor.rb +103 -0
- data/lib/legion/extensions/conceptual_metaphor/version.rb +9 -0
- data/lib/legion/extensions/conceptual_metaphor.rb +16 -0
- data/spec/legion/extensions/conceptual_metaphor/client_spec.rb +29 -0
- data/spec/legion/extensions/conceptual_metaphor/helpers/metaphor_engine_spec.rb +166 -0
- data/spec/legion/extensions/conceptual_metaphor/helpers/metaphor_spec.rb +133 -0
- data/spec/legion/extensions/conceptual_metaphor/runners/conceptual_metaphor_spec.rb +133 -0
- data/spec/spec_helper.rb +34 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: fb3da9900840165ae3f63ca71850bda064b046df078cf2aea51ce64e2948adbd
|
|
4
|
+
data.tar.gz: 1ddee2263dcb341d811fb8457fb239f855efd48f51d64d58d91a91651c1a7506
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 5622988996a7f7871587dc50d28c395487819ceb4625c7a9f394f84acfc2b038a6f4307ddcab7ecd80d96a9d75517edc5d03bb30641737723081322e37a83c3f
|
|
7
|
+
data.tar.gz: 3e379a94b39799a96f1a31f252494e7fb3194125ed17669fdec7c384a89f92aa0ead653f4321f918e742799c77f149bf3a1f721149fa182c118d9a1240d94ef4
|
data/Gemfile
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/conceptual_metaphor/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-conceptual-metaphor'
|
|
7
|
+
spec.version = Legion::Extensions::ConceptualMetaphor::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Conceptual Metaphor'
|
|
12
|
+
spec.description = 'Lakoff & Johnson conceptual metaphor theory — understanding abstractions ' \
|
|
13
|
+
'through embodied metaphors for brain-modeled agentic AI'
|
|
14
|
+
spec.homepage = 'https://github.com/LegionIO/lex-conceptual-metaphor'
|
|
15
|
+
spec.license = 'MIT'
|
|
16
|
+
spec.required_ruby_version = '>= 3.4'
|
|
17
|
+
|
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-conceptual-metaphor'
|
|
20
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-conceptual-metaphor'
|
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-conceptual-metaphor'
|
|
22
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-conceptual-metaphor/issues'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
24
|
+
|
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
26
|
+
Dir.glob('{lib,spec}/**/*') + %w[lex-conceptual-metaphor.gemspec Gemfile]
|
|
27
|
+
end
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
spec.add_development_dependency 'legion-gaia'
|
|
30
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ConceptualMetaphor
|
|
6
|
+
class Client
|
|
7
|
+
include Runners::ConceptualMetaphor
|
|
8
|
+
|
|
9
|
+
def initialize(engine: nil)
|
|
10
|
+
@engine = engine || Helpers::MetaphorEngine.new
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ConceptualMetaphor
|
|
6
|
+
module Helpers
|
|
7
|
+
module Constants
|
|
8
|
+
MAX_METAPHORS = 200
|
|
9
|
+
MAX_MAPPINGS = 500
|
|
10
|
+
MAX_DOMAINS = 100
|
|
11
|
+
MAX_HISTORY = 300
|
|
12
|
+
|
|
13
|
+
DEFAULT_STRENGTH = 0.5
|
|
14
|
+
STRENGTH_FLOOR = 0.0
|
|
15
|
+
STRENGTH_CEILING = 1.0
|
|
16
|
+
|
|
17
|
+
REINFORCEMENT_BOOST = 0.1
|
|
18
|
+
DECAY_RATE = 0.02
|
|
19
|
+
STALE_THRESHOLD = 120
|
|
20
|
+
|
|
21
|
+
CONVENTIONALITY_THRESHOLD = 0.7
|
|
22
|
+
NOVELTY_THRESHOLD = 0.3
|
|
23
|
+
|
|
24
|
+
METAPHOR_TYPES = %i[structural orientational ontological].freeze
|
|
25
|
+
|
|
26
|
+
CONVENTIONALITY_LABELS = {
|
|
27
|
+
(0.8..) => :dead,
|
|
28
|
+
(0.6...0.8) => :conventional,
|
|
29
|
+
(0.4...0.6) => :familiar,
|
|
30
|
+
(0.2...0.4) => :novel,
|
|
31
|
+
(..0.2) => :creative
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
STRENGTH_LABELS = {
|
|
35
|
+
(0.8..) => :dominant,
|
|
36
|
+
(0.6...0.8) => :strong,
|
|
37
|
+
(0.4...0.6) => :moderate,
|
|
38
|
+
(0.2...0.4) => :weak,
|
|
39
|
+
(..0.2) => :fading
|
|
40
|
+
}.freeze
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module ConceptualMetaphor
|
|
8
|
+
module Helpers
|
|
9
|
+
class Metaphor
|
|
10
|
+
include Constants
|
|
11
|
+
|
|
12
|
+
attr_reader :id, :source_domain, :target_domain, :metaphor_type,
|
|
13
|
+
:mappings, :entailments, :strength, :conventionality,
|
|
14
|
+
:use_count, :created_at, :last_used_at
|
|
15
|
+
|
|
16
|
+
def initialize(source_domain:, target_domain:, metaphor_type:,
|
|
17
|
+
mappings:, strength: nil, conventionality: nil)
|
|
18
|
+
@id = SecureRandom.uuid
|
|
19
|
+
@source_domain = source_domain
|
|
20
|
+
@target_domain = target_domain
|
|
21
|
+
@metaphor_type = metaphor_type
|
|
22
|
+
@mappings = mappings
|
|
23
|
+
@entailments = []
|
|
24
|
+
@strength = (strength || DEFAULT_STRENGTH).to_f.clamp(STRENGTH_FLOOR, STRENGTH_CEILING)
|
|
25
|
+
@conventionality = (conventionality || DEFAULT_STRENGTH).to_f.clamp(STRENGTH_FLOOR, STRENGTH_CEILING)
|
|
26
|
+
@use_count = 0
|
|
27
|
+
@created_at = Time.now.utc
|
|
28
|
+
@last_used_at = @created_at
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def use!
|
|
32
|
+
@use_count += 1
|
|
33
|
+
@last_used_at = Time.now.utc
|
|
34
|
+
@strength = (@strength + REINFORCEMENT_BOOST).clamp(STRENGTH_FLOOR, STRENGTH_CEILING)
|
|
35
|
+
increase_conventionality
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_entailment(entailment)
|
|
39
|
+
@entailments << entailment
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def map_concept(source_concept)
|
|
43
|
+
@mappings[source_concept]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def coverage
|
|
47
|
+
return 0.0 if @mappings.empty?
|
|
48
|
+
|
|
49
|
+
@mappings.values.compact.size.to_f / @mappings.size
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def conventional?
|
|
53
|
+
@conventionality >= CONVENTIONALITY_THRESHOLD
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def novel?
|
|
57
|
+
@conventionality <= NOVELTY_THRESHOLD
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def conventionality_label
|
|
61
|
+
CONVENTIONALITY_LABELS.find { |range, _| range.cover?(@conventionality) }&.last || :unknown
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def strength_label
|
|
65
|
+
STRENGTH_LABELS.find { |range, _| range.cover?(@strength) }&.last || :unknown
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def decay!
|
|
69
|
+
@strength = (@strength - DECAY_RATE).clamp(STRENGTH_FLOOR, STRENGTH_CEILING)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def stale?
|
|
73
|
+
(Time.now.utc - @last_used_at) > STALE_THRESHOLD
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_h
|
|
77
|
+
{
|
|
78
|
+
id: @id,
|
|
79
|
+
source_domain: @source_domain,
|
|
80
|
+
target_domain: @target_domain,
|
|
81
|
+
metaphor_type: @metaphor_type,
|
|
82
|
+
mappings: @mappings,
|
|
83
|
+
entailments: @entailments,
|
|
84
|
+
strength: @strength,
|
|
85
|
+
conventionality: @conventionality,
|
|
86
|
+
conventionality_label: conventionality_label,
|
|
87
|
+
strength_label: strength_label,
|
|
88
|
+
use_count: @use_count,
|
|
89
|
+
coverage: coverage,
|
|
90
|
+
created_at: @created_at,
|
|
91
|
+
last_used_at: @last_used_at
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def increase_conventionality
|
|
98
|
+
increment = REINFORCEMENT_BOOST * 0.5
|
|
99
|
+
@conventionality = (@conventionality + increment).clamp(STRENGTH_FLOOR, STRENGTH_CEILING)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ConceptualMetaphor
|
|
6
|
+
module Helpers
|
|
7
|
+
class MetaphorEngine
|
|
8
|
+
include Constants
|
|
9
|
+
|
|
10
|
+
attr_reader :history
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@metaphors = {}
|
|
14
|
+
@domains = Set.new
|
|
15
|
+
@history = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_metaphor(source_domain:, target_domain:, metaphor_type:,
|
|
19
|
+
mappings:, strength: nil, conventionality: nil)
|
|
20
|
+
evict_oldest if @metaphors.size >= MAX_METAPHORS
|
|
21
|
+
|
|
22
|
+
return { success: false, reason: :invalid_metaphor_type } unless METAPHOR_TYPES.include?(metaphor_type)
|
|
23
|
+
|
|
24
|
+
metaphor = Metaphor.new(
|
|
25
|
+
source_domain: source_domain,
|
|
26
|
+
target_domain: target_domain,
|
|
27
|
+
metaphor_type: metaphor_type,
|
|
28
|
+
mappings: mappings,
|
|
29
|
+
strength: strength,
|
|
30
|
+
conventionality: conventionality
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@metaphors[metaphor.id] = metaphor
|
|
34
|
+
register_domain(source_domain)
|
|
35
|
+
register_domain(target_domain)
|
|
36
|
+
record_history(:created, metaphor.id)
|
|
37
|
+
metaphor
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def apply_metaphor(metaphor_id:, source_concept:)
|
|
41
|
+
metaphor = @metaphors[metaphor_id]
|
|
42
|
+
return { found: false } unless metaphor
|
|
43
|
+
|
|
44
|
+
target = metaphor.map_concept(source_concept)
|
|
45
|
+
return { found: true, mapped: false } unless target
|
|
46
|
+
|
|
47
|
+
metaphor.use!
|
|
48
|
+
record_history(:applied, metaphor_id)
|
|
49
|
+
build_apply_result(metaphor, source_concept, target)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def add_entailment(metaphor_id:, entailment:)
|
|
53
|
+
metaphor = @metaphors[metaphor_id]
|
|
54
|
+
return { success: false, reason: :not_found } unless metaphor
|
|
55
|
+
|
|
56
|
+
metaphor.add_entailment(entailment)
|
|
57
|
+
record_history(:entailment_added, metaphor_id)
|
|
58
|
+
{ success: true, metaphor_id: metaphor_id, entailment_count: metaphor.entailments.size }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def find_by_target(target_domain:)
|
|
62
|
+
@metaphors.values.select { |m| m.target_domain == target_domain }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def find_by_source(source_domain:)
|
|
66
|
+
@metaphors.values.select { |m| m.source_domain == source_domain }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def find_by_domain(domain:)
|
|
70
|
+
@metaphors.values.select do |m|
|
|
71
|
+
m.source_domain == domain || m.target_domain == domain
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def conventional_metaphors
|
|
76
|
+
@metaphors.values.select(&:conventional?)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def novel_metaphors
|
|
80
|
+
@metaphors.values.select(&:novel?)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def strongest(limit: 5)
|
|
84
|
+
@metaphors.values.sort_by { |m| -m.strength }.first(limit)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def by_type(metaphor_type:)
|
|
88
|
+
@metaphors.values.select { |m| m.metaphor_type == metaphor_type }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def decay_all
|
|
92
|
+
@metaphors.each_value(&:decay!)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def prune_weak
|
|
96
|
+
weak_ids = @metaphors.select { |_id, m| m.strength <= 0.05 }.keys
|
|
97
|
+
weak_ids.each { |id| @metaphors.delete(id) }
|
|
98
|
+
weak_ids.size
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def to_h
|
|
102
|
+
{
|
|
103
|
+
total_metaphors: @metaphors.size,
|
|
104
|
+
total_domains: @domains.size,
|
|
105
|
+
conventional_count: conventional_metaphors.size,
|
|
106
|
+
novel_count: novel_metaphors.size,
|
|
107
|
+
history_count: @history.size,
|
|
108
|
+
domains: @domains.to_a,
|
|
109
|
+
type_counts: type_counts
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def build_apply_result(metaphor, source_concept, target)
|
|
116
|
+
{
|
|
117
|
+
found: true,
|
|
118
|
+
mapped: true,
|
|
119
|
+
source_concept: source_concept,
|
|
120
|
+
target_concept: target,
|
|
121
|
+
strength: metaphor.strength,
|
|
122
|
+
metaphor_id: metaphor.id
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def register_domain(domain)
|
|
127
|
+
@domains.add(domain)
|
|
128
|
+
@domains.delete(@domains.first) if @domains.size > MAX_DOMAINS
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def evict_oldest
|
|
132
|
+
oldest_id = @metaphors.min_by { |_id, m| m.last_used_at }&.first
|
|
133
|
+
@metaphors.delete(oldest_id) if oldest_id
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def record_history(event, metaphor_id)
|
|
137
|
+
@history << { event: event, metaphor_id: metaphor_id, at: Time.now.utc }
|
|
138
|
+
@history.shift while @history.size > MAX_HISTORY
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def type_counts
|
|
142
|
+
@metaphors.values.each_with_object(Hash.new(0)) do |m, counts|
|
|
143
|
+
counts[m.metaphor_type] += 1
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module ConceptualMetaphor
|
|
6
|
+
module Runners
|
|
7
|
+
module ConceptualMetaphor
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
9
|
+
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
|
|
11
|
+
def create_metaphor(source_domain:, target_domain:, metaphor_type:,
|
|
12
|
+
mappings:, strength: nil, conventionality: nil, **)
|
|
13
|
+
unless Helpers::Constants::METAPHOR_TYPES.include?(metaphor_type)
|
|
14
|
+
return { success: false, error: :invalid_metaphor_type,
|
|
15
|
+
valid_types: Helpers::Constants::METAPHOR_TYPES }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
result = engine.create_metaphor(
|
|
19
|
+
source_domain: source_domain,
|
|
20
|
+
target_domain: target_domain,
|
|
21
|
+
metaphor_type: metaphor_type,
|
|
22
|
+
mappings: mappings,
|
|
23
|
+
strength: strength,
|
|
24
|
+
conventionality: conventionality
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return result unless result.is_a?(Helpers::Metaphor)
|
|
28
|
+
|
|
29
|
+
Legion::Logging.debug "[conceptual_metaphor] created #{source_domain}->#{target_domain} " \
|
|
30
|
+
"type=#{metaphor_type} id=#{result.id[0..7]}"
|
|
31
|
+
{ success: true, metaphor_id: result.id, source_domain: source_domain,
|
|
32
|
+
target_domain: target_domain, metaphor_type: metaphor_type,
|
|
33
|
+
strength: result.strength, conventionality: result.conventionality }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def apply_metaphor(metaphor_id:, source_concept:, **)
|
|
37
|
+
result = engine.apply_metaphor(metaphor_id: metaphor_id, source_concept: source_concept)
|
|
38
|
+
Legion::Logging.debug "[conceptual_metaphor] apply id=#{metaphor_id[0..7]} " \
|
|
39
|
+
"concept=#{source_concept} mapped=#{result[:mapped]}"
|
|
40
|
+
{ success: true }.merge(result)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def add_metaphor_entailment(metaphor_id:, entailment:, **)
|
|
44
|
+
result = engine.add_entailment(metaphor_id: metaphor_id, entailment: entailment)
|
|
45
|
+
Legion::Logging.debug "[conceptual_metaphor] entailment id=#{metaphor_id[0..7]} " \
|
|
46
|
+
"success=#{result[:success]}"
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def find_metaphors_for(domain:, **)
|
|
51
|
+
metaphors = engine.find_by_domain(domain: domain)
|
|
52
|
+
Legion::Logging.debug "[conceptual_metaphor] find domain=#{domain} count=#{metaphors.size}"
|
|
53
|
+
{ success: true, domain: domain, metaphors: metaphors.map(&:to_h), count: metaphors.size }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def conventional_metaphors(**)
|
|
57
|
+
metaphors = engine.conventional_metaphors
|
|
58
|
+
Legion::Logging.debug "[conceptual_metaphor] conventional count=#{metaphors.size}"
|
|
59
|
+
{ success: true, metaphors: metaphors.map(&:to_h), count: metaphors.size }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def novel_metaphors(**)
|
|
63
|
+
metaphors = engine.novel_metaphors
|
|
64
|
+
Legion::Logging.debug "[conceptual_metaphor] novel count=#{metaphors.size}"
|
|
65
|
+
{ success: true, metaphors: metaphors.map(&:to_h), count: metaphors.size }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def strongest_metaphors(limit: 5, **)
|
|
69
|
+
metaphors = engine.strongest(limit: limit)
|
|
70
|
+
Legion::Logging.debug "[conceptual_metaphor] strongest limit=#{limit} count=#{metaphors.size}"
|
|
71
|
+
{ success: true, metaphors: metaphors.map(&:to_h), count: metaphors.size }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def metaphors_by_type(metaphor_type:, **)
|
|
75
|
+
metaphors = engine.by_type(metaphor_type: metaphor_type)
|
|
76
|
+
Legion::Logging.debug "[conceptual_metaphor] by_type=#{metaphor_type} count=#{metaphors.size}"
|
|
77
|
+
{ success: true, metaphor_type: metaphor_type, metaphors: metaphors.map(&:to_h),
|
|
78
|
+
count: metaphors.size }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def update_conceptual_metaphor(**)
|
|
82
|
+
engine.decay_all
|
|
83
|
+
pruned = engine.prune_weak
|
|
84
|
+
Legion::Logging.debug "[conceptual_metaphor] decay+prune pruned=#{pruned}"
|
|
85
|
+
{ success: true, pruned: pruned }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def conceptual_metaphor_stats(**)
|
|
89
|
+
stats = engine.to_h
|
|
90
|
+
Legion::Logging.debug "[conceptual_metaphor] stats total=#{stats[:total_metaphors]}"
|
|
91
|
+
{ success: true }.merge(stats)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def engine
|
|
97
|
+
@engine ||= Helpers::MetaphorEngine.new
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/conceptual_metaphor/version'
|
|
4
|
+
require 'legion/extensions/conceptual_metaphor/helpers/constants'
|
|
5
|
+
require 'legion/extensions/conceptual_metaphor/helpers/metaphor'
|
|
6
|
+
require 'legion/extensions/conceptual_metaphor/helpers/metaphor_engine'
|
|
7
|
+
require 'legion/extensions/conceptual_metaphor/runners/conceptual_metaphor'
|
|
8
|
+
require 'legion/extensions/conceptual_metaphor/client'
|
|
9
|
+
|
|
10
|
+
module Legion
|
|
11
|
+
module Extensions
|
|
12
|
+
module ConceptualMetaphor
|
|
13
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::ConceptualMetaphor::Client do
|
|
4
|
+
subject(:client) { described_class.new }
|
|
5
|
+
|
|
6
|
+
it 'creates a metaphor' do
|
|
7
|
+
result = client.create_metaphor(
|
|
8
|
+
source_domain: :money, target_domain: :time,
|
|
9
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
10
|
+
)
|
|
11
|
+
expect(result[:success]).to be true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'applies a metaphor' do
|
|
15
|
+
created = client.create_metaphor(
|
|
16
|
+
source_domain: :money, target_domain: :time,
|
|
17
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
18
|
+
)
|
|
19
|
+
result = client.apply_metaphor(
|
|
20
|
+
metaphor_id: created[:metaphor_id], source_concept: :spend
|
|
21
|
+
)
|
|
22
|
+
expect(result[:target_concept]).to eq(:waste)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'returns stats' do
|
|
26
|
+
result = client.conceptual_metaphor_stats
|
|
27
|
+
expect(result[:success]).to be true
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::ConceptualMetaphor::Helpers::MetaphorEngine do
|
|
4
|
+
subject(:engine) { described_class.new }
|
|
5
|
+
|
|
6
|
+
let(:metaphor) do
|
|
7
|
+
engine.create_metaphor(
|
|
8
|
+
source_domain: :money,
|
|
9
|
+
target_domain: :time,
|
|
10
|
+
metaphor_type: :structural,
|
|
11
|
+
mappings: { spend: :waste, save: :conserve }
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#create_metaphor' do
|
|
16
|
+
it 'creates and stores a metaphor' do
|
|
17
|
+
result = metaphor
|
|
18
|
+
expect(result).to be_a(Legion::Extensions::ConceptualMetaphor::Helpers::Metaphor)
|
|
19
|
+
expect(result.source_domain).to eq(:money)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'rejects invalid metaphor types' do
|
|
23
|
+
result = engine.create_metaphor(
|
|
24
|
+
source_domain: :a, target_domain: :b,
|
|
25
|
+
metaphor_type: :invalid, mappings: {}
|
|
26
|
+
)
|
|
27
|
+
expect(result[:success]).to be false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'records history' do
|
|
31
|
+
metaphor
|
|
32
|
+
expect(engine.history.size).to eq(1)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe '#apply_metaphor' do
|
|
37
|
+
it 'maps a source concept to target' do
|
|
38
|
+
result = engine.apply_metaphor(metaphor_id: metaphor.id, source_concept: :spend)
|
|
39
|
+
expect(result[:mapped]).to be true
|
|
40
|
+
expect(result[:target_concept]).to eq(:waste)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'returns found: false for unknown metaphor' do
|
|
44
|
+
result = engine.apply_metaphor(metaphor_id: 'nonexistent', source_concept: :spend)
|
|
45
|
+
expect(result[:found]).to be false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'returns mapped: false for unmapped concept' do
|
|
49
|
+
result = engine.apply_metaphor(metaphor_id: metaphor.id, source_concept: :borrow)
|
|
50
|
+
expect(result[:mapped]).to be false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe '#add_entailment' do
|
|
55
|
+
it 'adds entailment to metaphor' do
|
|
56
|
+
result = engine.add_entailment(metaphor_id: metaphor.id, entailment: 'time is valuable')
|
|
57
|
+
expect(result[:success]).to be true
|
|
58
|
+
expect(result[:entailment_count]).to eq(1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'returns error for unknown metaphor' do
|
|
62
|
+
result = engine.add_entailment(metaphor_id: 'bad', entailment: 'test')
|
|
63
|
+
expect(result[:success]).to be false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#find_by_domain' do
|
|
68
|
+
it 'finds metaphors involving the domain' do
|
|
69
|
+
metaphor
|
|
70
|
+
results = engine.find_by_domain(domain: :time)
|
|
71
|
+
expect(results.size).to eq(1)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#find_by_source' do
|
|
76
|
+
it 'finds metaphors by source domain' do
|
|
77
|
+
metaphor
|
|
78
|
+
results = engine.find_by_source(source_domain: :money)
|
|
79
|
+
expect(results.size).to eq(1)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '#find_by_target' do
|
|
84
|
+
it 'finds metaphors by target domain' do
|
|
85
|
+
metaphor
|
|
86
|
+
results = engine.find_by_target(target_domain: :time)
|
|
87
|
+
expect(results.size).to eq(1)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe '#conventional_metaphors' do
|
|
92
|
+
it 'returns metaphors with high conventionality' do
|
|
93
|
+
engine.create_metaphor(
|
|
94
|
+
source_domain: :war, target_domain: :argument,
|
|
95
|
+
metaphor_type: :structural, mappings: { attack: :criticize },
|
|
96
|
+
conventionality: 0.9
|
|
97
|
+
)
|
|
98
|
+
expect(engine.conventional_metaphors.size).to eq(1)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe '#novel_metaphors' do
|
|
103
|
+
it 'returns metaphors with low conventionality' do
|
|
104
|
+
engine.create_metaphor(
|
|
105
|
+
source_domain: :ocean, target_domain: :emotion,
|
|
106
|
+
metaphor_type: :ontological, mappings: { depth: :intensity },
|
|
107
|
+
conventionality: 0.1
|
|
108
|
+
)
|
|
109
|
+
expect(engine.novel_metaphors.size).to eq(1)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
describe '#strongest' do
|
|
114
|
+
it 'returns metaphors sorted by strength' do
|
|
115
|
+
metaphor
|
|
116
|
+
engine.create_metaphor(
|
|
117
|
+
source_domain: :war, target_domain: :argument,
|
|
118
|
+
metaphor_type: :structural, mappings: {}, strength: 0.9
|
|
119
|
+
)
|
|
120
|
+
results = engine.strongest(limit: 2)
|
|
121
|
+
expect(results.first.strength).to be >= results.last.strength
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
describe '#by_type' do
|
|
126
|
+
it 'filters by metaphor type' do
|
|
127
|
+
metaphor
|
|
128
|
+
engine.create_metaphor(
|
|
129
|
+
source_domain: :container, target_domain: :mind,
|
|
130
|
+
metaphor_type: :ontological, mappings: { full: :knowledgeable }
|
|
131
|
+
)
|
|
132
|
+
structural = engine.by_type(metaphor_type: :structural)
|
|
133
|
+
expect(structural.size).to eq(1)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
describe '#decay_all' do
|
|
138
|
+
it 'reduces strength of all metaphors' do
|
|
139
|
+
original = metaphor.strength
|
|
140
|
+
engine.decay_all
|
|
141
|
+
expect(metaphor.strength).to be < original
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
describe '#prune_weak' do
|
|
146
|
+
it 'removes very weak metaphors' do
|
|
147
|
+
weak = engine.create_metaphor(
|
|
148
|
+
source_domain: :a, target_domain: :b,
|
|
149
|
+
metaphor_type: :structural, mappings: {}, strength: 0.03
|
|
150
|
+
)
|
|
151
|
+
30.times { weak.decay! }
|
|
152
|
+
pruned = engine.prune_weak
|
|
153
|
+
expect(pruned).to be >= 1
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
describe '#to_h' do
|
|
158
|
+
it 'returns summary stats' do
|
|
159
|
+
metaphor
|
|
160
|
+
stats = engine.to_h
|
|
161
|
+
expect(stats[:total_metaphors]).to eq(1)
|
|
162
|
+
expect(stats[:total_domains]).to eq(2)
|
|
163
|
+
expect(stats).to include(:type_counts, :conventional_count, :novel_count)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::ConceptualMetaphor::Helpers::Metaphor do
|
|
4
|
+
subject(:metaphor) do
|
|
5
|
+
described_class.new(
|
|
6
|
+
source_domain: :money,
|
|
7
|
+
target_domain: :time,
|
|
8
|
+
metaphor_type: :structural,
|
|
9
|
+
mappings: { spend: :waste, save: :conserve, invest: :dedicate },
|
|
10
|
+
strength: 0.6,
|
|
11
|
+
conventionality: 0.8
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#initialize' do
|
|
16
|
+
it 'assigns a UUID id' do
|
|
17
|
+
expect(metaphor.id).to match(/\A[0-9a-f-]{36}\z/)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'stores source and target domains' do
|
|
21
|
+
expect(metaphor.source_domain).to eq(:money)
|
|
22
|
+
expect(metaphor.target_domain).to eq(:time)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'stores metaphor type' do
|
|
26
|
+
expect(metaphor.metaphor_type).to eq(:structural)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'stores mappings' do
|
|
30
|
+
expect(metaphor.mappings).to eq({ spend: :waste, save: :conserve, invest: :dedicate })
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'clamps strength within bounds' do
|
|
34
|
+
over = described_class.new(source_domain: :a, target_domain: :b, metaphor_type: :structural,
|
|
35
|
+
mappings: {}, strength: 2.0)
|
|
36
|
+
expect(over.strength).to eq(1.0)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe '#use!' do
|
|
41
|
+
it 'increments use count' do
|
|
42
|
+
expect { metaphor.use! }.to change(metaphor, :use_count).by(1)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'boosts strength' do
|
|
46
|
+
original = metaphor.strength
|
|
47
|
+
metaphor.use!
|
|
48
|
+
expect(metaphor.strength).to be > original
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'increases conventionality' do
|
|
52
|
+
original = metaphor.conventionality
|
|
53
|
+
metaphor.use!
|
|
54
|
+
expect(metaphor.conventionality).to be >= original
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#map_concept' do
|
|
59
|
+
it 'returns mapped target concept' do
|
|
60
|
+
expect(metaphor.map_concept(:spend)).to eq(:waste)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'returns nil for unmapped concept' do
|
|
64
|
+
expect(metaphor.map_concept(:borrow)).to be_nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe '#coverage' do
|
|
69
|
+
it 'returns ratio of mapped concepts' do
|
|
70
|
+
expect(metaphor.coverage).to eq(1.0)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'returns 0.0 for empty mappings' do
|
|
74
|
+
empty = described_class.new(source_domain: :a, target_domain: :b,
|
|
75
|
+
metaphor_type: :structural, mappings: {})
|
|
76
|
+
expect(empty.coverage).to eq(0.0)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe '#conventional?' do
|
|
81
|
+
it 'returns true when conventionality is high' do
|
|
82
|
+
expect(metaphor).to be_conventional
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
describe '#novel?' do
|
|
87
|
+
it 'returns false when conventionality is high' do
|
|
88
|
+
expect(metaphor).not_to be_novel
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'returns true when conventionality is low' do
|
|
92
|
+
fresh = described_class.new(source_domain: :a, target_domain: :b,
|
|
93
|
+
metaphor_type: :structural, mappings: {},
|
|
94
|
+
conventionality: 0.2)
|
|
95
|
+
expect(fresh).to be_novel
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe '#conventionality_label' do
|
|
100
|
+
it 'returns a label symbol' do
|
|
101
|
+
expect(metaphor.conventionality_label).to eq(:dead)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe '#strength_label' do
|
|
106
|
+
it 'returns a label symbol' do
|
|
107
|
+
expect(metaphor.strength_label).to be_a(Symbol)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe '#decay!' do
|
|
112
|
+
it 'reduces strength' do
|
|
113
|
+
original = metaphor.strength
|
|
114
|
+
metaphor.decay!
|
|
115
|
+
expect(metaphor.strength).to be < original
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#add_entailment' do
|
|
120
|
+
it 'adds entailment to the list' do
|
|
121
|
+
metaphor.add_entailment('wasting time is losing money')
|
|
122
|
+
expect(metaphor.entailments).to include('wasting time is losing money')
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe '#to_h' do
|
|
127
|
+
it 'returns a hash representation' do
|
|
128
|
+
hash = metaphor.to_h
|
|
129
|
+
expect(hash).to include(:id, :source_domain, :target_domain, :mappings,
|
|
130
|
+
:strength, :conventionality, :coverage)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Legion::Extensions::ConceptualMetaphor::Runners::ConceptualMetaphor do
|
|
4
|
+
let(:runner_host) do
|
|
5
|
+
obj = Object.new
|
|
6
|
+
obj.extend(described_class)
|
|
7
|
+
obj
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe '#create_metaphor' do
|
|
11
|
+
it 'creates a metaphor successfully' do
|
|
12
|
+
result = runner_host.create_metaphor(
|
|
13
|
+
source_domain: :money, target_domain: :time,
|
|
14
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
15
|
+
)
|
|
16
|
+
expect(result[:success]).to be true
|
|
17
|
+
expect(result[:metaphor_id]).to be_a(String)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'rejects invalid metaphor type' do
|
|
21
|
+
result = runner_host.create_metaphor(
|
|
22
|
+
source_domain: :a, target_domain: :b,
|
|
23
|
+
metaphor_type: :invalid, mappings: {}
|
|
24
|
+
)
|
|
25
|
+
expect(result[:success]).to be false
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#apply_metaphor' do
|
|
30
|
+
it 'maps a concept through a metaphor' do
|
|
31
|
+
created = runner_host.create_metaphor(
|
|
32
|
+
source_domain: :money, target_domain: :time,
|
|
33
|
+
metaphor_type: :structural, mappings: { spend: :waste }
|
|
34
|
+
)
|
|
35
|
+
result = runner_host.apply_metaphor(
|
|
36
|
+
metaphor_id: created[:metaphor_id], source_concept: :spend
|
|
37
|
+
)
|
|
38
|
+
expect(result[:success]).to be true
|
|
39
|
+
expect(result[:target_concept]).to eq(:waste)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe '#add_metaphor_entailment' do
|
|
44
|
+
it 'adds an entailment' do
|
|
45
|
+
created = runner_host.create_metaphor(
|
|
46
|
+
source_domain: :money, target_domain: :time,
|
|
47
|
+
metaphor_type: :structural, mappings: {}
|
|
48
|
+
)
|
|
49
|
+
result = runner_host.add_metaphor_entailment(
|
|
50
|
+
metaphor_id: created[:metaphor_id],
|
|
51
|
+
entailment: 'wasting time is losing money'
|
|
52
|
+
)
|
|
53
|
+
expect(result[:success]).to be true
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe '#find_metaphors_for' do
|
|
58
|
+
it 'finds metaphors for a domain' do
|
|
59
|
+
runner_host.create_metaphor(
|
|
60
|
+
source_domain: :money, target_domain: :time,
|
|
61
|
+
metaphor_type: :structural, mappings: {}
|
|
62
|
+
)
|
|
63
|
+
result = runner_host.find_metaphors_for(domain: :time)
|
|
64
|
+
expect(result[:success]).to be true
|
|
65
|
+
expect(result[:count]).to eq(1)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
describe '#conventional_metaphors' do
|
|
70
|
+
it 'returns conventional metaphors' do
|
|
71
|
+
runner_host.create_metaphor(
|
|
72
|
+
source_domain: :war, target_domain: :argument,
|
|
73
|
+
metaphor_type: :structural, mappings: {},
|
|
74
|
+
conventionality: 0.9
|
|
75
|
+
)
|
|
76
|
+
result = runner_host.conventional_metaphors
|
|
77
|
+
expect(result[:success]).to be true
|
|
78
|
+
expect(result[:count]).to eq(1)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '#novel_metaphors' do
|
|
83
|
+
it 'returns novel metaphors' do
|
|
84
|
+
runner_host.create_metaphor(
|
|
85
|
+
source_domain: :ocean, target_domain: :emotion,
|
|
86
|
+
metaphor_type: :ontological, mappings: {},
|
|
87
|
+
conventionality: 0.1
|
|
88
|
+
)
|
|
89
|
+
result = runner_host.novel_metaphors
|
|
90
|
+
expect(result[:success]).to be true
|
|
91
|
+
expect(result[:count]).to eq(1)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe '#strongest_metaphors' do
|
|
96
|
+
it 'returns strongest metaphors' do
|
|
97
|
+
runner_host.create_metaphor(
|
|
98
|
+
source_domain: :a, target_domain: :b,
|
|
99
|
+
metaphor_type: :structural, mappings: {}, strength: 0.9
|
|
100
|
+
)
|
|
101
|
+
result = runner_host.strongest_metaphors(limit: 3)
|
|
102
|
+
expect(result[:success]).to be true
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
describe '#metaphors_by_type' do
|
|
107
|
+
it 'filters by type' do
|
|
108
|
+
runner_host.create_metaphor(
|
|
109
|
+
source_domain: :a, target_domain: :b,
|
|
110
|
+
metaphor_type: :orientational, mappings: {}
|
|
111
|
+
)
|
|
112
|
+
result = runner_host.metaphors_by_type(metaphor_type: :orientational)
|
|
113
|
+
expect(result[:success]).to be true
|
|
114
|
+
expect(result[:count]).to eq(1)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe '#update_conceptual_metaphor' do
|
|
119
|
+
it 'runs decay and prune cycle' do
|
|
120
|
+
result = runner_host.update_conceptual_metaphor
|
|
121
|
+
expect(result[:success]).to be true
|
|
122
|
+
expect(result).to include(:pruned)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe '#conceptual_metaphor_stats' do
|
|
127
|
+
it 'returns stats' do
|
|
128
|
+
result = runner_host.conceptual_metaphor_stats
|
|
129
|
+
expect(result[:success]).to be true
|
|
130
|
+
expect(result).to include(:total_metaphors, :total_domains)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Helpers
|
|
6
|
+
module Lex; end
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Logging
|
|
11
|
+
def self.debug(*); end
|
|
12
|
+
|
|
13
|
+
def self.info(*); end
|
|
14
|
+
|
|
15
|
+
def self.warn(*); end
|
|
16
|
+
|
|
17
|
+
def self.error(*); end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'legion/extensions/conceptual_metaphor'
|
|
22
|
+
|
|
23
|
+
RSpec.configure do |config|
|
|
24
|
+
config.expect_with :rspec do |expectations|
|
|
25
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
config.mock_with :rspec do |mocks|
|
|
29
|
+
mocks.verify_partial_doubles = true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
config.filter_run_when_matching :focus
|
|
33
|
+
config.order = :random
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-conceptual-metaphor
|
|
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: Lakoff & Johnson conceptual metaphor theory — understanding abstractions
|
|
27
|
+
through embodied metaphors for brain-modeled agentic AI
|
|
28
|
+
email:
|
|
29
|
+
- matthewdiverson@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- Gemfile
|
|
35
|
+
- lex-conceptual-metaphor.gemspec
|
|
36
|
+
- lib/legion/extensions/conceptual_metaphor.rb
|
|
37
|
+
- lib/legion/extensions/conceptual_metaphor/client.rb
|
|
38
|
+
- lib/legion/extensions/conceptual_metaphor/helpers/constants.rb
|
|
39
|
+
- lib/legion/extensions/conceptual_metaphor/helpers/metaphor.rb
|
|
40
|
+
- lib/legion/extensions/conceptual_metaphor/helpers/metaphor_engine.rb
|
|
41
|
+
- lib/legion/extensions/conceptual_metaphor/runners/conceptual_metaphor.rb
|
|
42
|
+
- lib/legion/extensions/conceptual_metaphor/version.rb
|
|
43
|
+
- spec/legion/extensions/conceptual_metaphor/client_spec.rb
|
|
44
|
+
- spec/legion/extensions/conceptual_metaphor/helpers/metaphor_engine_spec.rb
|
|
45
|
+
- spec/legion/extensions/conceptual_metaphor/helpers/metaphor_spec.rb
|
|
46
|
+
- spec/legion/extensions/conceptual_metaphor/runners/conceptual_metaphor_spec.rb
|
|
47
|
+
- spec/spec_helper.rb
|
|
48
|
+
homepage: https://github.com/LegionIO/lex-conceptual-metaphor
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata:
|
|
52
|
+
homepage_uri: https://github.com/LegionIO/lex-conceptual-metaphor
|
|
53
|
+
source_code_uri: https://github.com/LegionIO/lex-conceptual-metaphor
|
|
54
|
+
documentation_uri: https://github.com/LegionIO/lex-conceptual-metaphor
|
|
55
|
+
changelog_uri: https://github.com/LegionIO/lex-conceptual-metaphor
|
|
56
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-conceptual-metaphor/issues
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
58
|
+
rdoc_options: []
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: '3.4'
|
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
requirements: []
|
|
72
|
+
rubygems_version: 3.6.9
|
|
73
|
+
specification_version: 4
|
|
74
|
+
summary: LEX Conceptual Metaphor
|
|
75
|
+
test_files: []
|