aia 0.9.23 → 0.10.2
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/.version +1 -1
- data/CHANGELOG.md +95 -3
- data/README.md +187 -60
- data/bin/aia +6 -0
- data/docs/cli-reference.md +145 -72
- data/docs/configuration.md +156 -19
- data/docs/directives-reference.md +28 -8
- data/docs/examples/tools/index.md +2 -2
- data/docs/faq.md +11 -11
- data/docs/guides/available-models.md +11 -11
- data/docs/guides/basic-usage.md +18 -17
- data/docs/guides/chat.md +57 -11
- data/docs/guides/executable-prompts.md +15 -15
- data/docs/guides/first-prompt.md +2 -2
- data/docs/guides/getting-started.md +6 -6
- data/docs/guides/image-generation.md +24 -24
- data/docs/guides/local-models.md +2 -2
- data/docs/guides/models.md +96 -18
- data/docs/guides/tools.md +4 -4
- data/docs/index.md +2 -2
- data/docs/installation.md +2 -2
- data/docs/prompt_management.md +11 -11
- data/docs/security.md +3 -3
- data/docs/workflows-and-pipelines.md +1 -1
- data/examples/README.md +6 -6
- data/examples/headlines +3 -3
- data/lib/aia/aia_completion.bash +2 -2
- data/lib/aia/aia_completion.fish +4 -4
- data/lib/aia/aia_completion.zsh +2 -2
- data/lib/aia/chat_processor_service.rb +31 -21
- data/lib/aia/config/cli_parser.rb +403 -403
- data/lib/aia/config/config_section.rb +87 -0
- data/lib/aia/config/defaults.yml +219 -0
- data/lib/aia/config/defaults_loader.rb +147 -0
- data/lib/aia/config/mcp_parser.rb +151 -0
- data/lib/aia/config/model_spec.rb +67 -0
- data/lib/aia/config/validator.rb +185 -136
- data/lib/aia/config.rb +336 -17
- data/lib/aia/directive_processor.rb +14 -6
- data/lib/aia/directives/checkpoint.rb +283 -0
- data/lib/aia/directives/configuration.rb +27 -98
- data/lib/aia/directives/models.rb +15 -9
- data/lib/aia/directives/registry.rb +2 -0
- data/lib/aia/directives/utility.rb +25 -9
- data/lib/aia/directives/web_and_file.rb +50 -47
- data/lib/aia/logger.rb +328 -0
- data/lib/aia/prompt_handler.rb +18 -22
- data/lib/aia/ruby_llm_adapter.rb +584 -65
- data/lib/aia/session.rb +49 -156
- data/lib/aia/topic_context.rb +125 -0
- data/lib/aia/ui_presenter.rb +20 -16
- data/lib/aia/utility.rb +50 -18
- data/lib/aia.rb +91 -66
- data/lib/extensions/ruby_llm/modalities.rb +2 -0
- data/mcp_servers/apple-mcp.json +8 -0
- data/mcp_servers/mcp_server_chart.json +11 -0
- data/mcp_servers/playwright_one.json +8 -0
- data/mcp_servers/playwright_two.json +8 -0
- data/mcp_servers/tavily_mcp_server.json +8 -0
- metadata +85 -26
- data/lib/aia/config/base.rb +0 -288
- data/lib/aia/config/defaults.rb +0 -91
- data/lib/aia/config/file_loader.rb +0 -163
- data/lib/aia/context_manager.rb +0 -134
- data/mcp_servers/imcp.json +0 -7
- data/mcp_servers/launcher.json +0 -11
- data/mcp_servers/timeserver.json +0 -8
data/lib/aia/config/base.rb
DELETED
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
# lib/aia/config/base.rb
|
|
2
|
-
|
|
3
|
-
require 'ostruct'
|
|
4
|
-
require 'date'
|
|
5
|
-
require_relative 'defaults'
|
|
6
|
-
require_relative 'cli_parser'
|
|
7
|
-
require_relative 'file_loader'
|
|
8
|
-
require_relative 'validator'
|
|
9
|
-
|
|
10
|
-
module AIA
|
|
11
|
-
module ConfigModules
|
|
12
|
-
module Base
|
|
13
|
-
class << self
|
|
14
|
-
# Delegate to other config modules
|
|
15
|
-
def cli_options
|
|
16
|
-
CLIParser.cli_options
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def cf_options(file)
|
|
20
|
-
FileLoader.cf_options(file)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def dump_config(config, file)
|
|
24
|
-
FileLoader.dump_config(config, file)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def generate_completion_script(shell)
|
|
28
|
-
FileLoader.generate_completion_script(shell)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def tailor_the_config(config)
|
|
32
|
-
Validator.tailor_the_config(config)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def validate_pipeline_prompts(config)
|
|
36
|
-
Validator.validate_pipeline_prompts(config)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def normalize_boolean_flag(config, flag)
|
|
40
|
-
Validator.normalize_boolean_flag(config, flag)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def process_tools_option(path_list, config)
|
|
44
|
-
CLIParser.process_tools_option(path_list, config)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def validate_and_set_context_files(config, remaining_args)
|
|
48
|
-
Validator.validate_and_set_context_files(config, remaining_args)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def setup_mode_options(opts, config)
|
|
52
|
-
CLIParser.setup_mode_options(opts, config)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def parse_config_content(content, ext)
|
|
56
|
-
FileLoader.parse_config_content(content, ext)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def normalize_last_refresh_date(config)
|
|
60
|
-
FileLoader.normalize_last_refresh_date(config)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def process_prompt_id_from_args(config, remaining_args)
|
|
64
|
-
Validator.process_prompt_id_from_args(config, remaining_args)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def process_role_configuration(config)
|
|
68
|
-
Validator.process_role_configuration(config)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def prepare_pipeline(config)
|
|
72
|
-
Validator.prepare_pipeline(config)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def setup_model_options(opts, config)
|
|
76
|
-
CLIParser.setup_model_options(opts, config)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def setup_ai_parameters(opts, config)
|
|
80
|
-
CLIParser.setup_ai_parameters(opts, config)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def read_and_process_config_file(file)
|
|
84
|
-
FileLoader.read_and_process_config_file(file)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def process_stdin_content
|
|
88
|
-
Validator.process_stdin_content
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def process_allowed_tools_option(tools_list, config)
|
|
92
|
-
CLIParser.process_allowed_tools_option(tools_list, config)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def process_rejected_tools_option(tools_list, config)
|
|
96
|
-
CLIParser.process_rejected_tools_option(tools_list, config)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def normalize_boolean_flags(config)
|
|
100
|
-
Validator.normalize_boolean_flags(config)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def handle_executable_prompt(config)
|
|
104
|
-
Validator.handle_executable_prompt(config)
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def handle_fuzzy_search_prompt_id(config)
|
|
108
|
-
Validator.handle_fuzzy_search_prompt_id(config)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def create_option_parser(config)
|
|
112
|
-
CLIParser.create_option_parser(config)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def apply_file_config_to_struct(config, file_config)
|
|
116
|
-
FileLoader.apply_file_config_to_struct(config, file_config)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def configure_prompt_manager(config)
|
|
120
|
-
Validator.configure_prompt_manager(config)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def setup
|
|
124
|
-
default_config = Defaults::DEFAULT_CONFIG.dup
|
|
125
|
-
cli_config = cli_options
|
|
126
|
-
envar_config = envar_options(default_config, cli_config)
|
|
127
|
-
|
|
128
|
-
file = envar_config.config_file unless envar_config.config_file.nil?
|
|
129
|
-
file = cli_config.config_file unless cli_config.config_file.nil?
|
|
130
|
-
|
|
131
|
-
cf_config = cf_options(file)
|
|
132
|
-
|
|
133
|
-
config = OpenStruct.merge(
|
|
134
|
-
default_config,
|
|
135
|
-
cf_config || {},
|
|
136
|
-
envar_config || {},
|
|
137
|
-
cli_config || {}
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
config = tailor_the_config(config)
|
|
141
|
-
load_libraries(config)
|
|
142
|
-
load_tools(config)
|
|
143
|
-
load_mcp_servers(config)
|
|
144
|
-
|
|
145
|
-
if config.dump_file
|
|
146
|
-
dump_config(config, config.dump_file)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
config
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def load_libraries(config)
|
|
153
|
-
return if config.require_libs.empty?
|
|
154
|
-
|
|
155
|
-
exit_on_error = false
|
|
156
|
-
|
|
157
|
-
config.require_libs.each do |library|
|
|
158
|
-
begin
|
|
159
|
-
require(library)
|
|
160
|
-
rescue => e
|
|
161
|
-
STDERR.puts "Error loading library '#{library}' #{e.message}"
|
|
162
|
-
exit_on_error = true
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
exit(1) if exit_on_error
|
|
167
|
-
|
|
168
|
-
config
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def load_tools(config)
|
|
172
|
-
return if config.tool_paths.empty?
|
|
173
|
-
|
|
174
|
-
require_all_tools(config)
|
|
175
|
-
|
|
176
|
-
config
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def require_all_tools(config)
|
|
180
|
-
exit_on_error = false
|
|
181
|
-
|
|
182
|
-
config.tool_paths.each do |tool_path|
|
|
183
|
-
begin
|
|
184
|
-
# expands path based on PWD
|
|
185
|
-
absolute_tool_path = File.expand_path(tool_path)
|
|
186
|
-
require(absolute_tool_path)
|
|
187
|
-
rescue => e
|
|
188
|
-
STDERR.puts "Error loading tool '#{tool_path}' #{e.message}"
|
|
189
|
-
exit_on_error = true
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
exit(1) if exit_on_error
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def load_mcp_servers(config)
|
|
197
|
-
servers = config.mcp_servers
|
|
198
|
-
|
|
199
|
-
return config if servers.nil? || (servers.respond_to?(:empty?) && servers.empty?)
|
|
200
|
-
|
|
201
|
-
servers.each do |server|
|
|
202
|
-
name = server[:name] || server["name"]
|
|
203
|
-
command = server[:command] || server["command"]
|
|
204
|
-
args = server[:args] || server["args"] || []
|
|
205
|
-
env = server[:env] || server["env"] || {}
|
|
206
|
-
timeout = server[:timeout] || server["timeout"] || 8000 # default 8 seconds in ms
|
|
207
|
-
|
|
208
|
-
unless name && command
|
|
209
|
-
STDERR.puts "WARNING: MCP server entry missing name or command: #{server.inspect}"
|
|
210
|
-
next
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Resolve command path if not absolute
|
|
214
|
-
resolved_command = resolve_command_path(command)
|
|
215
|
-
unless resolved_command
|
|
216
|
-
STDERR.puts "WARNING: MCP server '#{name}' command not found: #{command}"
|
|
217
|
-
next
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
begin
|
|
221
|
-
RubyLLM::MCP.add_client(
|
|
222
|
-
name: name,
|
|
223
|
-
transport_type: :stdio,
|
|
224
|
-
request_timeout: timeout,
|
|
225
|
-
config: {
|
|
226
|
-
command: resolved_command,
|
|
227
|
-
args: args,
|
|
228
|
-
env: env
|
|
229
|
-
}
|
|
230
|
-
)
|
|
231
|
-
rescue => e
|
|
232
|
-
STDERR.puts "ERROR: Failed to load MCP server '#{name}': #{e.message}"
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
config
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def resolve_command_path(command)
|
|
240
|
-
# If already absolute path, verify it exists
|
|
241
|
-
if command.start_with?('/')
|
|
242
|
-
return File.executable?(command) ? command : nil
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
# Search in PATH
|
|
246
|
-
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
|
247
|
-
full_path = File.join(dir, command)
|
|
248
|
-
return full_path if File.executable?(full_path)
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
nil
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# envar values are always String object so need other config
|
|
255
|
-
# layers to know the prompter type for each key's value
|
|
256
|
-
def envar_options(default, cli_config)
|
|
257
|
-
config = OpenStruct.merge(default, cli_config)
|
|
258
|
-
envars = ENV.keys.select { |key, _| key.start_with?('AIA_') }
|
|
259
|
-
envars.each do |envar|
|
|
260
|
-
key = envar.sub(/^AIA_/, '').downcase.to_sym
|
|
261
|
-
value = ENV[envar]
|
|
262
|
-
|
|
263
|
-
value = case config[key]
|
|
264
|
-
when TrueClass, FalseClass
|
|
265
|
-
value.downcase == 'true'
|
|
266
|
-
when Integer
|
|
267
|
-
value.to_i
|
|
268
|
-
when Float
|
|
269
|
-
value.to_f
|
|
270
|
-
when Array
|
|
271
|
-
# Special handling for :model to support inline role syntax (ADR-005 v2)
|
|
272
|
-
if key == :model && value.include?('=')
|
|
273
|
-
CLIParser.parse_models_with_roles(value)
|
|
274
|
-
else
|
|
275
|
-
value.split(',').map(&:strip)
|
|
276
|
-
end
|
|
277
|
-
else
|
|
278
|
-
value # defaults to String
|
|
279
|
-
end
|
|
280
|
-
config[key] = value
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
config
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
end
|
data/lib/aia/config/defaults.rb
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# lib/aia/config/defaults.rb
|
|
2
|
-
|
|
3
|
-
require 'yaml'
|
|
4
|
-
require 'toml-rb'
|
|
5
|
-
require 'date'
|
|
6
|
-
require 'prompt_manager'
|
|
7
|
-
|
|
8
|
-
module AIA
|
|
9
|
-
module ConfigModules
|
|
10
|
-
module Defaults
|
|
11
|
-
DEFAULT_CONFIG = OpenStruct.new({
|
|
12
|
-
adapter: 'ruby_llm', # 'ruby_llm' or ???
|
|
13
|
-
#
|
|
14
|
-
aia_dir: File.join(ENV['HOME'], '.aia'),
|
|
15
|
-
config_file: File.join(ENV['HOME'], '.aia', 'config.yml'),
|
|
16
|
-
out_file: 'temp.md',
|
|
17
|
-
log_file: File.join(ENV['HOME'], '.prompts', '_prompts.log'),
|
|
18
|
-
context_files: [],
|
|
19
|
-
#
|
|
20
|
-
prompts_dir: File.join(ENV['HOME'], '.prompts'),
|
|
21
|
-
prompt_extname: PromptManager::Storage::FileSystemAdapter::PROMPT_EXTENSION,
|
|
22
|
-
#
|
|
23
|
-
roles_prefix: 'roles',
|
|
24
|
-
roles_dir: File.join(ENV['HOME'], '.prompts', 'roles'),
|
|
25
|
-
role: '',
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
system_prompt: '',
|
|
29
|
-
|
|
30
|
-
# Tools
|
|
31
|
-
tools: '', # Comma-separated string of loaded tool names (set by adapter)
|
|
32
|
-
allowed_tools: nil, # nil means all tools are allowed; otherwise an Array of Strings which are the tool names
|
|
33
|
-
rejected_tools: nil, # nil means no tools are rejected
|
|
34
|
-
tool_paths: [], # Strings - absolute and relative to tools
|
|
35
|
-
|
|
36
|
-
# Flags
|
|
37
|
-
markdown: true,
|
|
38
|
-
shell: true,
|
|
39
|
-
erb: true,
|
|
40
|
-
chat: false,
|
|
41
|
-
clear: false,
|
|
42
|
-
terse: false,
|
|
43
|
-
verbose: false,
|
|
44
|
-
debug: $DEBUG_ME,
|
|
45
|
-
fuzzy: false,
|
|
46
|
-
speak: false,
|
|
47
|
-
append: false, # Default to not append to existing out_file
|
|
48
|
-
|
|
49
|
-
# workflow
|
|
50
|
-
pipeline: [],
|
|
51
|
-
|
|
52
|
-
# PromptManager::Prompt Tailoring
|
|
53
|
-
parameter_regex: PromptManager::Prompt.parameter_regex.to_s,
|
|
54
|
-
|
|
55
|
-
# LLM tuning parameters
|
|
56
|
-
temperature: 0.7,
|
|
57
|
-
max_tokens: 2048,
|
|
58
|
-
top_p: 1.0,
|
|
59
|
-
frequency_penalty: 0.0,
|
|
60
|
-
presence_penalty: 0.0,
|
|
61
|
-
|
|
62
|
-
# Audio Parameters
|
|
63
|
-
voice: 'alloy',
|
|
64
|
-
speak_command: 'afplay', # 'afplay' for audio files on MacOS
|
|
65
|
-
|
|
66
|
-
# Image Parameters
|
|
67
|
-
image_size: '1024x1024',
|
|
68
|
-
image_quality: 'standard',
|
|
69
|
-
image_style: 'vivid',
|
|
70
|
-
|
|
71
|
-
# Models
|
|
72
|
-
model: ['gpt-4o-mini'],
|
|
73
|
-
consensus: nil, # nil/false = individual responses; true = consensus response
|
|
74
|
-
speech_model: 'tts-1',
|
|
75
|
-
transcription_model: 'whisper-1',
|
|
76
|
-
embedding_model: 'text-embedding-ada-002',
|
|
77
|
-
image_model: 'dall-e-3',
|
|
78
|
-
|
|
79
|
-
# Model Regristery
|
|
80
|
-
refresh: 7, # days between refreshes of model info; 0 means every startup
|
|
81
|
-
last_refresh: Date.today - 1,
|
|
82
|
-
|
|
83
|
-
# Ruby libraries to require for Ruby binding
|
|
84
|
-
require_libs: [],
|
|
85
|
-
|
|
86
|
-
# MCP Servers (nil means not configured, set in config file)
|
|
87
|
-
mcp_servers: nil,
|
|
88
|
-
}).freeze
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
# lib/aia/config/file_loader.rb
|
|
2
|
-
|
|
3
|
-
require 'yaml'
|
|
4
|
-
require 'toml-rb'
|
|
5
|
-
require 'erb'
|
|
6
|
-
require 'date'
|
|
7
|
-
|
|
8
|
-
module AIA
|
|
9
|
-
module ConfigModules
|
|
10
|
-
module FileLoader
|
|
11
|
-
class << self
|
|
12
|
-
def load_config_file(file, config)
|
|
13
|
-
if File.exist?(file)
|
|
14
|
-
ext = File.extname(file).downcase
|
|
15
|
-
content = File.read(file)
|
|
16
|
-
|
|
17
|
-
# Process ERB if filename ends with .erb
|
|
18
|
-
if file.end_with?('.erb')
|
|
19
|
-
content = ERB.new(content).result
|
|
20
|
-
file = file.chomp('.erb')
|
|
21
|
-
File.write(file, content)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
file_config = case ext
|
|
25
|
-
when '.yml', '.yaml'
|
|
26
|
-
YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)
|
|
27
|
-
when '.toml'
|
|
28
|
-
TomlRB.parse(content)
|
|
29
|
-
else
|
|
30
|
-
raise "Unsupported config file format: #{ext}"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
file_config.each do |key, value|
|
|
34
|
-
config[key.to_sym] = value
|
|
35
|
-
end
|
|
36
|
-
else
|
|
37
|
-
raise "Config file not found: #{file}"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def cf_options(file)
|
|
42
|
-
config = OpenStruct.new
|
|
43
|
-
|
|
44
|
-
if File.exist?(file)
|
|
45
|
-
content = read_and_process_config_file(file)
|
|
46
|
-
file_config = parse_config_content(content, File.extname(file).downcase)
|
|
47
|
-
apply_file_config_to_struct(config, file_config)
|
|
48
|
-
else
|
|
49
|
-
STDERR.puts "WARNING:Config file not found: #{file}"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
normalize_last_refresh_date(config)
|
|
53
|
-
config
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def read_and_process_config_file(file)
|
|
57
|
-
content = File.read(file)
|
|
58
|
-
|
|
59
|
-
# Process ERB if filename ends with .erb
|
|
60
|
-
if file.end_with?('.erb')
|
|
61
|
-
content = ERB.new(content).result
|
|
62
|
-
processed_file = file.chomp('.erb')
|
|
63
|
-
File.write(processed_file, content)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
content
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def parse_config_content(content, ext)
|
|
70
|
-
case ext
|
|
71
|
-
when '.yml', '.yaml'
|
|
72
|
-
YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true)
|
|
73
|
-
when '.toml'
|
|
74
|
-
TomlRB.parse(content)
|
|
75
|
-
else
|
|
76
|
-
raise "Unsupported config file format: #{ext}"
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def apply_file_config_to_struct(config, file_config)
|
|
81
|
-
file_config.each do |key, value|
|
|
82
|
-
# Special handling for model array with roles (ADR-005 v2)
|
|
83
|
-
if (key == :model || key == 'model') && value.is_a?(Array) && value.first.is_a?(Hash)
|
|
84
|
-
config[:model] = process_model_array_with_roles(value)
|
|
85
|
-
else
|
|
86
|
-
config[key] = value
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Process model array with roles from config file (ADR-005 v2)
|
|
92
|
-
# Format: [{model: "gpt-4o", role: "architect"}, ...]
|
|
93
|
-
# Also supports models without roles: [{model: "gpt-4o"}, ...]
|
|
94
|
-
def process_model_array_with_roles(models_array)
|
|
95
|
-
return [] if models_array.nil? || models_array.empty?
|
|
96
|
-
|
|
97
|
-
model_specs = []
|
|
98
|
-
model_counts = Hash.new(0)
|
|
99
|
-
|
|
100
|
-
models_array.each do |spec|
|
|
101
|
-
model_name = spec[:model] || spec['model']
|
|
102
|
-
role_name = spec[:role] || spec['role']
|
|
103
|
-
|
|
104
|
-
model_counts[model_name] += 1
|
|
105
|
-
instance = model_counts[model_name]
|
|
106
|
-
|
|
107
|
-
model_specs << {
|
|
108
|
-
model: model_name,
|
|
109
|
-
role: role_name,
|
|
110
|
-
instance: instance,
|
|
111
|
-
internal_id: instance > 1 ? "#{model_name}##{instance}" : model_name
|
|
112
|
-
}
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
model_specs
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def normalize_last_refresh_date(config)
|
|
119
|
-
return unless config.last_refresh&.is_a?(String)
|
|
120
|
-
|
|
121
|
-
config.last_refresh = Date.strptime(config.last_refresh, '%Y-%m-%d')
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def dump_config(config, file)
|
|
125
|
-
# Implementation for config dump
|
|
126
|
-
ext = File.extname(file).downcase
|
|
127
|
-
|
|
128
|
-
config.last_refresh = config.last_refresh.to_s if config.last_refresh.is_a? Date
|
|
129
|
-
|
|
130
|
-
config_hash = config.to_h
|
|
131
|
-
|
|
132
|
-
# Remove prompt_id to prevent automatic initial pompting in --chat mode
|
|
133
|
-
config_hash.delete(:prompt_id)
|
|
134
|
-
|
|
135
|
-
# Remove dump_file key to prevent automatic exit on next load
|
|
136
|
-
config_hash.delete(:dump_file)
|
|
137
|
-
|
|
138
|
-
content = case ext
|
|
139
|
-
when '.yml', '.yaml'
|
|
140
|
-
YAML.dump(config_hash)
|
|
141
|
-
when '.toml'
|
|
142
|
-
TomlRB.dump(config_hash)
|
|
143
|
-
else
|
|
144
|
-
raise "Unsupported config file format: #{ext}"
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
File.write(file, content)
|
|
148
|
-
puts "Config successfully dumped to #{file}"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def generate_completion_script(shell)
|
|
152
|
-
script_path = File.join(File.dirname(__FILE__), "../../aia_completion.#{shell}")
|
|
153
|
-
|
|
154
|
-
if File.exist?(script_path)
|
|
155
|
-
puts File.read(script_path)
|
|
156
|
-
else
|
|
157
|
-
STDERR.puts "ERROR: The shell '#{shell}' is not supported or the completion script is missing."
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
data/lib/aia/context_manager.rb
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# lib/aia/context_manager.rb
|
|
2
|
-
|
|
3
|
-
module AIA
|
|
4
|
-
# Manages the conversation context for chat sessions.
|
|
5
|
-
class ContextManager
|
|
6
|
-
attr_reader :context, :checkpoints
|
|
7
|
-
|
|
8
|
-
# Initializes the ContextManager with an optional system prompt.
|
|
9
|
-
def initialize(system_prompt: nil)
|
|
10
|
-
@context = []
|
|
11
|
-
@checkpoints = {}
|
|
12
|
-
@checkpoint_counter = 0
|
|
13
|
-
add_system_prompt(system_prompt) if system_prompt && !system_prompt.strip.empty?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Adds a message to the conversation context.
|
|
17
|
-
#
|
|
18
|
-
# @param role [String] The role of the message sender ('user' or 'assistant').
|
|
19
|
-
# @param content [String] The content of the message.
|
|
20
|
-
def add_to_context(role:, content:)
|
|
21
|
-
@context << { role: role, content: content }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Returns the current conversation context.
|
|
25
|
-
# Optionally adds the system prompt if it wasn't added during initialization
|
|
26
|
-
# or needs to be re-added after clearing.
|
|
27
|
-
#
|
|
28
|
-
# @param system_prompt [String, nil] The system prompt to potentially prepend.
|
|
29
|
-
# @return [Array<Hash>] The conversation context array.
|
|
30
|
-
def get_context(system_prompt: nil)
|
|
31
|
-
# Add or replace system prompt if provided and not empty
|
|
32
|
-
if system_prompt && !system_prompt.strip.empty?
|
|
33
|
-
add_system_prompt(system_prompt)
|
|
34
|
-
end
|
|
35
|
-
@context
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Clears the conversation context, optionally keeping the system prompt.
|
|
39
|
-
#
|
|
40
|
-
# @param keep_system_prompt [Boolean] Whether to retain the initial system prompt.
|
|
41
|
-
def clear_context(keep_system_prompt: true)
|
|
42
|
-
if keep_system_prompt && !@context.empty? && @context.first[:role] == 'system'
|
|
43
|
-
@context = [@context.first]
|
|
44
|
-
else
|
|
45
|
-
@context = []
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Clear all checkpoints when clearing context
|
|
49
|
-
@checkpoints.clear
|
|
50
|
-
@checkpoint_counter = 0
|
|
51
|
-
|
|
52
|
-
# Attempt to clear the LLM client's context as well
|
|
53
|
-
begin
|
|
54
|
-
if AIA.config.client && AIA.config.client.respond_to?(:clear_context)
|
|
55
|
-
AIA.config.client.clear_context
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
if AIA.config.respond_to?(:llm) && AIA.config.llm && AIA.config.llm.respond_to?(:clear_context)
|
|
59
|
-
AIA.config.llm.clear_context
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
if defined?(RubyLLM) && RubyLLM.respond_to?(:chat) && RubyLLM.chat.respond_to?(:clear_history)
|
|
63
|
-
RubyLLM.chat.clear_history
|
|
64
|
-
end
|
|
65
|
-
rescue => e
|
|
66
|
-
STDERR.puts "ERROR: context_manager clear_context error #{e.message}"
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Creates a checkpoint of the current context with an optional name.
|
|
71
|
-
#
|
|
72
|
-
# @param name [String, nil] The name of the checkpoint. If nil, uses an incrementing integer.
|
|
73
|
-
# @return [String] The name of the created checkpoint.
|
|
74
|
-
def create_checkpoint(name: nil)
|
|
75
|
-
if name.nil?
|
|
76
|
-
@checkpoint_counter += 1
|
|
77
|
-
name = @checkpoint_counter.to_s
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Store a deep copy of the current context and its position
|
|
81
|
-
@checkpoints[name] = {
|
|
82
|
-
context: @context.map(&:dup),
|
|
83
|
-
position: @context.size
|
|
84
|
-
}
|
|
85
|
-
@last_checkpoint_name = name
|
|
86
|
-
name
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Restores the context to a previously saved checkpoint.
|
|
90
|
-
#
|
|
91
|
-
# @param name [String, nil] The name of the checkpoint to restore. If nil, uses the last checkpoint.
|
|
92
|
-
# @return [Boolean] True if restore was successful, false otherwise.
|
|
93
|
-
def restore_checkpoint(name: nil)
|
|
94
|
-
name = @last_checkpoint_name if name.nil?
|
|
95
|
-
|
|
96
|
-
return false if name.nil? || !@checkpoints.key?(name)
|
|
97
|
-
|
|
98
|
-
# Restore the context from the checkpoint
|
|
99
|
-
checkpoint_data = @checkpoints[name]
|
|
100
|
-
@context = checkpoint_data[:context].map(&:dup)
|
|
101
|
-
true
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Returns the list of available checkpoint names.
|
|
105
|
-
#
|
|
106
|
-
# @return [Array<String>] The names of all checkpoints.
|
|
107
|
-
def checkpoint_names
|
|
108
|
-
@checkpoints.keys
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Returns checkpoint information mapped to context positions.
|
|
112
|
-
#
|
|
113
|
-
# @return [Hash<Integer, Array<String>>] Position to checkpoint names mapping.
|
|
114
|
-
def checkpoint_positions
|
|
115
|
-
positions = {}
|
|
116
|
-
@checkpoints.each do |name, data|
|
|
117
|
-
position = data[:position]
|
|
118
|
-
positions[position] ||= []
|
|
119
|
-
positions[position] << name
|
|
120
|
-
end
|
|
121
|
-
positions
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
private
|
|
125
|
-
|
|
126
|
-
# Adds or replaces the system prompt at the beginning of the context.
|
|
127
|
-
def add_system_prompt(system_prompt)
|
|
128
|
-
# Remove existing system prompt if present
|
|
129
|
-
@context.shift if !@context.empty? && @context.first[:role] == 'system'
|
|
130
|
-
# Add the new system prompt at the beginning
|
|
131
|
-
@context.unshift({ role: 'system', content: system_prompt })
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
end
|
data/mcp_servers/imcp.json
DELETED
data/mcp_servers/launcher.json
DELETED