lex-dual-process 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d2f51b67089c8d64b94d67427a3f892d7a65924e24e12203092c3fdcddda73cd
4
+ data.tar.gz: e742fa6726c11c5fad560a709daad2ec95fb3c0435d49450df99c69f9828e8f8
5
+ SHA512:
6
+ metadata.gz: 54df463d20f44b00ed14c43901db6cd50012049b4ae934fadfc299e231156264c21ca10916c8b184ec064ab7a3f2ef96e6a6f3ab4db82f1640b2a75db3e7c9e7
7
+ data.tar.gz: 960ade33ca509551591ea7dfe577b4d4afe1a35374ae18fbccaf1a98952f6e75d16d49b4c729532c742d1bbdd40967b1b1504eef9e04ede9fedb41a49b96374d
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rspec_junit_formatter'
10
+ gem 'rubocop', '~> 1.75', require: false
11
+ gem 'rubocop-rspec', require: false
12
+ gem 'simplecov'
13
+ end
14
+
15
+ gem 'legion-gaia', path: '../../legion-gaia'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Iverson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # lex-dual-process
2
+
3
+ Dual-process cognition modeling for the LegionIO brain-modeled cognitive architecture.
4
+
5
+ ## What It Does
6
+
7
+ Implements Kahneman's System 1 / System 2 theory. Routes decisions to fast heuristic reasoning (System 1) or slow deliberative reasoning (System 2) based on query complexity, confidence threshold, and available effort budget. Maintains a library of learned heuristics that improve through reinforcement. Models cognitive fatigue via a depletable effort budget that recovers over time.
8
+
9
+ ## Usage
10
+
11
+ ```ruby
12
+ client = Legion::Extensions::DualProcess::Client.new
13
+
14
+ # Register a heuristic pattern
15
+ client.register_heuristic(
16
+ pattern: 'familiar HTTP request',
17
+ domain: :networking,
18
+ confidence: 0.8
19
+ )
20
+
21
+ # Route a decision — automatically selects System 1 or System 2
22
+ client.route_decision(query: 'handle incoming request', domain: :networking, complexity: 0.3)
23
+ # => { success: true, system: :system_one, confidence: 0.8, complexity: 0.3,
24
+ # heuristic_used: "...", effort_remaining: 1.0 }
25
+
26
+ # High-complexity query forces System 2
27
+ client.route_decision(query: 'design new architecture', domain: :planning, complexity: 0.9)
28
+ # => { success: true, system: :system_two, confidence: 0.7, effort_cost: 0.1, ... }
29
+
30
+ # Record outcome to reinforce the heuristic
31
+ client.record_decision_outcome(decision_id: '...', success: true)
32
+
33
+ # Check effort budget
34
+ client.effort_assessment
35
+ # => { effort_budget: 0.8, effort_label: :fluent, system_bias: :system_one }
36
+
37
+ # Best heuristics in a domain
38
+ client.best_heuristics(domain: :networking, limit: 5)
39
+
40
+ # Periodic tick: recover effort budget
41
+ client.update_dual_process
42
+ # => { success: true, effort_recovered: 0.05, effort_budget: 0.85 }
43
+ ```
44
+
45
+ ## Routing Labels
46
+
47
+ | Effort Budget | Label |
48
+ |---|---|
49
+ | 0.8 – 1.0 | `:automatic` |
50
+ | 0.6 – 0.8 | `:fluent` |
51
+ | 0.4 – 0.6 | `:effortful` |
52
+ | 0.2 – 0.4 | `:strained` |
53
+ | 0.0 – 0.2 | `:depleted` |
54
+
55
+ When effort is depleted, all decisions route to System 1 regardless of complexity.
56
+
57
+ ## Development
58
+
59
+ ```bash
60
+ bundle install
61
+ bundle exec rspec
62
+ bundle exec rubocop
63
+ ```
64
+
65
+ ## License
66
+
67
+ MIT
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/dual_process/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-dual-process'
7
+ spec.version = Legion::Extensions::DualProcess::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX Dual Process'
12
+ spec.description = "Kahneman's Dual Process Theory for brain-modeled agentic AI: System 1 (fast/intuitive) vs System 2 (slow/deliberate)"
13
+ spec.homepage = 'https://github.com/LegionIO/lex-dual-process'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-dual-process'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-dual-process'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-dual-process'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-dual-process/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ Dir.glob('{lib,spec}/**/*') + %w[lex-dual-process.gemspec Gemfile LICENSE README.md]
26
+ end
27
+ spec.require_paths = ['lib']
28
+ spec.add_development_dependency 'legion-gaia'
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/dual_process/helpers/constants'
4
+ require 'legion/extensions/dual_process/helpers/heuristic'
5
+ require 'legion/extensions/dual_process/helpers/decision'
6
+ require 'legion/extensions/dual_process/helpers/dual_process_engine'
7
+ require 'legion/extensions/dual_process/runners/dual_process'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module DualProcess
12
+ class Client
13
+ include Runners::DualProcess
14
+
15
+ def initialize(engine: nil)
16
+ @engine = engine || Helpers::DualProcessEngine.new
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module DualProcess
6
+ module Helpers
7
+ module Constants
8
+ MAX_DECISIONS = 200
9
+ MAX_HEURISTICS = 100
10
+ MAX_HISTORY = 300
11
+
12
+ DEFAULT_CONFIDENCE = 0.5
13
+ CONFIDENCE_FLOOR = 0.05
14
+ CONFIDENCE_CEILING = 0.95
15
+ SYSTEM_ONE_THRESHOLD = 0.6
16
+ COMPLEXITY_THRESHOLD = 0.5
17
+
18
+ EFFORT_COST = 0.1
19
+ EFFORT_RECOVERY_RATE = 0.05
20
+ MAX_EFFORT_BUDGET = 1.0
21
+ FATIGUE_PENALTY = 0.15
22
+ HEURISTIC_BOOST = 0.2
23
+ DECAY_RATE = 0.01
24
+
25
+ SYSTEMS = %i[system_one system_two].freeze
26
+
27
+ ROUTING_LABELS = {
28
+ (0.8..) => :automatic,
29
+ (0.6...0.8) => :fluent,
30
+ (0.4...0.6) => :effortful,
31
+ (0.2...0.4) => :strained,
32
+ (..0.2) => :depleted
33
+ }.freeze
34
+
35
+ DECISION_OUTCOMES = %i[correct incorrect uncertain].freeze
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module DualProcess
8
+ module Helpers
9
+ class Decision
10
+ attr_accessor :outcome
11
+ attr_reader :id, :query, :domain, :system_used, :confidence, :complexity, :heuristic_id, :effort_cost, :processing_time_ms, :created_at
12
+
13
+ def initialize(query:, domain:, system_used:, confidence:, complexity:, **opts)
14
+ @id = SecureRandom.uuid
15
+ @query = query
16
+ @domain = domain
17
+ @system_used = system_used
18
+ @confidence = confidence.clamp(Constants::CONFIDENCE_FLOOR, Constants::CONFIDENCE_CEILING)
19
+ @complexity = complexity.clamp(0.0, 1.0)
20
+ @heuristic_id = opts.fetch(:heuristic_id, nil)
21
+ @outcome = nil
22
+ @effort_cost = opts.fetch(:effort_cost, 0.0)
23
+ @processing_time_ms = opts.fetch(:processing_time_ms, 0)
24
+ @created_at = Time.now.utc
25
+ end
26
+
27
+ def to_h
28
+ {
29
+ id: @id,
30
+ query: @query,
31
+ domain: @domain,
32
+ system_used: @system_used,
33
+ confidence: @confidence,
34
+ complexity: @complexity,
35
+ heuristic_id: @heuristic_id,
36
+ outcome: @outcome,
37
+ effort_cost: @effort_cost,
38
+ processing_time_ms: @processing_time_ms,
39
+ created_at: @created_at
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,196 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module DualProcess
6
+ module Helpers
7
+ class DualProcessEngine
8
+ include Constants
9
+
10
+ def initialize
11
+ @effort_budget = MAX_EFFORT_BUDGET
12
+ @heuristics = {}
13
+ @decisions = []
14
+ end
15
+
16
+ def register_heuristic(pattern:, domain:, response:, confidence: DEFAULT_CONFIDENCE)
17
+ heuristic = Heuristic.new(pattern: pattern, domain: domain, response: response, confidence: confidence)
18
+ if @heuristics.size >= MAX_HEURISTICS
19
+ oldest_key = @heuristics.min_by { |_, h| h.last_used_at || h.created_at }.first
20
+ @heuristics.delete(oldest_key)
21
+ end
22
+ @heuristics[heuristic.id] = heuristic
23
+ heuristic
24
+ end
25
+
26
+ def route_decision(query:, domain:, complexity:)
27
+ matching = find_matching_heuristic(query, domain)
28
+ use_system_one = complexity < COMPLEXITY_THRESHOLD &&
29
+ matching &&
30
+ matching.confidence >= SYSTEM_ONE_THRESHOLD
31
+
32
+ if use_system_one
33
+ { system: :system_one, reason: :heuristic_match, heuristic_id: matching.id }
34
+ elsif @effort_budget >= EFFORT_COST
35
+ { system: :system_two, reason: complexity_reason(complexity, matching) }
36
+ else
37
+ { system: :system_one, reason: :effort_depleted, fatigue: true }
38
+ end
39
+ end
40
+
41
+ def execute_system_one(query:, domain:)
42
+ matching = find_matching_heuristic(query, domain)
43
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
44
+
45
+ if matching
46
+ matching.use!(success: true)
47
+ confidence = matching.confidence
48
+ response = matching.response
49
+ else
50
+ confidence = (DEFAULT_CONFIDENCE - FATIGUE_PENALTY).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
51
+ response = :no_heuristic
52
+ end
53
+
54
+ elapsed = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
55
+
56
+ decision = Decision.new(
57
+ query: query,
58
+ domain: domain,
59
+ system_used: :system_one,
60
+ confidence: confidence,
61
+ complexity: 0.0,
62
+ heuristic_id: matching&.id,
63
+ effort_cost: 0.0,
64
+ processing_time_ms: elapsed
65
+ )
66
+ store_decision(decision)
67
+ { decision_id: decision.id, system: :system_one, response: response, confidence: confidence }
68
+ end
69
+
70
+ def execute_system_two(query:, domain:, deliberation: {})
71
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
72
+ @effort_budget = (@effort_budget - EFFORT_COST).clamp(0.0, MAX_EFFORT_BUDGET)
73
+
74
+ confidence = deliberation.fetch(:confidence, DEFAULT_CONFIDENCE + HEURISTIC_BOOST)
75
+ .clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING)
76
+ response = deliberation.fetch(:response, :deliberated)
77
+ elapsed = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
78
+
79
+ decision = Decision.new(
80
+ query: query,
81
+ domain: domain,
82
+ system_used: :system_two,
83
+ confidence: confidence,
84
+ complexity: deliberation.fetch(:complexity, COMPLEXITY_THRESHOLD),
85
+ effort_cost: EFFORT_COST,
86
+ processing_time_ms: elapsed
87
+ )
88
+ store_decision(decision)
89
+ { decision_id: decision.id, system: :system_two, response: response, confidence: confidence }
90
+ end
91
+
92
+ def record_outcome(decision_id:, outcome:)
93
+ decision = @decisions.find { |d| d.id == decision_id }
94
+ return { success: false, reason: :not_found } unless decision
95
+
96
+ decision.outcome = outcome
97
+
98
+ if decision.heuristic_id
99
+ heuristic = @heuristics[decision.heuristic_id]
100
+ heuristic&.use!(success: outcome == :correct)
101
+ end
102
+
103
+ { success: true, decision_id: decision_id, outcome: outcome }
104
+ end
105
+
106
+ def effort_level
107
+ @effort_budget / MAX_EFFORT_BUDGET
108
+ end
109
+
110
+ def routing_label
111
+ ROUTING_LABELS.find { |range, _| range.cover?(effort_level) }&.last || :unknown
112
+ end
113
+
114
+ def recover_effort
115
+ @effort_budget = (@effort_budget + EFFORT_RECOVERY_RATE).clamp(0.0, MAX_EFFORT_BUDGET)
116
+ end
117
+
118
+ def decay_heuristics
119
+ @heuristics.each_value do |h|
120
+ next unless h.use_count.positive?
121
+
122
+ delta = -DECAY_RATE
123
+ h.instance_variable_set(:@confidence, (h.confidence + delta).clamp(CONFIDENCE_FLOOR, CONFIDENCE_CEILING))
124
+ end
125
+ end
126
+
127
+ def system_stats
128
+ s1 = @decisions.count { |d| d.system_used == :system_one }
129
+ s2 = @decisions.count { |d| d.system_used == :system_two }
130
+ total = @decisions.size
131
+
132
+ {
133
+ system_one: s1,
134
+ system_two: s2,
135
+ total: total,
136
+ s1_ratio: total.zero? ? 0.0 : (s1.to_f / total).round(3),
137
+ s2_ratio: total.zero? ? 0.0 : (s2.to_f / total).round(3)
138
+ }
139
+ end
140
+
141
+ def best_heuristics(limit: 5)
142
+ @heuristics.values
143
+ .select(&:reliable?)
144
+ .sort_by { |h| [-h.success_rate, -h.use_count] }
145
+ .first(limit)
146
+ .map(&:to_h)
147
+ end
148
+
149
+ def to_h
150
+ {
151
+ effort_budget: @effort_budget,
152
+ effort_level: effort_level,
153
+ routing_label: routing_label,
154
+ heuristic_count: @heuristics.size,
155
+ decision_count: @decisions.size,
156
+ system_stats: system_stats
157
+ }
158
+ end
159
+
160
+ private
161
+
162
+ def find_matching_heuristic(query, domain)
163
+ @heuristics.values
164
+ .select { |h| h.domain == domain || domain.nil? }
165
+ .select { |h| query_matches?(h.pattern, query) }
166
+ .max_by(&:confidence)
167
+ end
168
+
169
+ def query_matches?(pattern, query)
170
+ case pattern
171
+ when Symbol then query.is_a?(Hash) && query.key?(pattern)
172
+ when String then query.to_s.include?(pattern)
173
+ when Regexp then pattern.match?(query.to_s)
174
+ else false
175
+ end
176
+ end
177
+
178
+ def complexity_reason(complexity, matching)
179
+ if complexity >= COMPLEXITY_THRESHOLD
180
+ :high_complexity
181
+ elsif matching.nil?
182
+ :no_heuristic
183
+ else
184
+ :low_confidence
185
+ end
186
+ end
187
+
188
+ def store_decision(decision)
189
+ @decisions.shift if @decisions.size >= MAX_DECISIONS
190
+ @decisions << decision
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module DualProcess
8
+ module Helpers
9
+ class Heuristic
10
+ attr_reader :id, :pattern, :domain, :response, :confidence,
11
+ :use_count, :success_count, :created_at, :last_used_at
12
+
13
+ def initialize(pattern:, domain:, response:, confidence: Constants::DEFAULT_CONFIDENCE)
14
+ @id = SecureRandom.uuid
15
+ @pattern = pattern
16
+ @domain = domain
17
+ @response = response
18
+ @confidence = confidence.clamp(Constants::CONFIDENCE_FLOOR, Constants::CONFIDENCE_CEILING)
19
+ @use_count = 0
20
+ @success_count = 0
21
+ @created_at = Time.now.utc
22
+ @last_used_at = nil
23
+ end
24
+
25
+ def use!(success: true)
26
+ @use_count += 1
27
+ @last_used_at = Time.now.utc
28
+ @success_count += 1 if success
29
+
30
+ delta = success ? Constants::HEURISTIC_BOOST : -Constants::HEURISTIC_BOOST
31
+ @confidence = (@confidence + delta).clamp(Constants::CONFIDENCE_FLOOR, Constants::CONFIDENCE_CEILING)
32
+ end
33
+
34
+ def success_rate
35
+ return 0.0 if @use_count.zero?
36
+
37
+ @success_count.to_f / @use_count
38
+ end
39
+
40
+ def reliable?
41
+ success_rate >= 0.7 && @use_count >= 3
42
+ end
43
+
44
+ def to_h
45
+ {
46
+ id: @id,
47
+ pattern: @pattern,
48
+ domain: @domain,
49
+ response: @response,
50
+ confidence: @confidence,
51
+ use_count: @use_count,
52
+ success_count: @success_count,
53
+ success_rate: success_rate,
54
+ reliable: reliable?,
55
+ created_at: @created_at,
56
+ last_used_at: @last_used_at
57
+ }
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module DualProcess
6
+ module Runners
7
+ module DualProcess
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex)
10
+
11
+ def register_heuristic(pattern:, domain:, response:, confidence: nil, **)
12
+ Legion::Logging.debug "[dual_process] register_heuristic pattern=#{pattern} domain=#{domain}"
13
+ opts = { pattern: pattern, domain: domain, response: response }
14
+ opts[:confidence] = confidence unless confidence.nil?
15
+ heuristic = engine.register_heuristic(**opts)
16
+ { success: true, heuristic: heuristic.to_h }
17
+ end
18
+
19
+ def route_decision(query:, domain:, complexity:, **)
20
+ Legion::Logging.debug "[dual_process] route_decision domain=#{domain} complexity=#{complexity}"
21
+ route = engine.route_decision(query: query, domain: domain, complexity: complexity)
22
+ { success: true, **route }
23
+ end
24
+
25
+ def execute_system_one(query:, domain:, **)
26
+ Legion::Logging.debug "[dual_process] execute_system_one domain=#{domain}"
27
+ result = engine.execute_system_one(query: query, domain: domain)
28
+ { success: true, **result }
29
+ end
30
+
31
+ def execute_system_two(query:, domain:, deliberation: {}, **)
32
+ Legion::Logging.debug "[dual_process] execute_system_two domain=#{domain}"
33
+ result = engine.execute_system_two(query: query, domain: domain, deliberation: deliberation)
34
+ { success: true, **result }
35
+ end
36
+
37
+ def record_decision_outcome(decision_id:, outcome:, **)
38
+ Legion::Logging.debug "[dual_process] record_outcome decision_id=#{decision_id} outcome=#{outcome}"
39
+ engine.record_outcome(decision_id: decision_id, outcome: outcome)
40
+ end
41
+
42
+ def effort_assessment(**)
43
+ Legion::Logging.debug '[dual_process] effort_assessment'
44
+ {
45
+ success: true,
46
+ effort_level: engine.effort_level,
47
+ effort_budget: engine.instance_variable_get(:@effort_budget),
48
+ routing_label: engine.routing_label
49
+ }
50
+ end
51
+
52
+ def best_heuristics(limit: 5, **)
53
+ Legion::Logging.debug "[dual_process] best_heuristics limit=#{limit}"
54
+ { success: true, heuristics: engine.best_heuristics(limit: limit) }
55
+ end
56
+
57
+ def system_usage_stats(**)
58
+ Legion::Logging.debug '[dual_process] system_usage_stats'
59
+ { success: true, stats: engine.system_stats }
60
+ end
61
+
62
+ def update_dual_process(**)
63
+ Legion::Logging.debug '[dual_process] update_dual_process'
64
+ engine.recover_effort
65
+ engine.decay_heuristics
66
+ { success: true, effort_level: engine.effort_level, routing_label: engine.routing_label }
67
+ end
68
+
69
+ def dual_process_stats(**)
70
+ Legion::Logging.debug '[dual_process] dual_process_stats'
71
+ { success: true, **engine.to_h }
72
+ end
73
+
74
+ private
75
+
76
+ def engine
77
+ @engine ||= Helpers::DualProcessEngine.new
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module DualProcess
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/dual_process/version'
4
+ require 'legion/extensions/dual_process/helpers/constants'
5
+ require 'legion/extensions/dual_process/helpers/heuristic'
6
+ require 'legion/extensions/dual_process/helpers/decision'
7
+ require 'legion/extensions/dual_process/helpers/dual_process_engine'
8
+ require 'legion/extensions/dual_process/runners/dual_process'
9
+
10
+ module Legion
11
+ module Extensions
12
+ module DualProcess
13
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
14
+ end
15
+ end
16
+ end