aia 0.9.11 → 0.9.12
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 +66 -2
- data/README.md +133 -4
- data/docs/advanced-prompting.md +721 -0
- data/docs/cli-reference.md +582 -0
- data/docs/configuration.md +347 -0
- data/docs/contributing.md +332 -0
- data/docs/directives-reference.md +490 -0
- data/docs/examples/index.md +277 -0
- data/docs/examples/mcp/index.md +479 -0
- data/docs/examples/prompts/analysis/index.md +78 -0
- data/docs/examples/prompts/automation/index.md +108 -0
- data/docs/examples/prompts/development/index.md +125 -0
- data/docs/examples/prompts/index.md +333 -0
- data/docs/examples/prompts/learning/index.md +127 -0
- data/docs/examples/prompts/writing/index.md +62 -0
- data/docs/examples/tools/index.md +292 -0
- data/docs/faq.md +414 -0
- data/docs/guides/available-models.md +366 -0
- data/docs/guides/basic-usage.md +477 -0
- data/docs/guides/chat.md +474 -0
- data/docs/guides/executable-prompts.md +417 -0
- data/docs/guides/first-prompt.md +454 -0
- data/docs/guides/getting-started.md +455 -0
- data/docs/guides/image-generation.md +507 -0
- data/docs/guides/index.md +46 -0
- data/docs/guides/models.md +507 -0
- data/docs/guides/tools.md +856 -0
- data/docs/index.md +173 -0
- data/docs/installation.md +238 -0
- data/docs/mcp-integration.md +612 -0
- data/docs/prompt_management.md +579 -0
- data/docs/security.md +629 -0
- data/docs/tools-and-mcp-examples.md +1186 -0
- data/docs/workflows-and-pipelines.md +563 -0
- data/examples/tools/mcp/github_mcp_server.json +11 -0
- data/examples/tools/mcp/imcp.json +7 -0
- data/lib/aia/chat_processor_service.rb +19 -3
- data/lib/aia/config/base.rb +224 -0
- data/lib/aia/config/cli_parser.rb +409 -0
- data/lib/aia/config/defaults.rb +88 -0
- data/lib/aia/config/file_loader.rb +131 -0
- data/lib/aia/config/validator.rb +184 -0
- data/lib/aia/config.rb +10 -860
- data/lib/aia/directive_processor.rb +27 -372
- data/lib/aia/directives/configuration.rb +114 -0
- data/lib/aia/directives/execution.rb +37 -0
- data/lib/aia/directives/models.rb +178 -0
- data/lib/aia/directives/registry.rb +120 -0
- data/lib/aia/directives/utility.rb +70 -0
- data/lib/aia/directives/web_and_file.rb +71 -0
- data/lib/aia/prompt_handler.rb +23 -3
- data/lib/aia/ruby_llm_adapter.rb +307 -128
- data/lib/aia/session.rb +27 -14
- data/lib/aia/utility.rb +12 -8
- data/lib/aia.rb +11 -2
- data/lib/extensions/ruby_llm/.irbrc +56 -0
- data/mkdocs.yml +165 -0
- metadata +77 -20
- /data/{images → docs/assets/images}/aia.png +0 -0
@@ -0,0 +1,224 @@
|
|
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
|
+
|
144
|
+
if config.dump_file
|
145
|
+
dump_config(config, config.dump_file)
|
146
|
+
end
|
147
|
+
|
148
|
+
config
|
149
|
+
end
|
150
|
+
|
151
|
+
def load_libraries(config)
|
152
|
+
return if config.require_libs.empty?
|
153
|
+
|
154
|
+
exit_on_error = false
|
155
|
+
|
156
|
+
config.require_libs.each do |library|
|
157
|
+
begin
|
158
|
+
require(library)
|
159
|
+
rescue => e
|
160
|
+
STDERR.puts "Error loading library '#{library}' #{e.message}"
|
161
|
+
exit_on_error = true
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
exit(1) if exit_on_error
|
166
|
+
|
167
|
+
config
|
168
|
+
end
|
169
|
+
|
170
|
+
def load_tools(config)
|
171
|
+
return if config.tool_paths.empty?
|
172
|
+
|
173
|
+
require_all_tools(config)
|
174
|
+
|
175
|
+
config
|
176
|
+
end
|
177
|
+
|
178
|
+
def require_all_tools(config)
|
179
|
+
exit_on_error = false
|
180
|
+
|
181
|
+
config.tool_paths.each do |tool_path|
|
182
|
+
begin
|
183
|
+
# expands path based on PWD
|
184
|
+
absolute_tool_path = File.expand_path(tool_path)
|
185
|
+
require(absolute_tool_path)
|
186
|
+
rescue => e
|
187
|
+
STDERR.puts "Error loading tool '#{tool_path}' #{e.message}"
|
188
|
+
exit_on_error = true
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
exit(1) if exit_on_error
|
193
|
+
end
|
194
|
+
|
195
|
+
# envar values are always String object so need other config
|
196
|
+
# layers to know the prompter type for each key's value
|
197
|
+
def envar_options(default, cli_config)
|
198
|
+
config = OpenStruct.merge(default, cli_config)
|
199
|
+
envars = ENV.keys.select { |key, _| key.start_with?('AIA_') }
|
200
|
+
envars.each do |envar|
|
201
|
+
key = envar.sub(/^AIA_/, '').downcase.to_sym
|
202
|
+
value = ENV[envar]
|
203
|
+
|
204
|
+
value = case config[key]
|
205
|
+
when TrueClass, FalseClass
|
206
|
+
value.downcase == 'true'
|
207
|
+
when Integer
|
208
|
+
value.to_i
|
209
|
+
when Float
|
210
|
+
value.to_f
|
211
|
+
when Array
|
212
|
+
value.split(',').map(&:strip)
|
213
|
+
else
|
214
|
+
value # defaults to String
|
215
|
+
end
|
216
|
+
config[key] = value
|
217
|
+
end
|
218
|
+
|
219
|
+
config
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,409 @@
|
|
1
|
+
# lib/aia/config/cli_parser.rb
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module AIA
|
7
|
+
module ConfigModules
|
8
|
+
module CLIParser
|
9
|
+
class << self
|
10
|
+
def cli_options
|
11
|
+
config = OpenStruct.new
|
12
|
+
|
13
|
+
begin
|
14
|
+
opt_parser = create_option_parser(config)
|
15
|
+
opt_parser.parse!
|
16
|
+
rescue => e
|
17
|
+
STDERR.puts "ERROR: #{e.message}"
|
18
|
+
STDERR.puts " use --help for usage report"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
parse_remaining_arguments(opt_parser, config)
|
23
|
+
config
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_option_parser(config)
|
27
|
+
OptionParser.new do |opts|
|
28
|
+
setup_banner(opts)
|
29
|
+
setup_mode_options(opts, config)
|
30
|
+
setup_adapter_options(opts, config)
|
31
|
+
setup_model_options(opts, config)
|
32
|
+
setup_file_options(opts, config)
|
33
|
+
setup_prompt_options(opts, config)
|
34
|
+
setup_ai_parameters(opts, config)
|
35
|
+
setup_audio_image_options(opts, config)
|
36
|
+
setup_tool_options(opts, config)
|
37
|
+
setup_utility_options(opts, config)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def setup_banner(opts)
|
42
|
+
opts.banner = "Usage: aia [options] [PROMPT_ID] [CONTEXT_FILE]*\n" +
|
43
|
+
" aia --chat [PROMPT_ID] [CONTEXT_FILE]*\n" +
|
44
|
+
" aia --chat [CONTEXT_FILE]*"
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_mode_options(opts, config)
|
48
|
+
opts.on("--chat", "Begin a chat session with the LLM after processing all prompts in the pipeline.") do
|
49
|
+
config.chat = true
|
50
|
+
puts "Debug: Setting chat mode to true" if config.debug
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("-f", "--fuzzy", "Use fuzzy matching for prompt search") do
|
54
|
+
unless system("which fzf > /dev/null 2>&1")
|
55
|
+
STDERR.puts "Error: 'fzf' is not installed. Please install 'fzf' to use the --fuzzy option."
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
config.fuzzy = true
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.on("--terse", "Adds a special instruction to the prompt asking the AI to keep responses short and to the point") do
|
62
|
+
config.terse = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup_adapter_options(opts, config)
|
67
|
+
opts.on("--adapter ADAPTER", "Interface that adapts AIA to the LLM") do |adapter|
|
68
|
+
adapter.downcase!
|
69
|
+
valid_adapters = %w[ ruby_llm ] # NOTE: Add additional adapters here when needed
|
70
|
+
if valid_adapters.include? adapter
|
71
|
+
config.adapter = adapter
|
72
|
+
else
|
73
|
+
STDERR.puts "ERROR: Invalid adapter #{adapter} must be one of these: #{valid_adapters.join(', ')}"
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on('--available_models [QUERY]', 'List (then exit) available models that match the optional query - a comma separated list of AND components like: openai,mini') do |query|
|
79
|
+
list_available_models(query)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def setup_model_options(opts, config)
|
84
|
+
opts.on("-m MODEL", "--model MODEL", "Name of the LLM model(s) to use (comma-separated for multiple models)") do |model|
|
85
|
+
config.model = model.split(',').map(&:strip)
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("--[no-]consensus", "Enable/disable consensus mode for multi-model responses (default: show individual responses)") do |consensus|
|
89
|
+
config.consensus = consensus
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on("--sm", "--speech_model MODEL", "Speech model to use") do |model|
|
93
|
+
config.speech_model = model
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on("--tm", "--transcription_model MODEL", "Transcription model to use") do |model|
|
97
|
+
config.transcription_model = model
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def setup_file_options(opts, config)
|
102
|
+
opts.on("-c", "--config_file FILE", "Load config file") do |file|
|
103
|
+
FileLoader.load_config_file(file, config)
|
104
|
+
end
|
105
|
+
|
106
|
+
opts.on("-o", "--[no-]out_file [FILE]", "Output file (default: temp.md)") do |file|
|
107
|
+
if file == false # --no-out_file was used
|
108
|
+
config.out_file = nil
|
109
|
+
elsif file.nil? # No argument provided
|
110
|
+
config.out_file = 'temp.md'
|
111
|
+
else # File name provided
|
112
|
+
config.out_file = File.expand_path(file, Dir.pwd)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
opts.on("-a", "--[no-]append", "Append to output file instead of overwriting") do |append|
|
117
|
+
config.append = append
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.on("-l", "--[no-]log_file [FILE]", "Log file") do |file|
|
121
|
+
config.log_file = file
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on("--md", "--[no-]markdown", "Format with Markdown") do |md|
|
125
|
+
config.markdown = md
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def setup_prompt_options(opts, config)
|
130
|
+
opts.on("--prompts_dir DIR", "Directory containing prompt files") do |dir|
|
131
|
+
config.prompts_dir = dir
|
132
|
+
end
|
133
|
+
|
134
|
+
opts.on("--roles_prefix PREFIX", "Subdirectory name for role files (default: roles)") do |prefix|
|
135
|
+
config.roles_prefix = prefix
|
136
|
+
end
|
137
|
+
|
138
|
+
opts.on("-r", "--role ROLE_ID", "Role ID to prepend to prompt") do |role|
|
139
|
+
config.role = role
|
140
|
+
end
|
141
|
+
|
142
|
+
opts.on("-n", "--next PROMPT_ID", "Next prompt to process") do |next_prompt|
|
143
|
+
config.pipeline ||= []
|
144
|
+
config.pipeline << next_prompt
|
145
|
+
end
|
146
|
+
|
147
|
+
opts.on("-p PROMPTS", "--pipeline PROMPTS", "Pipeline of comma-seperated prompt IDs to process") do |pipeline|
|
148
|
+
config.pipeline ||= []
|
149
|
+
config.pipeline += pipeline.split(',').map(&:strip)
|
150
|
+
end
|
151
|
+
|
152
|
+
opts.on("-x", "--[no-]exec", "Used to designate an executable prompt file") do |value|
|
153
|
+
config.executable_prompt = value
|
154
|
+
end
|
155
|
+
|
156
|
+
opts.on("--system_prompt PROMPT_ID", "System prompt ID to use for chat sessions") do |prompt_id|
|
157
|
+
config.system_prompt = prompt_id
|
158
|
+
end
|
159
|
+
|
160
|
+
opts.on('--regex pattern', 'Regex pattern to extract parameters from prompt text') do |pattern|
|
161
|
+
config.parameter_regex = pattern
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def setup_ai_parameters(opts, config)
|
166
|
+
opts.on("-t", "--temperature TEMP", Float, "Temperature for text generation") do |temp|
|
167
|
+
config.temperature = temp
|
168
|
+
end
|
169
|
+
|
170
|
+
opts.on("--max_tokens TOKENS", Integer, "Maximum tokens for text generation") do |tokens|
|
171
|
+
config.max_tokens = tokens
|
172
|
+
end
|
173
|
+
|
174
|
+
opts.on("--top_p VALUE", Float, "Top-p sampling value") do |value|
|
175
|
+
config.top_p = value
|
176
|
+
end
|
177
|
+
|
178
|
+
opts.on("--frequency_penalty VALUE", Float, "Frequency penalty") do |value|
|
179
|
+
config.frequency_penalty = value
|
180
|
+
end
|
181
|
+
|
182
|
+
opts.on("--presence_penalty VALUE", Float, "Presence penalty") do |value|
|
183
|
+
config.presence_penalty = value
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def setup_audio_image_options(opts, config)
|
188
|
+
opts.on("--speak", "Simple implementation. Uses the speech model to convert text to audio, then plays the audio. Fun with --chat. Supports configuration of speech model and voice.") do
|
189
|
+
config.speak = true
|
190
|
+
end
|
191
|
+
|
192
|
+
opts.on("--voice VOICE", "Voice to use for speech") do |voice|
|
193
|
+
config.voice = voice
|
194
|
+
end
|
195
|
+
|
196
|
+
opts.on("--is", "--image_size SIZE", "Image size for image generation") do |size|
|
197
|
+
config.image_size = size
|
198
|
+
end
|
199
|
+
|
200
|
+
opts.on("--iq", "--image_quality QUALITY", "Image quality for image generation") do |quality|
|
201
|
+
config.image_quality = quality
|
202
|
+
end
|
203
|
+
|
204
|
+
opts.on("--style", "--image_style STYLE", "Style for image generation") do |style|
|
205
|
+
config.image_style = style
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def setup_tool_options(opts, config)
|
210
|
+
opts.on("--rq LIBS", "--require LIBS", "Ruby libraries to require for Ruby directive") do |libs|
|
211
|
+
config.require_libs ||= []
|
212
|
+
config.require_libs += libs.split(',')
|
213
|
+
end
|
214
|
+
|
215
|
+
opts.on("--tools PATH_LIST", "Add a tool(s)") do |a_path_list|
|
216
|
+
process_tools_option(a_path_list, config)
|
217
|
+
end
|
218
|
+
|
219
|
+
opts.on("--at", "--allowed_tools TOOLS_LIST", "Allow only these tools to be used") do |tools_list|
|
220
|
+
process_allowed_tools_option(tools_list, config)
|
221
|
+
end
|
222
|
+
|
223
|
+
opts.on("--rt", "--rejected_tools TOOLS_LIST", "Reject these tools") do |tools_list|
|
224
|
+
process_rejected_tools_option(tools_list, config)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def setup_utility_options(opts, config)
|
229
|
+
opts.on("-d", "--debug", "Enable debug output") do
|
230
|
+
config.debug = $DEBUG_ME = true
|
231
|
+
end
|
232
|
+
|
233
|
+
opts.on("--no-debug", "Disable debug output") do
|
234
|
+
config.debug = $DEBUG_ME = false
|
235
|
+
end
|
236
|
+
|
237
|
+
opts.on("-v", "--[no-]verbose", "Be verbose") do |value|
|
238
|
+
config.verbose = value
|
239
|
+
end
|
240
|
+
|
241
|
+
opts.on("--refresh DAYS", Integer, "Refresh models database interval in days") do |days|
|
242
|
+
config.refresh = days || 0
|
243
|
+
end
|
244
|
+
|
245
|
+
opts.on("--dump FILE", "Dump config to file") do |file|
|
246
|
+
config.dump_file = file
|
247
|
+
end
|
248
|
+
|
249
|
+
opts.on("--completion SHELL", "Show completion script for bash|zsh|fish - default is nil") do |shell|
|
250
|
+
config.completion = shell
|
251
|
+
end
|
252
|
+
|
253
|
+
opts.on("--version", "Show version") do
|
254
|
+
puts AIA::VERSION
|
255
|
+
exit
|
256
|
+
end
|
257
|
+
|
258
|
+
opts.on("-h", "--help", "Prints this help") do
|
259
|
+
puts <<~HELP
|
260
|
+
|
261
|
+
AIA your AI Assistant
|
262
|
+
- designed for generative AI workflows,
|
263
|
+
- effortlessly manage AI prompts,
|
264
|
+
- integrate seamlessly with shell and embedded Ruby (ERB),
|
265
|
+
- run batch processes,
|
266
|
+
- engage in interactive chats,
|
267
|
+
- with user defined directives, tools and MCP clients.
|
268
|
+
|
269
|
+
HELP
|
270
|
+
|
271
|
+
puts opts
|
272
|
+
|
273
|
+
puts <<~EXTRA
|
274
|
+
|
275
|
+
Explore Further:
|
276
|
+
- AIA Report an Issue: https://github.com/MadBomber/aia/issues
|
277
|
+
- AIA Documentation: https://github.com/MadBomber/aia/blob/main/README.md
|
278
|
+
- AIA GitHub Repository: https://github.com/MadBomber/aia
|
279
|
+
- PromptManager Docs: https://github.com/MadBomber/prompt_manager/blob/main/README.md
|
280
|
+
- ERB Documentation: https://rubyapi.org/o/erb
|
281
|
+
- RubyLLM Tool Docs: https://rubyllm.com/guides/tools
|
282
|
+
- MCP Client Docs: https://github.com/patvice/ruby_llm-mcp/blob/main/README.md
|
283
|
+
|
284
|
+
EXTRA
|
285
|
+
|
286
|
+
exit
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def list_available_models(query)
|
291
|
+
# SMELL: mostly duplications the code in the vailable_models directive
|
292
|
+
# assumes that the adapter is for the ruby_llm gem
|
293
|
+
# should this be moved to the Utilities class as a common method?
|
294
|
+
|
295
|
+
if query.nil?
|
296
|
+
query = []
|
297
|
+
else
|
298
|
+
query = query.split(',')
|
299
|
+
end
|
300
|
+
|
301
|
+
header = "\nAvailable LLMs"
|
302
|
+
header += " for #{query.join(' and ')}" if query
|
303
|
+
|
304
|
+
puts header + ':'
|
305
|
+
puts
|
306
|
+
|
307
|
+
q1 = query.select{|q| q.include?('_to_')}.map{|q| ':'==q[0] ? q[1...] : q}
|
308
|
+
q2 = query.reject{|q| q.include?('_to_')}
|
309
|
+
|
310
|
+
counter = 0
|
311
|
+
|
312
|
+
RubyLLM.models.all.each do |llm|
|
313
|
+
inputs = llm.modalities.input.join(',')
|
314
|
+
outputs = llm.modalities.output.join(',')
|
315
|
+
entry = "- #{llm.id} (#{llm.provider}) #{inputs} to #{outputs}"
|
316
|
+
|
317
|
+
if query.nil? || query.empty?
|
318
|
+
counter += 1
|
319
|
+
puts entry
|
320
|
+
next
|
321
|
+
end
|
322
|
+
|
323
|
+
show_it = true
|
324
|
+
q1.each{|q| show_it &&= llm.modalities.send("#{q}?")}
|
325
|
+
q2.each{|q| show_it &&= entry.include?(q)}
|
326
|
+
|
327
|
+
if show_it
|
328
|
+
counter += 1
|
329
|
+
puts entry
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
puts if counter > 0
|
334
|
+
puts "#{counter} LLMs matching your query"
|
335
|
+
puts
|
336
|
+
|
337
|
+
exit
|
338
|
+
end
|
339
|
+
|
340
|
+
def parse_remaining_arguments(opt_parser, config)
|
341
|
+
args = ARGV.dup
|
342
|
+
|
343
|
+
# Parse the command line arguments
|
344
|
+
begin
|
345
|
+
config.remaining_args = opt_parser.parse(args)
|
346
|
+
rescue OptionParser::InvalidOption => e
|
347
|
+
puts e.message
|
348
|
+
puts opt_parser
|
349
|
+
exit 1
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def process_tools_option(a_path_list, config)
|
354
|
+
config.tool_paths ||= []
|
355
|
+
|
356
|
+
if a_path_list.empty?
|
357
|
+
STDERR.puts "No list of paths for --tools option"
|
358
|
+
exit 1
|
359
|
+
else
|
360
|
+
paths = a_path_list.split(',').map(&:strip).uniq
|
361
|
+
end
|
362
|
+
|
363
|
+
paths.each do |a_path|
|
364
|
+
if File.exist?(a_path)
|
365
|
+
if File.file?(a_path)
|
366
|
+
if '.rb' == File.extname(a_path)
|
367
|
+
config.tool_paths << a_path
|
368
|
+
else
|
369
|
+
STDERR.puts "file should have *.rb extension: #{a_path}"
|
370
|
+
exit 1
|
371
|
+
end
|
372
|
+
elsif File.directory?(a_path)
|
373
|
+
rb_files = Dir.glob(File.join(a_path, '*.rb'))
|
374
|
+
config.tool_paths += rb_files
|
375
|
+
end
|
376
|
+
else
|
377
|
+
STDERR.puts "file/dir path is not valid: #{a_path}"
|
378
|
+
exit 1
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
config.tool_paths.uniq!
|
383
|
+
end
|
384
|
+
|
385
|
+
def process_allowed_tools_option(tools_list, config)
|
386
|
+
config.allowed_tools ||= []
|
387
|
+
if tools_list.empty?
|
388
|
+
STDERR.puts "No list of tool names provided for --allowed_tools option"
|
389
|
+
exit 1
|
390
|
+
else
|
391
|
+
config.allowed_tools += tools_list.split(',').map(&:strip)
|
392
|
+
config.allowed_tools.uniq!
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def process_rejected_tools_option(tools_list, config)
|
397
|
+
config.rejected_tools ||= []
|
398
|
+
if tools_list.empty?
|
399
|
+
STDERR.puts "No list of tool names provided for --rejected_tools option"
|
400
|
+
exit 1
|
401
|
+
else
|
402
|
+
config.rejected_tools += tools_list.split(',').map(&:strip)
|
403
|
+
config.rejected_tools.uniq!
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|