aia 0.5.18 → 0.8.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 +4 -4
- data/.envrc +1 -0
- data/.version +1 -1
- data/CHANGELOG.md +39 -5
- data/README.md +388 -219
- data/Rakefile +16 -5
- data/_notes.txt +231 -0
- data/bin/aia +3 -2
- data/examples/README.md +140 -0
- data/examples/headlines +21 -0
- data/lib/aia/ai_client_adapter.rb +210 -0
- data/lib/aia/chat_processor_service.rb +120 -0
- data/lib/aia/config.rb +473 -4
- data/lib/aia/context_manager.rb +58 -0
- data/lib/aia/directive_processor.rb +267 -0
- data/lib/aia/{tools/fzf.rb → fzf.rb} +9 -17
- data/lib/aia/history_manager.rb +85 -0
- data/lib/aia/prompt_handler.rb +178 -0
- data/lib/aia/session.rb +215 -0
- data/lib/aia/shell_command_executor.rb +109 -0
- data/lib/aia/ui_presenter.rb +110 -0
- data/lib/aia/utility.rb +24 -0
- data/lib/aia/version.rb +9 -6
- data/lib/aia.rb +57 -61
- data/lib/extensions/openstruct_merge.rb +44 -0
- metadata +29 -43
- data/LICENSE.txt +0 -21
- data/doc/aia_and_pre_compositional_prompts.md +0 -474
- data/lib/aia/clause.rb +0 -7
- data/lib/aia/cli.rb +0 -452
- data/lib/aia/directives.rb +0 -142
- data/lib/aia/dynamic_content.rb +0 -26
- data/lib/aia/logging.rb +0 -62
- data/lib/aia/main.rb +0 -265
- data/lib/aia/prompt.rb +0 -275
- data/lib/aia/tools/ai_client_backend.rb +0 -92
- data/lib/aia/tools/backend_common.rb +0 -58
- data/lib/aia/tools/client.rb +0 -197
- data/lib/aia/tools/editor.rb +0 -52
- data/lib/aia/tools/glow.rb +0 -90
- data/lib/aia/tools/llm.rb +0 -77
- data/lib/aia/tools/mods.rb +0 -100
- data/lib/aia/tools/sgpt.rb +0 -79
- data/lib/aia/tools/subl.rb +0 -68
- data/lib/aia/tools/vim.rb +0 -93
- data/lib/aia/tools.rb +0 -88
- data/lib/aia/user_query.rb +0 -21
- data/lib/core_ext/string_wrap.rb +0 -73
- data/lib/core_ext/tty-spinner_log.rb +0 -25
- data/man/aia.1 +0 -272
- data/man/aia.1.md +0 -236
data/lib/aia/main.rb
DELETED
@@ -1,265 +0,0 @@
|
|
1
|
-
# lib/aia/main.rb
|
2
|
-
|
3
|
-
module AIA ; end
|
4
|
-
|
5
|
-
require_relative 'config'
|
6
|
-
require_relative 'cli'
|
7
|
-
require_relative 'directives'
|
8
|
-
require_relative 'dynamic_content'
|
9
|
-
require_relative 'prompt'
|
10
|
-
require_relative 'logging'
|
11
|
-
require_relative 'tools'
|
12
|
-
require_relative 'user_query'
|
13
|
-
|
14
|
-
# Everything is being handled within the context
|
15
|
-
# of a single class.
|
16
|
-
|
17
|
-
class AIA::Main
|
18
|
-
SPINNER_FORMAT = :bouncing_ball
|
19
|
-
|
20
|
-
include AIA::DynamicContent
|
21
|
-
include AIA::UserQuery
|
22
|
-
|
23
|
-
attr_accessor :logger, :tools, :backend, :directive_output, :piped_content
|
24
|
-
|
25
|
-
attr_reader :spinner
|
26
|
-
|
27
|
-
def initialize(args= ARGV)
|
28
|
-
unless $stdin.tty?
|
29
|
-
@piped_content = $stdin.readlines.join.chomp
|
30
|
-
$stdin.reopen("/dev/tty")
|
31
|
-
end
|
32
|
-
|
33
|
-
@directive_output = ""
|
34
|
-
AIA::Tools.load_tools
|
35
|
-
|
36
|
-
AIA.client = AIA::Client.new
|
37
|
-
|
38
|
-
AIA::Cli.new(args)
|
39
|
-
|
40
|
-
if AIA.config.debug?
|
41
|
-
debug_me('== CONFIG AFTER CLI =='){[
|
42
|
-
"AIA.config"
|
43
|
-
]}
|
44
|
-
end
|
45
|
-
|
46
|
-
@spinner = TTY::Spinner.new(":spinner :title", format: SPINNER_FORMAT)
|
47
|
-
spinner.update(title: "composing response ... ")
|
48
|
-
|
49
|
-
@logger = AIA::Logging.new(AIA.config.log_file)
|
50
|
-
|
51
|
-
@logger.info(AIA.config) if AIA.config.debug? || AIA.config.verbose?
|
52
|
-
|
53
|
-
|
54
|
-
@directives_processor = AIA::Directives.new
|
55
|
-
|
56
|
-
@prompt = AIA::Prompt.new.prompt
|
57
|
-
|
58
|
-
@prompt.text += piped_content unless piped_content.nil?
|
59
|
-
|
60
|
-
# TODO: still should verify that the tools are ion the $PATH
|
61
|
-
# tools.class.verify_tools
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
# Function to setup the Reline history with a maximum depth
|
66
|
-
def setup_reline_history(max_history_size=5)
|
67
|
-
Reline::HISTORY.clear
|
68
|
-
# Reline::HISTORY.max_size = max_history_size
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# This will be recursive with the new options
|
73
|
-
# --next and --pipeline
|
74
|
-
def call
|
75
|
-
@directive_output = @directives_processor.execute_my_directives
|
76
|
-
|
77
|
-
if AIA.config.chat?
|
78
|
-
AIA.config.out_file = STDOUT
|
79
|
-
AIA.config.extra = "--quiet" if 'mods' == AIA.config.backend
|
80
|
-
end
|
81
|
-
|
82
|
-
# TODO: the context_files left in the @arguments array
|
83
|
-
# should be verified BEFORE asking the user for a
|
84
|
-
# prompt keyword or process the prompt. Do not
|
85
|
-
# want invalid files to make it this far.
|
86
|
-
|
87
|
-
found = AIA::Tools
|
88
|
-
.search_for(
|
89
|
-
name: AIA.config.backend,
|
90
|
-
role: :backend
|
91
|
-
)
|
92
|
-
|
93
|
-
if found.empty?
|
94
|
-
abort "There are no :backend tools named #{AIA.config.backend}"
|
95
|
-
end
|
96
|
-
|
97
|
-
if found.size > 1
|
98
|
-
abort "There are #{found.size} :backend tools with the name #{AIAA.config.backend}"
|
99
|
-
end
|
100
|
-
|
101
|
-
backend_klass = found.first.klass
|
102
|
-
|
103
|
-
abort "backend not found: #{AIA.config.backend}" if backend_klass.nil?
|
104
|
-
|
105
|
-
the_prompt = @prompt.to_s
|
106
|
-
|
107
|
-
the_prompt.prepend(@directive_output + "\n") unless @directive_output.nil? || @directive_output.empty?
|
108
|
-
|
109
|
-
if AIA.config.terse?
|
110
|
-
the_prompt.prepend "Be terse in your response. "
|
111
|
-
end
|
112
|
-
|
113
|
-
@backend = backend_klass.new(
|
114
|
-
text: the_prompt,
|
115
|
-
files: AIA.config.arguments # FIXME: want validated context files
|
116
|
-
)
|
117
|
-
|
118
|
-
result = get_and_display_result(the_prompt)
|
119
|
-
|
120
|
-
AIA.speak(result) if AIA.config.speak?
|
121
|
-
|
122
|
-
logger.prompt_result(@prompt, result)
|
123
|
-
|
124
|
-
if AIA.config.chat?
|
125
|
-
setup_reline_history
|
126
|
-
AIA.speak result
|
127
|
-
lets_chat
|
128
|
-
end
|
129
|
-
|
130
|
-
return if AIA.config.next.empty? && AIA.config.pipeline.empty?
|
131
|
-
|
132
|
-
keep_going(result) unless AIA.config.pipeline.empty?
|
133
|
-
end
|
134
|
-
|
135
|
-
|
136
|
-
# The AIA.config.pipeline is NOT empty, so feed this result
|
137
|
-
# into the next prompt within the pipeline.
|
138
|
-
#
|
139
|
-
def keep_going(result)
|
140
|
-
temp_file = Tempfile.new('aia_pipeline')
|
141
|
-
temp_file.write(result)
|
142
|
-
temp_file.close
|
143
|
-
|
144
|
-
AIA.config.directives = []
|
145
|
-
AIA.config.model = ""
|
146
|
-
AIA.config.arguments = [
|
147
|
-
AIA.config.pipeline.shift,
|
148
|
-
temp_file.path,
|
149
|
-
# TODO: additional arguments from the pipeline
|
150
|
-
]
|
151
|
-
AIA.config.next = ""
|
152
|
-
|
153
|
-
AIA.config.files = [temp_file.path]
|
154
|
-
|
155
|
-
@prompt = AIA::Prompt.new.prompt
|
156
|
-
call # Recurse! until the AIA.config.pipeline is emplty
|
157
|
-
puts
|
158
|
-
ensure
|
159
|
-
temp_file.unlink
|
160
|
-
end
|
161
|
-
|
162
|
-
|
163
|
-
def get_and_display_result(the_prompt_text)
|
164
|
-
spinner.auto_spin if AIA.config.verbose?
|
165
|
-
|
166
|
-
backend.text = the_prompt_text
|
167
|
-
result = backend.run
|
168
|
-
|
169
|
-
if AIA.config.verbose?
|
170
|
-
spinner.success "Done."
|
171
|
-
end
|
172
|
-
|
173
|
-
AIA.config.out_file.write "\nResponse:\n"
|
174
|
-
|
175
|
-
if STDOUT == AIA.config.out_file
|
176
|
-
if AIA.config.render?
|
177
|
-
AIA::Glow.new(content: result).run
|
178
|
-
else
|
179
|
-
result = result.wrap(indent: 2)
|
180
|
-
AIA.config.out_file.write result
|
181
|
-
end
|
182
|
-
else
|
183
|
-
AIA.config.out_file.write result
|
184
|
-
if AIA.config.render?
|
185
|
-
AIA::Glow.new(file_path: AIA.config.out_file).run
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
result
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
def log_the_follow_up(the_prompt_text, result)
|
194
|
-
logger.info "Follow Up:\n#{the_prompt_text}"
|
195
|
-
logger.info "Response:\n#{result}"
|
196
|
-
end
|
197
|
-
|
198
|
-
|
199
|
-
def add_continue_option
|
200
|
-
if 'mods' == AIA.config.backend
|
201
|
-
continue_option = " -C"
|
202
|
-
AIA.config.extra += continue_option unless AIA.config.extra.include?(continue_option)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
def insert_terse_phrase(a_string)
|
208
|
-
if AIA.config.terse?
|
209
|
-
a_string.prepend "Be terse in your response. "
|
210
|
-
end
|
211
|
-
|
212
|
-
a_string
|
213
|
-
end
|
214
|
-
|
215
|
-
|
216
|
-
def handle_directives(the_prompt_text)
|
217
|
-
signal = PromptManager::Prompt::DIRECTIVE_SIGNAL
|
218
|
-
result = the_prompt_text.start_with?(signal)
|
219
|
-
|
220
|
-
if result
|
221
|
-
parts = the_prompt_text[signal.size..].split(' ')
|
222
|
-
directive = parts.shift
|
223
|
-
parameters = parts.join(' ')
|
224
|
-
AIA.config.directives << [directive, parameters]
|
225
|
-
|
226
|
-
@directive_output = @directives_processor.execute_my_directives
|
227
|
-
else
|
228
|
-
@directive_output = ""
|
229
|
-
end
|
230
|
-
|
231
|
-
result
|
232
|
-
end
|
233
|
-
|
234
|
-
|
235
|
-
def lets_chat
|
236
|
-
add_continue_option
|
237
|
-
|
238
|
-
the_prompt_text = ask_question_with_reline("\nFollow Up: ")
|
239
|
-
|
240
|
-
until the_prompt_text.empty?
|
241
|
-
the_prompt_text = render_erb(the_prompt_text) if AIA.config.erb?
|
242
|
-
the_prompt_text = render_env(the_prompt_text) if AIA.config.shell?
|
243
|
-
|
244
|
-
if handle_directives(the_prompt_text)
|
245
|
-
if @directive_output.nil? || @directive_output.empty?
|
246
|
-
# Do nothing
|
247
|
-
else
|
248
|
-
the_prompt_text = @directive_output
|
249
|
-
the_prompt_text = render_erb(the_prompt_text) if AIA.config.erb?
|
250
|
-
the_prompt_text = render_env(the_prompt_text) if AIA.config.shell?
|
251
|
-
result = get_and_display_result(the_prompt_text)
|
252
|
-
log_the_follow_up(the_prompt_text, result)
|
253
|
-
AIA.speak result
|
254
|
-
end
|
255
|
-
else
|
256
|
-
the_prompt_text = insert_terse_phrase(the_prompt_text)
|
257
|
-
result = get_and_display_result(the_prompt_text)
|
258
|
-
log_the_follow_up(the_prompt_text, result)
|
259
|
-
AIA.speak result
|
260
|
-
end
|
261
|
-
|
262
|
-
the_prompt_text = ask_question_with_reline("\nFollow Up: ")
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
data/lib/aia/prompt.rb
DELETED
@@ -1,275 +0,0 @@
|
|
1
|
-
# lib/aia/prompt.rb
|
2
|
-
|
3
|
-
require 'reline'
|
4
|
-
|
5
|
-
require_relative 'dynamic_content'
|
6
|
-
require_relative 'user_query'
|
7
|
-
|
8
|
-
class AIA::Prompt
|
9
|
-
include AIA::DynamicContent
|
10
|
-
include AIA::UserQuery
|
11
|
-
|
12
|
-
#
|
13
|
-
# used when no prompt_id is provided but there
|
14
|
-
# are extra parameters that need to be passed
|
15
|
-
# to the backend. For example "aia -- --settings"
|
16
|
-
#
|
17
|
-
class Fake
|
18
|
-
def id = '_fake_'
|
19
|
-
def path = '_fake_'
|
20
|
-
def text = ''
|
21
|
-
def keywords = []
|
22
|
-
def directives = []
|
23
|
-
def to_s = ''
|
24
|
-
end
|
25
|
-
|
26
|
-
KW_HISTORY_MAX = 5
|
27
|
-
COMMENT_SIGNAL = '#'
|
28
|
-
DIRECTIVE_SIGNAL = "//"
|
29
|
-
|
30
|
-
attr_reader :prompt
|
31
|
-
|
32
|
-
# setting build: false supports unit testing.
|
33
|
-
def initialize(build: true)
|
34
|
-
if AIA.config.role.empty?
|
35
|
-
@role = nil
|
36
|
-
else
|
37
|
-
@role = (AIA.config.roles_dir + "#{AIA.config.role}.txt").read
|
38
|
-
end
|
39
|
-
|
40
|
-
get_prompt
|
41
|
-
|
42
|
-
@prompt_text_before_role = @prompt.text.dup
|
43
|
-
|
44
|
-
unless @role.nil?
|
45
|
-
@prompt.text.prepend @role
|
46
|
-
end
|
47
|
-
|
48
|
-
if build
|
49
|
-
@prompt.text = render_erb(@prompt.text) if AIA.config.erb?
|
50
|
-
@prompt.text = render_env(@prompt.text) if AIA.config.shell?
|
51
|
-
process_prompt
|
52
|
-
end
|
53
|
-
|
54
|
-
AIA.config.directives = @prompt.directives
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
# Fetch the first argument which should be the prompt id
|
59
|
-
def get_prompt
|
60
|
-
prompt_id = AIA.config.arguments.shift
|
61
|
-
|
62
|
-
unless prompt_id
|
63
|
-
if AIA.config.extra.empty?
|
64
|
-
abort("Please provide a prompt id")
|
65
|
-
else
|
66
|
-
@prompt = Fake.new
|
67
|
-
return
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
72
|
-
edit_prompt if AIA.config.edit?
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
# Check if a prompt with the given id already exists. If so, use it.
|
77
|
-
def existing_prompt?(prompt_id)
|
78
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
79
|
-
true
|
80
|
-
rescue ArgumentError
|
81
|
-
false
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
# Process the prompt's associated keywords and parameters
|
86
|
-
def process_prompt
|
87
|
-
unless @prompt.keywords.empty?
|
88
|
-
replace_keywords
|
89
|
-
@prompt.build
|
90
|
-
save(@prompt_text_before_role)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
|
-
def save(original_text)
|
96
|
-
temp_text = @prompt.text
|
97
|
-
@prompt.text = original_text
|
98
|
-
@prompt.save
|
99
|
-
@prompt.text = temp_text
|
100
|
-
end
|
101
|
-
|
102
|
-
|
103
|
-
def replace_keywords
|
104
|
-
puts
|
105
|
-
puts "ID: #{@prompt.id}"
|
106
|
-
|
107
|
-
show_prompt_without_comments
|
108
|
-
|
109
|
-
puts "\nPress up/down arrow to scroll through history."
|
110
|
-
puts "Type new input or edit the current input."
|
111
|
-
puts "Quit #{MY_NAME} with a CNTL-D or a CNTL-C"
|
112
|
-
puts
|
113
|
-
@prompt.keywords.each do |kw|
|
114
|
-
value = keyword_value(kw, @prompt.parameters[kw])
|
115
|
-
|
116
|
-
unless value.nil? || value.strip.empty?
|
117
|
-
value_inx = @prompt.parameters[kw].index(value)
|
118
|
-
|
119
|
-
if value_inx
|
120
|
-
@prompt.parameters[kw].delete_at(value_inx)
|
121
|
-
end
|
122
|
-
|
123
|
-
# The most recent value for this kw will always be
|
124
|
-
# in the last position
|
125
|
-
@prompt.parameters[kw] << value
|
126
|
-
@prompt.parameters[kw].shift if @prompt.parameters[kw].size > KW_HISTORY_MAX
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
# Function to setup the Reline history with a maximum depth
|
133
|
-
def setup_reline_history(max_history_size=5)
|
134
|
-
Reline::HISTORY.clear
|
135
|
-
# Reline::HISTORY.max_size = max_history_size
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
# query the user for a value to the keyword allow the
|
140
|
-
# reuse of the previous value shown as the default
|
141
|
-
#
|
142
|
-
# FIXME: Ruby v3.3.0 drops readline in favor or reline
|
143
|
-
# internally it redirects "require 'readline'" to Reline
|
144
|
-
# puts lipstick on the pig so that you can continue to
|
145
|
-
# use the Readline namespace
|
146
|
-
#
|
147
|
-
def keyword_value(kw, history_array)
|
148
|
-
setup_reline_history
|
149
|
-
|
150
|
-
default = history_array.last
|
151
|
-
|
152
|
-
Array(history_array).each { |entry| Reline::HISTORY.push(entry) unless entry.nil? || entry.empty? }
|
153
|
-
|
154
|
-
puts "Parameter #{kw} ..."
|
155
|
-
|
156
|
-
if default&.empty?
|
157
|
-
user_prompt = "\n-=> "
|
158
|
-
else
|
159
|
-
user_prompt = "\n(#{default}) -=> "
|
160
|
-
end
|
161
|
-
|
162
|
-
a_string = ask_question_with_reline(user_prompt)
|
163
|
-
|
164
|
-
if a_string.nil?
|
165
|
-
puts "okay. Come back soon."
|
166
|
-
exit
|
167
|
-
end
|
168
|
-
|
169
|
-
puts
|
170
|
-
a_string.empty? ? default : a_string
|
171
|
-
end
|
172
|
-
|
173
|
-
|
174
|
-
# Search for a prompt with a matching id or keyword
|
175
|
-
def search_for_a_matching_prompt(prompt_id)
|
176
|
-
# TODO: using the rgfzf version of the search_proc should only
|
177
|
-
# return a single prompt_id
|
178
|
-
found_prompts = PromptManager::Prompt.search(prompt_id)
|
179
|
-
|
180
|
-
if found_prompts.empty?
|
181
|
-
if AIA.config.edit?
|
182
|
-
create_prompt(prompt_id)
|
183
|
-
edit_prompt
|
184
|
-
else
|
185
|
-
abort <<~EOS
|
186
|
-
|
187
|
-
No prompts where found for: #{prompt_id}
|
188
|
-
To create a prompt with this ID use the --edit option
|
189
|
-
like this:
|
190
|
-
#{MY_NAME} #{prompt_id} --edit
|
191
|
-
|
192
|
-
EOS
|
193
|
-
end
|
194
|
-
else
|
195
|
-
prompt_id = 1 == found_prompts.size ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
196
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
|
201
|
-
def handle_multiple_prompts(found_these, while_looking_for_this)
|
202
|
-
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
203
|
-
|
204
|
-
# Create an instance of AIA::Fzf with appropriate parameters
|
205
|
-
fzf_instance = AIA::Fzf.new(
|
206
|
-
list: found_these,
|
207
|
-
directory: AIA.config.prompts_dir, # Assuming this is the correct directory
|
208
|
-
query: while_looking_for_this,
|
209
|
-
subject: 'Prompt IDs',
|
210
|
-
prompt: 'Select one: '
|
211
|
-
)
|
212
|
-
|
213
|
-
# Run the fzf instance and get the selected result
|
214
|
-
result = fzf_instance.run
|
215
|
-
|
216
|
-
exit unless result
|
217
|
-
|
218
|
-
result
|
219
|
-
end
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
def create_prompt(prompt_id)
|
225
|
-
@prompt = PromptManager::Prompt.create(id: prompt_id)
|
226
|
-
# TODO: consider a configurable prompt template
|
227
|
-
# ERB ???
|
228
|
-
end
|
229
|
-
|
230
|
-
|
231
|
-
def edit_prompt
|
232
|
-
# FIXME: replace with the editor from the configuration
|
233
|
-
|
234
|
-
@editor = AIA::Subl.new(
|
235
|
-
file: @prompt.path
|
236
|
-
)
|
237
|
-
|
238
|
-
@editor.run # blocks until file is closed
|
239
|
-
|
240
|
-
AIA.config[:edit?] = false # turn off the --edit switch
|
241
|
-
|
242
|
-
# reload the edited prompt
|
243
|
-
@prompt = PromptManager::Prompt.get(id: @prompt.id)
|
244
|
-
end
|
245
|
-
|
246
|
-
|
247
|
-
def show_prompt_without_comments
|
248
|
-
puts remove_comments.wrap(indent: 4)
|
249
|
-
end
|
250
|
-
|
251
|
-
|
252
|
-
# removes comments and directives
|
253
|
-
def remove_comments
|
254
|
-
lines = @prompt.text
|
255
|
-
.split("\n")
|
256
|
-
.reject{|a_line|
|
257
|
-
a_line.strip.start_with?('#') ||
|
258
|
-
a_line.strip.start_with?('//')
|
259
|
-
}
|
260
|
-
|
261
|
-
# Remove empty lines at the start of the prompt
|
262
|
-
#
|
263
|
-
lines = lines.drop_while(&:empty?)
|
264
|
-
|
265
|
-
# Drop all the lines at __END__ and after
|
266
|
-
#
|
267
|
-
logical_end_inx = lines.index("__END__")
|
268
|
-
|
269
|
-
if logical_end_inx
|
270
|
-
lines[0...logical_end_inx] # NOTE: ... means to not include last index
|
271
|
-
else
|
272
|
-
lines
|
273
|
-
end.join("\n")
|
274
|
-
end
|
275
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# lib/aia/tools/ai_client_backend.rb
|
2
|
-
|
3
|
-
# This is WIP in the `develop` branch
|
4
|
-
# do not use.
|
5
|
-
|
6
|
-
require 'ai_client'
|
7
|
-
require_relative 'backend_common'
|
8
|
-
|
9
|
-
class AIA::AiClientBackend < AIA::Tools
|
10
|
-
include AIA::BackendCommon
|
11
|
-
|
12
|
-
meta(
|
13
|
-
name: 'ai_client',
|
14
|
-
role: :backend,
|
15
|
-
desc: 'AI Client integration for unified model access',
|
16
|
-
url: 'https://github.com/path/to/ai_client', # TODO: Update URL
|
17
|
-
install: 'gem install ai_client',
|
18
|
-
)
|
19
|
-
|
20
|
-
attr_reader :client, :raw_response
|
21
|
-
|
22
|
-
DEFAULT_PARAMETERS = ''
|
23
|
-
DIRECTIVES = %w[
|
24
|
-
model
|
25
|
-
temperature
|
26
|
-
max_tokens
|
27
|
-
top_p
|
28
|
-
frequency_penalty
|
29
|
-
presence_penalty
|
30
|
-
]
|
31
|
-
|
32
|
-
def initialize(text: "", files: [])
|
33
|
-
super
|
34
|
-
@client = AiClient.new
|
35
|
-
end
|
36
|
-
|
37
|
-
def build_command
|
38
|
-
# No-op - ai_client doesn't use command line
|
39
|
-
@parameters = ""
|
40
|
-
end
|
41
|
-
|
42
|
-
def run
|
43
|
-
handle_model(AIA.config.model)
|
44
|
-
rescue => e
|
45
|
-
puts "Error handling model #{AIA.config.model}: #{e.message}"
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def handle_model(model_name)
|
51
|
-
case model_name
|
52
|
-
when /vision/
|
53
|
-
image2text
|
54
|
-
when /^gpt/
|
55
|
-
text2text
|
56
|
-
when /^dall-e/
|
57
|
-
text2image
|
58
|
-
when /^tts/
|
59
|
-
text2audio
|
60
|
-
when /^whisper/
|
61
|
-
audio2text
|
62
|
-
else
|
63
|
-
raise "Unsupported model: #{model_name}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def text2text
|
68
|
-
response = client.complete(
|
69
|
-
prompt: text,
|
70
|
-
model: AIA.config.model,
|
71
|
-
temperature: AIA.config.temp
|
72
|
-
)
|
73
|
-
response.completion
|
74
|
-
end
|
75
|
-
|
76
|
-
# Placeholder methods to maintain API compatibility
|
77
|
-
def image2text
|
78
|
-
raise "Not yet implemented for ai_client"
|
79
|
-
end
|
80
|
-
|
81
|
-
def text2image
|
82
|
-
raise "Not yet implemented for ai_client"
|
83
|
-
end
|
84
|
-
|
85
|
-
def text2audio
|
86
|
-
raise "Not yet implemented for ai_client"
|
87
|
-
end
|
88
|
-
|
89
|
-
def audio2text
|
90
|
-
raise "Not yet implemented for ai_client"
|
91
|
-
end
|
92
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# aia/lib/aia/tools/backend_common.rb
|
2
|
-
|
3
|
-
# Used by both the AIA::Mods and AIA::Sgpt classes
|
4
|
-
|
5
|
-
module AIA::BackendCommon
|
6
|
-
attr_accessor :command, :text, :files, :parameters
|
7
|
-
|
8
|
-
def initialize(text: "", files: [])
|
9
|
-
@text = text
|
10
|
-
@files = files
|
11
|
-
@parameters = self.class::DEFAULT_PARAMETERS.dup
|
12
|
-
build_command
|
13
|
-
end
|
14
|
-
|
15
|
-
|
16
|
-
def sanitize(input)
|
17
|
-
Shellwords.escape(input)
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
def build_command
|
22
|
-
@parameters += " --model #{AIA.config.model} " if AIA.config.model
|
23
|
-
@parameters += AIA.config.extra
|
24
|
-
|
25
|
-
set_parameter_from_directives
|
26
|
-
|
27
|
-
@command = "#{meta.name} #{@parameters} "
|
28
|
-
@command += sanitize(text)
|
29
|
-
|
30
|
-
puts @command if AIA.config.debug?
|
31
|
-
|
32
|
-
@command
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
def set_parameter_from_directives
|
37
|
-
AIA.config.directives.each do |entry|
|
38
|
-
directive, value = entry
|
39
|
-
if self.class::DIRECTIVES.include?(directive)
|
40
|
-
@parameters += " --#{directive} #{sanitize(value)}" unless @parameters.include?(directive)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
def run
|
47
|
-
case @files.size
|
48
|
-
when 0
|
49
|
-
@result = `#{build_command}`
|
50
|
-
when 1
|
51
|
-
@result = `#{build_command} < #{@files.first}`
|
52
|
-
else
|
53
|
-
@result = %x[cat #{@files.join(' ')} | #{build_command}]
|
54
|
-
end
|
55
|
-
|
56
|
-
@result
|
57
|
-
end
|
58
|
-
end
|