lex-agentic-language 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -0
- data/lex-agentic-language.gemspec +3 -2
- data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb +2 -2
- data/lib/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor.rb +2 -2
- data/lib/legion/extensions/agentic/language/frame_semantics/helpers/client.rb +1 -0
- data/lib/legion/extensions/agentic/language/frame_semantics/runners/frame_semantics.rb +11 -11
- data/lib/legion/extensions/agentic/language/grammar/runners/cognitive_grammar.rb +2 -2
- data/lib/legion/extensions/agentic/language/language/runners/language.rb +2 -2
- data/lib/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning.rb +2 -2
- data/lib/legion/extensions/agentic/language/narrator/helpers/llm_enhancer.rb +12 -7
- data/lib/legion/extensions/agentic/language/narrator/runners/narrator.rb +2 -2
- data/lib/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference.rb +2 -2
- data/lib/legion/extensions/agentic/language/version.rb +1 -1
- data/lib/legion/extensions/agentic/language.rb +1 -1
- data/spec/legion/extensions/agentic/language/language/helpers/summarizer_spec.rb +1 -1
- data/spec/legion/extensions/agentic/language/language/runners/language_spec.rb +1 -1
- data/spec/legion/extensions/agentic/language/narrator/helpers/llm_enhancer_spec.rb +1 -0
- metadata +23 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c882ae8a5551a0d746effe0ed4bfe7c0ab1df089a494aa332db06c6ad654755
|
|
4
|
+
data.tar.gz: 832f52b494ed57900a3456d86ca9123177e58e308badd8b869cc8ca2e34fe2b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 897b30b1dce12487646fea641be95d4b7f67014e9485754818e1abce636ae20c5e5d720556bd995ea4afb36b0c15a08c3b2b4df6adec08d5e77315c0e7f80080
|
|
7
|
+
data.tar.gz: 3a571f5ec986c63f2b63c26cad9c1b271ec47e689fa6a95bbe24d525e714f7c0fbe82040b6505cd15eb6d0f2dde0dfbb005208045b105c98ac8ebc0e24e2a75c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.7] - 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- log backtrace in narrator llm_enhancer when narrate raises, aiding error diagnosis
|
|
7
|
+
- log warning in pipeline_available? rescue instead of silently swallowing errors
|
|
8
|
+
- improve pipeline_available? indentation and add blank lines around private_class_method declarations
|
|
9
|
+
|
|
10
|
+
## [0.1.6] - 2026-03-30
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- update to rubocop-legion 0.1.7, resolve all offenses
|
|
14
|
+
|
|
3
15
|
## [0.1.5] - 2026-03-26
|
|
4
16
|
|
|
5
17
|
### Changed
|
data/Gemfile
CHANGED
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
spec.add_dependency 'legion-transport', '>= 1.3.9'
|
|
34
34
|
|
|
35
35
|
spec.add_development_dependency 'rspec', '~> 3.13'
|
|
36
|
-
spec.add_development_dependency 'rubocop'
|
|
37
|
-
spec.add_development_dependency 'rubocop-
|
|
36
|
+
spec.add_development_dependency 'rubocop'
|
|
37
|
+
spec.add_development_dependency 'rubocop-legion'
|
|
38
|
+
spec.add_development_dependency 'rubocop-rspec'
|
|
38
39
|
end
|
data/lib/legion/extensions/agentic/language/conceptual_blending/runners/conceptual_blending.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module ConceptualBlending
|
|
8
8
|
module Runners
|
|
9
9
|
module ConceptualBlending
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def create_mental_space(name:, domain:, **)
|
|
14
14
|
log.debug "[conceptual_blending] create_space: name=#{name} domain=#{domain}"
|
data/lib/legion/extensions/agentic/language/conceptual_metaphor/runners/conceptual_metaphor.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module ConceptualMetaphor
|
|
8
8
|
module Runners
|
|
9
9
|
module ConceptualMetaphor
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def create_metaphor(source_domain:, target_domain:, metaphor_type:,
|
|
14
14
|
mappings:, strength: nil, conventionality: nil, **)
|
|
@@ -6,17 +6,17 @@ module Legion
|
|
|
6
6
|
module Language
|
|
7
7
|
module FrameSemantics
|
|
8
8
|
module Runners
|
|
9
|
-
module FrameSemantics
|
|
9
|
+
module FrameSemantics # rubocop:disable Legion/Extension/RunnerIncludeHelpers
|
|
10
10
|
def create_semantic_frame(name:, domain:, **)
|
|
11
11
|
frame = engine.create_frame(name: name, domain: domain)
|
|
12
|
-
|
|
12
|
+
log.debug("[frame_semantics] created frame name=#{name} domain=#{domain} id=#{frame.id[0..7]}")
|
|
13
13
|
{ frame_id: frame.id, name: frame.name, domain: frame.domain, created: true }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def define_frame_slot(frame_id:, name:, slot_type: :core, required: true, **)
|
|
17
17
|
frame = engine.define_slot(frame_id: frame_id, name: name, slot_type: slot_type, required: required)
|
|
18
18
|
if frame
|
|
19
|
-
|
|
19
|
+
log.debug("[frame_semantics] defined slot #{name} on frame #{frame_id[0..7]}")
|
|
20
20
|
{ defined: true, frame_id: frame_id, slot_name: name, slot_type: slot_type }
|
|
21
21
|
else
|
|
22
22
|
{ defined: false, reason: :frame_not_found }
|
|
@@ -26,7 +26,7 @@ module Legion
|
|
|
26
26
|
def fill_frame_slot(frame_id:, slot_name:, filler:, **)
|
|
27
27
|
result = engine.fill_slot(frame_id: frame_id, slot_name: slot_name, filler: filler)
|
|
28
28
|
if result
|
|
29
|
-
|
|
29
|
+
log.debug("[frame_semantics] filled slot #{slot_name} on frame #{frame_id[0..7]}")
|
|
30
30
|
{ filled: true, frame_id: frame_id, slot_name: slot_name, filler: filler }
|
|
31
31
|
else
|
|
32
32
|
{ filled: false, reason: :slot_or_frame_not_found }
|
|
@@ -36,7 +36,7 @@ module Legion
|
|
|
36
36
|
def instantiate_semantic_frame(frame_id:, context:, confidence: 0.7, **)
|
|
37
37
|
instance = engine.instantiate_frame(frame_id: frame_id, context: context, confidence: confidence)
|
|
38
38
|
if instance
|
|
39
|
-
|
|
39
|
+
log.debug("[frame_semantics] instantiated frame #{frame_id[0..7]} instance=#{instance.id[0..7]}")
|
|
40
40
|
{ instantiated: true, instance_id: instance.id, frame_id: frame_id,
|
|
41
41
|
filled_count: instance.filled_count }
|
|
42
42
|
else
|
|
@@ -48,7 +48,7 @@ module Legion
|
|
|
48
48
|
result = engine.add_frame_relation(frame_id: frame_id, relation: relation,
|
|
49
49
|
target_frame_id: target_frame_id)
|
|
50
50
|
if result
|
|
51
|
-
|
|
51
|
+
log.debug("[frame_semantics] added relation #{relation} #{frame_id[0..7]}->#{target_frame_id[0..7]}")
|
|
52
52
|
{ added: true, frame_id: frame_id, relation: relation, target_frame_id: target_frame_id }
|
|
53
53
|
else
|
|
54
54
|
{ added: false, reason: :invalid_frame_or_relation }
|
|
@@ -58,7 +58,7 @@ module Legion
|
|
|
58
58
|
def activate_semantic_frame(frame_id:, **)
|
|
59
59
|
result = engine.activate_frame(frame_id: frame_id)
|
|
60
60
|
if result
|
|
61
|
-
|
|
61
|
+
log.debug("[frame_semantics] activated frame #{frame_id[0..7]}")
|
|
62
62
|
{ activated: true, frame_id: frame_id }
|
|
63
63
|
else
|
|
64
64
|
{ activated: false, reason: :frame_not_found }
|
|
@@ -67,26 +67,26 @@ module Legion
|
|
|
67
67
|
|
|
68
68
|
def active_frames_report(**)
|
|
69
69
|
frames = engine.active_frames
|
|
70
|
-
|
|
70
|
+
log.debug("[frame_semantics] active_frames count=#{frames.size}")
|
|
71
71
|
{ frames: frames.map(&:to_h), count: frames.size }
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def related_frames_report(frame_id:, **)
|
|
75
75
|
frames = engine.related_frames(frame_id: frame_id)
|
|
76
|
-
|
|
76
|
+
log.debug("[frame_semantics] related_frames frame=#{frame_id[0..7]} count=#{frames.size}")
|
|
77
77
|
{ frames: frames.map(&:to_h), count: frames.size }
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def complete_frames_report(**)
|
|
81
81
|
frames = engine.complete_frames
|
|
82
|
-
|
|
82
|
+
log.debug("[frame_semantics] complete_frames count=#{frames.size}")
|
|
83
83
|
{ frames: frames.map(&:to_h), count: frames.size }
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def update_frame_semantics(**)
|
|
87
87
|
engine.decay_all
|
|
88
88
|
engine.prune_inactive
|
|
89
|
-
|
|
89
|
+
log.debug('[frame_semantics] decay_all + prune_inactive complete')
|
|
90
90
|
{ updated: true, stats: engine.to_h }
|
|
91
91
|
end
|
|
92
92
|
|
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module Grammar
|
|
8
8
|
module Runners
|
|
9
9
|
module CognitiveGrammar
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def create_grammar_construction(form:, meaning:, expression_type:, domain:, **)
|
|
14
14
|
construction = engine.create_construction(
|
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module Language
|
|
8
8
|
module Runners
|
|
9
9
|
module Language
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def summarize(domain:, depth: :standard, traces: [], **)
|
|
14
14
|
traces = filter_traces(traces, domain)
|
data/lib/legion/extensions/agentic/language/narrative_reasoning/runners/narrative_reasoning.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module NarrativeReasoning
|
|
8
8
|
module Runners
|
|
9
9
|
module NarrativeReasoning
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def create_narrative(title:, domain: nil, **)
|
|
14
14
|
narrative = narrative_engine.create_narrative(title: title, domain: domain)
|
|
@@ -18,7 +18,7 @@ module Legion
|
|
|
18
18
|
|
|
19
19
|
def available?
|
|
20
20
|
!!(defined?(Legion::LLM) && Legion::LLM.respond_to?(:started?) && Legion::LLM.started?)
|
|
21
|
-
rescue StandardError
|
|
21
|
+
rescue StandardError => _e
|
|
22
22
|
false
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -27,7 +27,8 @@ module Legion
|
|
|
27
27
|
response = llm_ask(prompt)
|
|
28
28
|
parse_narrate_response(response)
|
|
29
29
|
rescue StandardError => e
|
|
30
|
-
Legion::Logging.warn
|
|
30
|
+
Legion::Logging.warn("[narrator:llm] narrate failed: #{e.message}") # rubocop:disable Legion/HelperMigration/DirectLogging
|
|
31
|
+
Legion::Logging.warn(e.backtrace) # rubocop:disable Legion/HelperMigration/DirectLogging
|
|
31
32
|
nil
|
|
32
33
|
end
|
|
33
34
|
|
|
@@ -43,20 +44,23 @@ module Legion
|
|
|
43
44
|
content = response&.message&.dig(:content)
|
|
44
45
|
::Struct.new(:content).new(content) if content
|
|
45
46
|
else
|
|
46
|
-
chat = Legion::LLM.chat
|
|
47
|
+
chat = Legion::LLM.chat # rubocop:disable Legion/HelperMigration/DirectLlm
|
|
47
48
|
chat.with_instructions(SYSTEM_PROMPT)
|
|
48
49
|
chat.ask(prompt)
|
|
49
50
|
end
|
|
50
51
|
end
|
|
52
|
+
|
|
51
53
|
private_class_method :llm_ask
|
|
52
54
|
|
|
53
55
|
def pipeline_available?
|
|
54
56
|
!!(defined?(Legion::LLM::Pipeline::GaiaCaller) &&
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
rescue StandardError
|
|
57
|
+
Legion::LLM.respond_to?(:pipeline_enabled?) &&
|
|
58
|
+
Legion::LLM.pipeline_enabled?)
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
Legion::Logging.warn("pipeline_available? #{e.class}: #{e.message}") # rubocop:disable Legion/HelperMigration/DirectLogging
|
|
58
61
|
false
|
|
59
62
|
end
|
|
63
|
+
|
|
60
64
|
private_class_method :pipeline_available?
|
|
61
65
|
|
|
62
66
|
def build_narrate_prompt(sections_data)
|
|
@@ -80,7 +84,8 @@ module Legion
|
|
|
80
84
|
end
|
|
81
85
|
|
|
82
86
|
def curiosity_section(cur)
|
|
83
|
-
"CURIOSITY:\n- Intensity: #{cur[:intensity] || 0.0}\n
|
|
87
|
+
"CURIOSITY:\n- Intensity: #{cur[:intensity] || 0.0}\n" \
|
|
88
|
+
"- Active questions: #{cur[:wonder_count] || 0}\n- Top question: #{cur[:top_wonder] || 'none'}"
|
|
84
89
|
end
|
|
85
90
|
|
|
86
91
|
def prediction_section(pred)
|
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module Narrator
|
|
8
8
|
module Runners
|
|
9
9
|
module Narrator
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def narrate(tick_results: {}, cognitive_state: {}, **)
|
|
14
14
|
entry = Helpers::Synthesizer.narrate(tick_results: tick_results, cognitive_state: cognitive_state)
|
data/lib/legion/extensions/agentic/language/pragmatic_inference/runners/pragmatic_inference.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Legion
|
|
|
7
7
|
module PragmaticInference
|
|
8
8
|
module Runners
|
|
9
9
|
module PragmaticInference
|
|
10
|
-
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
|
|
11
|
-
Legion::Extensions::Helpers.const_defined?(:Lex)
|
|
10
|
+
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
11
|
+
Legion::Extensions::Helpers.const_defined?(:Lex, false)
|
|
12
12
|
|
|
13
13
|
def analyze_utterance(content:, speaker:, speech_act:, literal_meaning: nil,
|
|
14
14
|
domain: nil, maxim_scores: {}, **)
|
|
@@ -15,7 +15,7 @@ module Legion
|
|
|
15
15
|
module Extensions
|
|
16
16
|
module Agentic
|
|
17
17
|
module Language
|
|
18
|
-
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
|
|
18
|
+
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
|
|
19
19
|
|
|
20
20
|
def self.remote_invocable?
|
|
21
21
|
false
|
|
@@ -19,7 +19,7 @@ RSpec.describe Legion::Extensions::Agentic::Language::Language::Helpers::Summari
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def make_traces(count, overrides = {})
|
|
22
|
-
count
|
|
22
|
+
Array.new(count) do |i|
|
|
23
23
|
base_trace.merge(trace_id: "trace-#{i}").merge(overrides)
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -21,7 +21,7 @@ RSpec.describe Legion::Extensions::Agentic::Language::Language::Runners::Languag
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def make_traces(count, domain: :networking)
|
|
24
|
-
count
|
|
24
|
+
Array.new(count) do |i|
|
|
25
25
|
base_trace.merge(
|
|
26
26
|
trace_id: "trace-#{i}",
|
|
27
27
|
domain_tags: [domain],
|
|
@@ -84,6 +84,7 @@ RSpec.describe Legion::Extensions::Agentic::Language::Narrator::Helpers::LlmEnha
|
|
|
84
84
|
stub_const('Legion::LLM', llm_double)
|
|
85
85
|
|
|
86
86
|
expect(Legion::Logging).to receive(:warn).with(/narrator:llm.*narrate failed/)
|
|
87
|
+
expect(Legion::Logging).to receive(:warn).with(instance_of(Array))
|
|
87
88
|
result = described_class.narrate(sections_data: sections_data)
|
|
88
89
|
expect(result).to be_nil
|
|
89
90
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-agentic-language
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -125,30 +125,44 @@ dependencies:
|
|
|
125
125
|
name: rubocop
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
|
127
127
|
requirements:
|
|
128
|
-
- - "
|
|
128
|
+
- - ">="
|
|
129
129
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '
|
|
130
|
+
version: '0'
|
|
131
131
|
type: :development
|
|
132
132
|
prerelease: false
|
|
133
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
134
134
|
requirements:
|
|
135
|
-
- - "
|
|
135
|
+
- - ">="
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '
|
|
137
|
+
version: '0'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: rubocop-legion
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
138
152
|
- !ruby/object:Gem::Dependency
|
|
139
153
|
name: rubocop-rspec
|
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|
|
141
155
|
requirements:
|
|
142
|
-
- - "
|
|
156
|
+
- - ">="
|
|
143
157
|
- !ruby/object:Gem::Version
|
|
144
|
-
version: '
|
|
158
|
+
version: '0'
|
|
145
159
|
type: :development
|
|
146
160
|
prerelease: false
|
|
147
161
|
version_requirements: !ruby/object:Gem::Requirement
|
|
148
162
|
requirements:
|
|
149
|
-
- - "
|
|
163
|
+
- - ">="
|
|
150
164
|
- !ruby/object:Gem::Version
|
|
151
|
-
version: '
|
|
165
|
+
version: '0'
|
|
152
166
|
description: 'LEX agentic language domain: linguistic processing and communication'
|
|
153
167
|
email:
|
|
154
168
|
- matthewdiverson@gmail.com
|