aia 1.1.0 → 2.0.0.0.pre.alpha
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/.envrc +5 -1
- data/.loki +231 -0
- data/.quality/flay_baseline.txt +1 -0
- data/.quality/flog_baseline.txt +29 -0
- data/.quality/reek_baseline.txt +80 -0
- data/.rubocop.yml +116 -0
- data/.version +1 -1
- data/CHANGELOG.md +266 -42
- data/IMPLEMENTATION_PLAN.md +506 -0
- data/README.md +266 -238
- data/Rakefile +118 -5
- data/architecture_review.md +314 -0
- data/bin/aia +16 -0
- data/docs/AGENTS.md +40 -0
- data/docs/advanced-prompting.md +67 -3
- data/docs/cli-reference.md +312 -56
- data/docs/configuration.md +130 -19
- data/docs/contributing.md +56 -2
- data/docs/directives-reference.md +593 -78
- data/docs/faq.md +85 -3
- data/docs/guides/available-models.md +1 -1
- data/docs/guides/basic-usage.md +6 -6
- data/docs/guides/chat.md +40 -16
- data/docs/guides/crew.md +239 -0
- data/docs/guides/executable-prompts.md +1 -1
- data/docs/guides/index.md +1 -0
- data/docs/guides/models.md +15 -0
- data/docs/index.md +29 -2
- data/docs/installation.md +44 -17
- data/docs/mcp-integration.md +40 -0
- data/docs/prompt_management.md +85 -86
- data/docs/security.md +47 -0
- data/docs/special_projects_guide.md +386 -0
- data/docs/tools-and-mcp-examples.md +23 -0
- data/docs/workflows-and-pipelines.md +84 -7
- data/examples/.gitignore +1 -0
- data/examples/00_setup_aia.sh +27 -44
- data/examples/11_multi_model.sh +4 -14
- data/examples/12_token_usage.sh +3 -12
- data/examples/18_tools.sh +10 -2
- data/examples/22_chat_mode.sh +0 -10
- data/examples/23_verify.sh +139 -0
- data/examples/24_decompose.sh +139 -0
- data/examples/25_spawn.sh +139 -0
- data/examples/26_debate.sh +97 -0
- data/examples/27_mention_routing.sh +157 -0
- data/examples/28_model_switching.sh +106 -0
- data/examples/29_agent_harness.sh +177 -0
- data/examples/README.md +65 -0
- data/examples/advanced_multi_robot_capabilities_without_examples.md +106 -0
- data/examples/aia_config.yml +1 -1
- data/examples/aia_config_orchestrator.yml +45 -0
- data/examples/common.sh +19 -0
- data/examples/context/tech_stack.md +2 -2
- data/examples/prompts_dir/project_summary +2 -2
- data/examples/prompts_dir/roles/orchestrator.md +21 -0
- data/examples/requirements/sinatra_taskflow_app.md +139 -0
- data/examples/rules/01_classify_ruby.rb +16 -0
- data/examples/rules/02_prefer_claude_for_code.rb +19 -0
- data/examples/rules/03_gate_prompt_length.rb +19 -0
- data/examples/rules/04_tool_selection.rb +41 -0
- data/examples/rules/README.md +30 -0
- data/examples/run_all.sh +48 -15
- data/examples/tools/word_count_tool.rb +1 -1
- data/lib/AGENTS.md +57 -0
- data/lib/aia/chat_loop.rb +306 -159
- data/lib/aia/config/cli_parser.rb +174 -111
- data/lib/aia/config/defaults.yml +62 -33
- data/lib/aia/config/mcp_parser.rb +39 -46
- data/lib/aia/config/model_spec.rb +34 -2
- data/lib/aia/config/validator.rb +121 -138
- data/lib/aia/config.rb +110 -145
- data/lib/aia/content_extractor.rb +153 -0
- data/lib/aia/cost_calculator.rb +38 -0
- data/lib/aia/crew.rb +164 -0
- data/lib/aia/debate_handler.rb +166 -0
- data/lib/aia/delegate_handler.rb +112 -0
- data/lib/aia/directive.rb +33 -18
- data/lib/aia/directive_processor.rb +16 -7
- data/lib/aia/directives/configuration_directives.rb +160 -20
- data/lib/aia/directives/context_directives.rb +38 -26
- data/lib/aia/directives/execution_directives.rb +136 -4
- data/lib/aia/directives/model_directives.rb +76 -34
- data/lib/aia/directives/trakflow_directives.rb +44 -0
- data/lib/aia/directives/utility_directives.rb +203 -6
- data/lib/aia/directives/web_and_file_directives.rb +96 -60
- data/lib/aia/errors.rb +15 -0
- data/lib/aia/fact_asserter.rb +27 -0
- data/lib/aia/fzf.rb +9 -31
- data/lib/aia/handler_context.rb +17 -0
- data/lib/aia/handler_protocol.rb +19 -0
- data/lib/aia/history_transfer.rb +55 -0
- data/lib/aia/input_collector.rb +3 -3
- data/lib/aia/layered_orchestrator.rb +448 -0
- data/lib/aia/logger.rb +24 -4
- data/lib/aia/mcp_config_normalizer.rb +35 -0
- data/lib/aia/mcp_connection_manager.rb +305 -0
- data/lib/aia/mcp_discovery.rb +44 -0
- data/lib/aia/mcp_grouper.rb +33 -0
- data/lib/aia/mcp_utility.rb +57 -0
- data/lib/aia/mention_router.rb +260 -0
- data/lib/aia/model_alias_registry.rb +97 -0
- data/lib/aia/model_switch_handler.rb +100 -0
- data/lib/aia/network_builder.rb +155 -0
- data/lib/aia/network_memory_manager.rb +55 -0
- data/lib/aia/patches/ruby_llm_streaming_error.rb +43 -0
- data/lib/aia/patches/ruby_llm_tool_error.rb +96 -0
- data/lib/aia/pipeline_orchestrator.rb +262 -0
- data/lib/aia/plugin_loader.rb +170 -0
- data/lib/aia/plugin_monitor.rb +208 -0
- data/lib/aia/prompt_decomposer.rb +157 -0
- data/lib/aia/prompt_handler.rb +19 -39
- data/lib/aia/robot_builder.rb +51 -0
- data/lib/aia/robot_factory.rb +334 -0
- data/lib/aia/robot_namer.rb +116 -0
- data/lib/aia/session.rb +83 -17
- data/lib/aia/session_tracker.rb +209 -0
- data/lib/aia/similarity_scorer.rb +39 -0
- data/lib/aia/skill_utils.rb +105 -1
- data/lib/aia/spawn_handler.rb +129 -0
- data/lib/aia/spawn_spec_parser.rb +65 -0
- data/lib/aia/special_mode_handler.rb +302 -0
- data/lib/aia/startup_coordinator.rb +150 -0
- data/lib/aia/streaming_runner.rb +169 -0
- data/lib/aia/system_prompt_assembler.rb +88 -0
- data/lib/aia/task_coordinator.rb +202 -0
- data/lib/aia/task_decomposer.rb +57 -0
- data/lib/aia/task_executor.rb +51 -0
- data/lib/aia/tfidf_math.rb +27 -0
- data/lib/aia/tool_filter/tfidf.rb +116 -0
- data/lib/aia/tool_filter/wordnet_expander.rb +127 -0
- data/lib/aia/tool_filter.rb +82 -0
- data/lib/aia/tool_filter_registry.rb +30 -0
- data/lib/aia/tool_filter_strategy.rb +143 -0
- data/lib/aia/tool_loader.rb +210 -0
- data/lib/aia/tool_utility.rb +30 -0
- data/lib/aia/tools/delegate_to_foreman_tool.rb +70 -0
- data/lib/aia/tools/recruit_robot_tool.rb +60 -0
- data/lib/aia/tools/reskill_robot_tool.rb +44 -0
- data/lib/aia/tools/task_board_tool.rb +114 -0
- data/lib/aia/trakflow_bridge.rb +173 -0
- data/lib/aia/turn_state.rb +94 -0
- data/lib/aia/ui_presenter.rb +166 -198
- data/lib/aia/utility.rb +134 -87
- data/lib/aia/{history_manager.rb → variable_input_collector.rb} +8 -9
- data/lib/aia/verification_network.rb +58 -0
- data/lib/aia.rb +108 -63
- data/mkdocs.yml +1 -0
- metadata +179 -56
- data/justfile +0 -215
- data/lib/aia/adapter/chat_execution.rb +0 -242
- data/lib/aia/adapter/error_handler.rb +0 -68
- data/lib/aia/adapter/gem_activator.rb +0 -57
- data/lib/aia/adapter/mcp_connector.rb +0 -274
- data/lib/aia/adapter/modality_handlers.rb +0 -167
- data/lib/aia/adapter/model_registry.rb +0 -81
- data/lib/aia/adapter/multi_model_chat.rb +0 -218
- data/lib/aia/adapter/provider_configurator.rb +0 -59
- data/lib/aia/adapter/tool_filter.rb +0 -85
- data/lib/aia/adapter/tool_loader.rb +0 -90
- data/lib/aia/chat_processor_service.rb +0 -164
- data/lib/aia/prompt_pipeline.rb +0 -183
- data/lib/aia/ruby_llm_adapter.rb +0 -95
- data/lib/extensions/openstruct_merge.rb +0 -48
- data/lib/extensions/ruby_llm/.irbrc +0 -56
- data/lib/extensions/ruby_llm/modalities.rb +0 -36
- data/lib/extensions/ruby_llm/provider_fix.rb +0 -79
- data/lib/refinements/string.rb +0 -16
- data/main.just +0 -76
data/lib/aia/utility.rb
CHANGED
|
@@ -1,118 +1,165 @@
|
|
|
1
1
|
# lib/aia/utility.rb
|
|
2
2
|
|
|
3
|
-
require 'word_wrapper'
|
|
3
|
+
require 'word_wrapper'
|
|
4
|
+
require 'simple_flow'
|
|
5
|
+
require 'trak_flow'
|
|
6
|
+
require 'tty-table'
|
|
7
|
+
begin
|
|
8
|
+
require 'ruby_llm/mcp'
|
|
9
|
+
rescue LoadError, StandardError
|
|
10
|
+
# ruby_llm-mcp may not be installed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require_relative 'mcp_utility'
|
|
14
|
+
require_relative 'tool_utility'
|
|
4
15
|
|
|
5
16
|
module AIA
|
|
6
|
-
|
|
17
|
+
module Utility
|
|
7
18
|
class << self
|
|
8
|
-
|
|
9
|
-
|
|
19
|
+
include MCPUtility
|
|
20
|
+
include ToolUtility
|
|
21
|
+
|
|
22
|
+
def models_last_refresh
|
|
23
|
+
aia_dir = AIA.config&.paths&.aia_dir
|
|
24
|
+
return nil if aia_dir.nil?
|
|
25
|
+
models_file = File.join(File.expand_path(aia_dir), 'models.json')
|
|
26
|
+
File.mtime(models_file).strftime('%Y-%m-%d %H:%M')
|
|
27
|
+
rescue Errno::ENOENT
|
|
28
|
+
nil
|
|
10
29
|
end
|
|
11
30
|
|
|
12
|
-
def
|
|
13
|
-
|
|
31
|
+
def build_crew_line
|
|
32
|
+
names = robot_names
|
|
33
|
+
return '' if names.empty?
|
|
34
|
+
"Today's crew: #{format_crew_mentions(names)}"
|
|
14
35
|
end
|
|
15
36
|
|
|
16
|
-
def
|
|
17
|
-
|
|
37
|
+
def robot
|
|
38
|
+
art = [
|
|
39
|
+
' , ,',
|
|
40
|
+
' (\____/)',
|
|
41
|
+
' (_oo_)',
|
|
42
|
+
' (O)',
|
|
43
|
+
' __|||__ \)',
|
|
44
|
+
' [/ Tobor \] /',
|
|
45
|
+
' / \_______/ \/',
|
|
46
|
+
' / /___\\',
|
|
47
|
+
' (\ /_____\\',
|
|
48
|
+
' ::: :::',
|
|
49
|
+
' ::: :::'
|
|
50
|
+
].join("\n")
|
|
51
|
+
|
|
52
|
+
art_width = art.lines.map(&:length).max
|
|
53
|
+
info_width = [TTY::Screen.width - art_width - 4, 30].max
|
|
54
|
+
label_width = 7 # "Models:" is the widest label
|
|
55
|
+
value_width = [info_width - label_width - 1, 20].max
|
|
56
|
+
|
|
57
|
+
header = "AIA v#{AIA::VERSION} is Online"
|
|
58
|
+
|
|
59
|
+
inner = TTY::Table.new(banner_detail_rows)
|
|
60
|
+
details = inner.render(:basic, multiline: true,
|
|
61
|
+
column_widths: [label_width, value_width],
|
|
62
|
+
alignments: %i[right left])
|
|
63
|
+
|
|
64
|
+
outer = TTY::Table.new([[art, "#{header}\n\n#{details}"]])
|
|
65
|
+
puts "\n#{outer.render(:basic, multiline: true, column_widths: [art_width, info_width], padding: [0, 1, 0, 0])}"
|
|
18
66
|
end
|
|
19
67
|
|
|
20
|
-
|
|
21
|
-
def mcp_server_names
|
|
22
|
-
# Use connected_mcp_servers if available (populated during MCP setup)
|
|
23
|
-
connected = AIA.config&.connected_mcp_servers
|
|
24
|
-
return connected if connected && !connected.empty?
|
|
68
|
+
private
|
|
25
69
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
70
|
+
def robot_names
|
|
71
|
+
client = AIA.client
|
|
72
|
+
return [] unless client
|
|
73
|
+
client.crew.map(&:name)
|
|
29
74
|
end
|
|
30
75
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
connected = AIA.config&.connected_mcp_servers
|
|
34
|
-
connected && !connected.empty?
|
|
76
|
+
def format_crew_mentions(names)
|
|
77
|
+
names.map { |n| "@#{n.downcase}" }.join(', ')
|
|
35
78
|
end
|
|
36
79
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
80
|
+
def banner_detail_rows
|
|
81
|
+
[
|
|
82
|
+
['Models:', banner_models],
|
|
83
|
+
['DB:', banner_db],
|
|
84
|
+
['Libs:', banner_libs],
|
|
85
|
+
['Tools:', banner_tools],
|
|
86
|
+
['MCP:', banner_mcp],
|
|
87
|
+
['Crew:', banner_crew]
|
|
88
|
+
]
|
|
40
89
|
end
|
|
41
90
|
|
|
42
|
-
def
|
|
43
|
-
AIA.
|
|
91
|
+
def banner_models
|
|
92
|
+
models = AIA.config&.models
|
|
93
|
+
return 'unknown-model' if models.nil? || models.empty?
|
|
94
|
+
models.map do |spec|
|
|
95
|
+
case spec
|
|
96
|
+
when AIA::ModelSpec then spec.name
|
|
97
|
+
when Hash then spec[:name] || spec['name'] || spec.to_s
|
|
98
|
+
else spec.to_s
|
|
99
|
+
end
|
|
100
|
+
end.join(', ')
|
|
44
101
|
end
|
|
45
102
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
File.mtime(models_file).strftime('%Y-%m-%d %H:%M')
|
|
103
|
+
def banner_libs
|
|
104
|
+
parts = ["ruby_llm v#{RubyLLM::VERSION}"]
|
|
105
|
+
parts << "ruby_llm-mcp v#{RubyLLM::MCP::VERSION}" if defined?(RubyLLM::MCP::VERSION)
|
|
106
|
+
parts << "robot_lab v#{RobotLab::VERSION}" if defined?(RobotLab::VERSION)
|
|
107
|
+
parts << "simple_flow v#{SimpleFlow::VERSION}" if defined?(SimpleFlow::VERSION)
|
|
108
|
+
parts << "trak_flow v#{TrakFlow::VERSION}" if defined?(TrakFlow::VERSION)
|
|
109
|
+
parts << "typed_bus v#{TypedBus::VERSION}" if defined?(TypedBus::VERSION)
|
|
110
|
+
parts.join(', ')
|
|
55
111
|
end
|
|
56
112
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
indent = 18
|
|
62
|
-
spaces = " "*indent
|
|
63
|
-
width = TTY::Screen.width - indent - 2
|
|
64
|
-
|
|
65
|
-
mcp_version = defined?(RubyLLM::MCP::VERSION) ? " MCP v" + RubyLLM::MCP::VERSION : ''
|
|
66
|
-
|
|
67
|
-
# Extract model names from config (handles ModelSpec objects or Hashes)
|
|
68
|
-
model_display = if AIA.config&.models && !AIA.config.models.empty?
|
|
69
|
-
models = AIA.config.models
|
|
70
|
-
models.map do |spec|
|
|
71
|
-
if spec.is_a?(AIA::ModelSpec)
|
|
72
|
-
spec.name
|
|
73
|
-
elsif spec.is_a?(Hash)
|
|
74
|
-
spec[:name] || spec['name'] || spec.to_s
|
|
75
|
-
else
|
|
76
|
-
spec.to_s
|
|
77
|
-
end
|
|
78
|
-
end.join(', ')
|
|
113
|
+
def banner_tools
|
|
114
|
+
count = total_tool_count
|
|
115
|
+
if count.positive?
|
|
116
|
+
"#{count} #{count == 1 ? 'tool' : 'tools'} loaded"
|
|
79
117
|
else
|
|
80
|
-
'
|
|
118
|
+
'none loaded'
|
|
81
119
|
end
|
|
120
|
+
end
|
|
82
121
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
122
|
+
def banner_mcp
|
|
123
|
+
connected = mcp_client_labels
|
|
124
|
+
failed = failed_mcp_servers.map { |f| server_name(f) }
|
|
125
|
+
return '(none configured)' if connected.empty? && failed.empty?
|
|
126
|
+
parts = []
|
|
127
|
+
parts << connected.join(', ') unless connected.empty?
|
|
128
|
+
parts << "FAILED: #{failed.join(', ')}" unless failed.empty?
|
|
129
|
+
parts.join(' | ')
|
|
130
|
+
end
|
|
91
131
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if user_tools? && tools?
|
|
105
|
-
tool_names = AIA.config.tool_names
|
|
106
|
-
if tool_names && !tool_names.to_s.empty?
|
|
107
|
-
puts WordWrapper::MinimumRaggedness.new(
|
|
108
|
-
width,
|
|
109
|
-
tool_names.to_s # String of tool names, comma separated
|
|
110
|
-
).wrap
|
|
111
|
-
.split("\n")
|
|
112
|
-
.map{|s| spaces+s+"\n"}
|
|
113
|
-
.join
|
|
132
|
+
# Post-connection: connected_mcp_servers is the authoritative name list;
|
|
133
|
+
# mcp_server_tool_counts provides per-server tool counts.
|
|
134
|
+
# Pre-connection fallback: read live from RubyLLM::MCP.clients (--require clients).
|
|
135
|
+
def mcp_client_labels
|
|
136
|
+
return [] if AIA.config&.flags&.no_mcp
|
|
137
|
+
|
|
138
|
+
connected = AIA.config&.connected_mcp_servers
|
|
139
|
+
unless connected.nil?
|
|
140
|
+
counts = AIA.config&.mcp_server_tool_counts || {}
|
|
141
|
+
return connected.map do |name|
|
|
142
|
+
count = counts[name]
|
|
143
|
+
count ? "#{name}(#{count})" : name
|
|
114
144
|
end
|
|
115
145
|
end
|
|
146
|
+
|
|
147
|
+
return [] unless defined?(RubyLLM::MCP)
|
|
148
|
+
RubyLLM::MCP.clients.filter_map do |name, client|
|
|
149
|
+
count = client.tools.count rescue nil
|
|
150
|
+
"#{name}(#{count})" if count
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def banner_db
|
|
155
|
+
refresh = models_last_refresh
|
|
156
|
+
refresh ? "refreshed #{refresh.gsub(' ', ' at ')}" : 'not yet refreshed'
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def banner_crew
|
|
160
|
+
names = robot_names
|
|
161
|
+
return '' if names.empty?
|
|
162
|
+
format_crew_mentions(names)
|
|
116
163
|
end
|
|
117
164
|
end
|
|
118
165
|
end
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
# lib/aia/
|
|
1
|
+
# lib/aia/variable_input_collector.rb
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
4
|
require 'fileutils'
|
|
5
5
|
|
|
6
6
|
module AIA
|
|
7
|
-
class
|
|
7
|
+
class VariableInputCollector
|
|
8
8
|
def initialize
|
|
9
9
|
# No prompt dependency — just handles user input for parameter collection
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
def request_variable_value(variable_name:, default_value: nil)
|
|
14
13
|
Reline::HISTORY.clear
|
|
15
14
|
|
|
@@ -28,24 +27,24 @@ module AIA
|
|
|
28
27
|
|
|
29
28
|
if input.nil? # Ctrl+D
|
|
30
29
|
return default_value if default_value
|
|
31
|
-
|
|
32
|
-
exit(1)
|
|
30
|
+
raise AIA::Error, "Parameter '#{variable_name}' is required."
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
value = input.strip
|
|
36
34
|
if value.empty?
|
|
37
35
|
return default_value if default_value
|
|
38
|
-
|
|
39
|
-
exit(1)
|
|
36
|
+
raise AIA::Error, "Parameter '#{variable_name}' is required."
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
value
|
|
43
40
|
rescue Interrupt
|
|
44
|
-
|
|
45
|
-
exit(1)
|
|
41
|
+
raise AIA::Error, "Variable input interrupted."
|
|
46
42
|
ensure
|
|
47
43
|
Reline.line_editor.prompt_proc = original_prompt_proc
|
|
48
44
|
end
|
|
49
45
|
end
|
|
50
46
|
end
|
|
47
|
+
|
|
48
|
+
# Backward-compatibility alias — prefer VariableInputCollector in new code.
|
|
49
|
+
HistoryManager = VariableInputCollector
|
|
51
50
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# lib/aia/verification_network.rb
|
|
4
|
+
#
|
|
5
|
+
# Self-review via consensus: two robots independently answer,
|
|
6
|
+
# then a reconciler compares and produces a verified answer.
|
|
7
|
+
|
|
8
|
+
module AIA
|
|
9
|
+
class VerificationNetwork
|
|
10
|
+
# Build a verification network for the given config.
|
|
11
|
+
#
|
|
12
|
+
# @param config the AIA configuration
|
|
13
|
+
# @return [RobotLab::Network] a consensus network with verifiers + reconciler
|
|
14
|
+
def self.build(config)
|
|
15
|
+
model = config.models.first
|
|
16
|
+
run_config = RobotFactory.build_run_config(config)
|
|
17
|
+
tools = ToolLoader.filtered_tools(config)
|
|
18
|
+
mcp = RobotFactory.mcp_server_configs(config)
|
|
19
|
+
model_id = model.internal_id
|
|
20
|
+
|
|
21
|
+
# rubocop:disable Metrics/BlockLength
|
|
22
|
+
RobotLab.create_network(name: "aia-verification") do
|
|
23
|
+
worker_a = RobotLab.build(
|
|
24
|
+
name: "verifier-a",
|
|
25
|
+
model: model_id,
|
|
26
|
+
system_prompt: "Answer the following question. Be thorough and precise.",
|
|
27
|
+
local_tools: tools,
|
|
28
|
+
mcp_servers: mcp,
|
|
29
|
+
config: run_config
|
|
30
|
+
)
|
|
31
|
+
task :verify_a, worker_a, depends_on: :none
|
|
32
|
+
|
|
33
|
+
worker_b = RobotLab.build(
|
|
34
|
+
name: "verifier-b",
|
|
35
|
+
model: model_id,
|
|
36
|
+
system_prompt: "Answer the following question independently. " \
|
|
37
|
+
"Focus on correctness and completeness.",
|
|
38
|
+
local_tools: tools,
|
|
39
|
+
mcp_servers: mcp,
|
|
40
|
+
config: run_config
|
|
41
|
+
)
|
|
42
|
+
task :verify_b, worker_b, depends_on: :none
|
|
43
|
+
|
|
44
|
+
reconciler = RobotLab.build(
|
|
45
|
+
name: "reconciler",
|
|
46
|
+
model: model_id,
|
|
47
|
+
system_prompt: "Compare two independent answers to the same question. " \
|
|
48
|
+
"Identify agreements and disagreements. " \
|
|
49
|
+
"Produce a final, reconciled answer that is most accurate. " \
|
|
50
|
+
"Note any areas of uncertainty.",
|
|
51
|
+
config: run_config
|
|
52
|
+
)
|
|
53
|
+
task :reconcile, reconciler, depends_on: %i[verify_a verify_b]
|
|
54
|
+
end
|
|
55
|
+
# rubocop:enable Metrics/BlockLength
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/aia.rb
CHANGED
|
@@ -1,65 +1,96 @@
|
|
|
1
1
|
# lib/aia.rb
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# provides an interface for interacting with AI models and managing prompts.
|
|
6
|
-
|
|
7
|
-
require 'ruby_llm'
|
|
8
|
-
require 'ruby_llm/mcp'
|
|
9
|
-
|
|
10
|
-
# NOTE: Complex parameters are now supported natively in ruby_llm-mcp >= 0.8.0
|
|
11
|
-
# The support_complex_parameters! method has been removed.
|
|
3
|
+
# Main entry point for the AIA application (v2).
|
|
4
|
+
# AIA is a thin CLI shell orchestrating robot_lab robots.
|
|
12
5
|
|
|
6
|
+
gem 'bigdecimal', '>= 4.0'
|
|
7
|
+
require 'bigdecimal'
|
|
8
|
+
require 'robot_lab'
|
|
13
9
|
require 'pm'
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
include DebugMe
|
|
18
|
-
$DEBUG_ME = false
|
|
19
|
-
DebugMeDefaultOptions[:skip1] = true
|
|
20
|
-
|
|
21
|
-
require_relative 'extensions/openstruct_merge' # adds self.merge self.get_value
|
|
22
|
-
require_relative 'extensions/ruby_llm/modalities' # adds model.modalities.text_to_text? etc.
|
|
23
|
-
|
|
24
|
-
require_relative 'refinements/string' # adds #include_any? #include_all?
|
|
11
|
+
require_relative 'aia/patches/ruby_llm_tool_error'
|
|
12
|
+
require_relative 'aia/patches/ruby_llm_streaming_error'
|
|
25
13
|
|
|
26
14
|
require_relative 'aia/errors'
|
|
15
|
+
require_relative 'aia/turn_state'
|
|
16
|
+
require_relative 'aia/content_extractor'
|
|
27
17
|
require_relative 'aia/utility'
|
|
28
18
|
require_relative 'aia/skill_utils'
|
|
29
19
|
require_relative 'aia/version'
|
|
30
20
|
require_relative 'aia/config'
|
|
31
21
|
require_relative 'aia/logger'
|
|
32
22
|
|
|
33
|
-
# Top-level logger method available anywhere in the application
|
|
34
|
-
def logger
|
|
35
|
-
AIA::LoggerManager.aia_logger
|
|
36
|
-
end
|
|
37
|
-
|
|
38
23
|
require_relative 'aia/config/cli_parser'
|
|
39
24
|
require_relative 'aia/config/validator'
|
|
40
25
|
require_relative 'aia/prompt_handler'
|
|
41
|
-
require_relative 'aia/
|
|
26
|
+
require_relative 'aia/tool_loader'
|
|
27
|
+
require_relative 'aia/plugin_loader'
|
|
28
|
+
require_relative 'aia/plugin_monitor'
|
|
29
|
+
require_relative 'aia/spawn_spec_parser'
|
|
30
|
+
require_relative 'aia/crew'
|
|
31
|
+
require_relative 'aia/tools/recruit_robot_tool'
|
|
32
|
+
require_relative 'aia/tools/reskill_robot_tool'
|
|
33
|
+
require_relative 'aia/system_prompt_assembler'
|
|
34
|
+
require_relative 'aia/mcp_config_normalizer'
|
|
35
|
+
require_relative 'aia/network_memory_manager'
|
|
36
|
+
require_relative 'aia/robot_factory'
|
|
37
|
+
require_relative 'aia/robot_builder'
|
|
42
38
|
require_relative 'aia/directive_processor'
|
|
43
|
-
require_relative 'aia/
|
|
39
|
+
require_relative 'aia/variable_input_collector'
|
|
44
40
|
require_relative 'aia/ui_presenter'
|
|
45
|
-
require_relative 'aia/chat_processor_service'
|
|
46
41
|
require_relative 'aia/input_collector'
|
|
47
|
-
require_relative 'aia/
|
|
42
|
+
require_relative 'aia/handler_context'
|
|
43
|
+
require_relative 'aia/handler_protocol'
|
|
44
|
+
require_relative 'aia/model_alias_registry'
|
|
45
|
+
require_relative 'aia/model_switch_handler'
|
|
46
|
+
require_relative 'aia/mcp_discovery'
|
|
47
|
+
require_relative 'aia/mcp_grouper'
|
|
48
|
+
require_relative 'aia/cost_calculator'
|
|
49
|
+
require_relative 'aia/session_tracker'
|
|
50
|
+
require_relative 'aia/similarity_scorer'
|
|
51
|
+
require_relative 'aia/fact_asserter'
|
|
52
|
+
require_relative 'aia/tool_filter'
|
|
53
|
+
require_relative 'aia/tool_filter_registry'
|
|
54
|
+
require_relative 'aia/tool_filter_strategy'
|
|
55
|
+
require_relative 'aia/streaming_runner'
|
|
56
|
+
require_relative 'aia/mention_router'
|
|
57
|
+
require_relative 'aia/special_mode_handler'
|
|
58
|
+
require_relative 'aia/mcp_connection_manager'
|
|
59
|
+
require_relative 'aia/startup_coordinator'
|
|
60
|
+
require_relative 'aia/pipeline_orchestrator'
|
|
48
61
|
require_relative 'aia/chat_loop'
|
|
49
62
|
require_relative 'aia/session'
|
|
50
63
|
|
|
51
64
|
# The AIA module serves as the namespace for the AIA application, which
|
|
52
65
|
# provides an interface for interacting with AI models and managing prompts.
|
|
53
66
|
module AIA
|
|
67
|
+
require 'debug_me'
|
|
68
|
+
include DebugMe
|
|
69
|
+
|
|
70
|
+
$DEBUG_ME = false
|
|
71
|
+
DebugMeDefaultOptions[:skip1] = true
|
|
72
|
+
|
|
54
73
|
at_exit do
|
|
55
|
-
|
|
74
|
+
$stderr.puts 'Exiting AIA application...'
|
|
56
75
|
end
|
|
57
76
|
|
|
58
77
|
@config = nil
|
|
59
78
|
@client = nil
|
|
79
|
+
@session_tracker = nil
|
|
80
|
+
@turn_state = TurnState.new
|
|
60
81
|
|
|
61
82
|
class << self
|
|
62
|
-
attr_accessor :config, :client
|
|
83
|
+
attr_accessor :config, :client, :session_tracker, :turn_state, :task_coordinator
|
|
84
|
+
|
|
85
|
+
def logger
|
|
86
|
+
AIA::LoggerManager.aia_logger
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def reset!
|
|
90
|
+
@config = @client = @session_tracker = @turn_state =
|
|
91
|
+
@task_coordinator = nil
|
|
92
|
+
ToolLoader.reset_instance!
|
|
93
|
+
end
|
|
63
94
|
|
|
64
95
|
def good_file?(filename)
|
|
65
96
|
File.exist?(filename) &&
|
|
@@ -67,78 +98,92 @@ module AIA
|
|
|
67
98
|
!File.directory?(filename)
|
|
68
99
|
end
|
|
69
100
|
|
|
70
|
-
|
|
71
101
|
def bad_file?(filename)
|
|
72
102
|
!good_file?(filename)
|
|
73
103
|
end
|
|
74
104
|
|
|
75
|
-
|
|
76
105
|
# Convenience flag accessors (delegate to config.flags section)
|
|
77
106
|
def chat?
|
|
78
107
|
@config&.flags&.chat == true
|
|
79
108
|
end
|
|
80
109
|
|
|
81
|
-
|
|
82
110
|
def debug?
|
|
83
111
|
@config&.flags&.debug == true
|
|
84
112
|
end
|
|
85
113
|
|
|
114
|
+
# Emit a warning always; if --debug is active and an exception is given,
|
|
115
|
+
# also print the first 5 backtrace frames so errors are diagnosable.
|
|
116
|
+
#
|
|
117
|
+
# @param msg [String]
|
|
118
|
+
# @param exc [Exception, nil]
|
|
119
|
+
def debug_warn(msg, exc: nil)
|
|
120
|
+
$stderr.puts msg
|
|
121
|
+
return unless exc && exc.backtrace && config&.flags&.debug
|
|
122
|
+
$stderr.puts exc.backtrace.first(5).join("\n")
|
|
123
|
+
end
|
|
86
124
|
|
|
87
125
|
def verbose?
|
|
88
126
|
@config&.flags&.verbose == true
|
|
89
127
|
end
|
|
90
128
|
|
|
91
|
-
|
|
92
129
|
def fuzzy?
|
|
93
130
|
@config&.flags&.fuzzy == true
|
|
94
131
|
end
|
|
95
132
|
|
|
96
|
-
|
|
97
133
|
def speak?
|
|
98
134
|
@config&.flags&.speak == true
|
|
99
135
|
end
|
|
100
136
|
|
|
137
|
+
def paint?
|
|
138
|
+
@config&.flags&.paint == true
|
|
139
|
+
end
|
|
101
140
|
|
|
102
141
|
def append?
|
|
103
142
|
@config&.output&.append == true
|
|
104
143
|
end
|
|
105
144
|
|
|
106
|
-
|
|
107
145
|
def run
|
|
108
|
-
# Parse CLI arguments
|
|
109
146
|
cli_overrides = CLIParser.parse
|
|
110
|
-
|
|
111
|
-
# Create config with CLI overrides
|
|
112
147
|
@config = Config.setup(cli_overrides)
|
|
148
|
+
return if ConfigValidator.tailor(@config) == :early_exit
|
|
149
|
+
|
|
150
|
+
PluginLoader.load!(@config)
|
|
151
|
+
start_plugin_monitor
|
|
152
|
+
RobotFactory.setup(@config)
|
|
153
|
+
load_fzf_integration
|
|
154
|
+
start_session
|
|
155
|
+
rescue AIA::ConfigurationError => e
|
|
156
|
+
$stderr.puts e.message
|
|
157
|
+
exit 1
|
|
158
|
+
rescue AIA::Error => e
|
|
159
|
+
$stderr.puts e.message
|
|
160
|
+
exit 1
|
|
161
|
+
end
|
|
113
162
|
|
|
114
|
-
|
|
115
|
-
ConfigValidator.tailor(@config)
|
|
116
|
-
|
|
117
|
-
# Load Fzf if fuzzy search is enabled and fzf is installed
|
|
118
|
-
if @config.flags.fuzzy
|
|
119
|
-
begin
|
|
120
|
-
if system('which fzf >/dev/null 2>&1')
|
|
121
|
-
require_relative 'aia/fzf'
|
|
122
|
-
else
|
|
123
|
-
warn 'Warning: Fuzzy search enabled but fzf not found. Install fzf for enhanced search capabilities.'
|
|
124
|
-
end
|
|
125
|
-
rescue StandardError => e
|
|
126
|
-
warn "Warning: Failed to load fzf: #{e.message}"
|
|
127
|
-
end
|
|
128
|
-
end
|
|
163
|
+
private
|
|
129
164
|
|
|
130
|
-
|
|
165
|
+
def start_plugin_monitor
|
|
166
|
+
return unless @config.flags.chat
|
|
131
167
|
|
|
132
|
-
@
|
|
168
|
+
@plugin_monitor = PluginMonitor.new(@config).start
|
|
169
|
+
at_exit { @plugin_monitor&.stop }
|
|
170
|
+
end
|
|
133
171
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
172
|
+
def load_fzf_integration
|
|
173
|
+
return unless @config.flags.fuzzy
|
|
174
|
+
|
|
175
|
+
if system('which fzf >/dev/null 2>&1')
|
|
176
|
+
require_relative 'aia/fzf'
|
|
177
|
+
else
|
|
178
|
+
$stderr.puts 'Warning: Fuzzy search enabled but fzf not found. Install fzf for enhanced search capabilities.'
|
|
179
|
+
end
|
|
180
|
+
rescue StandardError => e
|
|
181
|
+
$stderr.puts "Warning: Failed to load fzf: #{e.message}"
|
|
182
|
+
end
|
|
140
183
|
|
|
141
|
-
|
|
184
|
+
def start_session
|
|
185
|
+
session = Session.new(PromptHandler.new)
|
|
186
|
+
at_exit { session.cleanup }
|
|
142
187
|
session.start
|
|
143
188
|
end
|
|
144
189
|
end
|
data/mkdocs.yml
CHANGED
|
@@ -151,6 +151,7 @@ nav:
|
|
|
151
151
|
- Getting Started: guides/getting-started.md
|
|
152
152
|
- Chat Mode: guides/chat.md
|
|
153
153
|
- Working with Models: guides/models.md
|
|
154
|
+
- Crews: guides/crew.md
|
|
154
155
|
- Local Models: guides/local-models.md
|
|
155
156
|
- Available Models: guides/available-models.md
|
|
156
157
|
- Image Generation: guides/image-generation.md
|