aider-ruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +542 -0
- data/bin/aider-ruby +320 -0
- data/lib/aider_ruby/client.rb +571 -0
- data/lib/aider_ruby/config.rb +371 -0
- data/lib/aider_ruby/error_handling.rb +57 -0
- data/lib/aider_ruby/models.rb +204 -0
- data/lib/aider_ruby/task_executor.rb +301 -0
- data/lib/aider_ruby/validation.rb +103 -0
- data/lib/aider_ruby/version.rb +3 -0
- data/lib/aider_ruby.rb +30 -0
- metadata +154 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module AiderRuby
|
|
5
|
+
module Config
|
|
6
|
+
# Module to manage model options
|
|
7
|
+
module ModelOptions
|
|
8
|
+
attr_accessor :model, :openai_api_key, :anthropic_api_key, :openai_api_base,
|
|
9
|
+
:openai_api_type, :openai_api_version, :openai_api_deployment_id,
|
|
10
|
+
:openai_organization_id, :reasoning_effort, :thinking_tokens,
|
|
11
|
+
:verify_ssl, :timeout, :edit_format, :architect, :auto_accept_architect,
|
|
12
|
+
:weak_model, :editor_model, :editor_edit_format, :show_model_warnings,
|
|
13
|
+
:check_model_accepts_settings, :max_chat_history_tokens
|
|
14
|
+
|
|
15
|
+
# Advanced model parameters
|
|
16
|
+
attr_accessor :use_temperature, :use_system_prompt, :use_repo_map, :extra_params,
|
|
17
|
+
:model_settings_file, :model_metadata_file, :alias_settings,
|
|
18
|
+
:reasoning_tag, :weak_model_name, :editor_model_name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Module to manage cache options
|
|
22
|
+
module CacheOptions
|
|
23
|
+
attr_accessor :cache_prompts, :cache_keepalive_pings
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Module to manage repomap options
|
|
27
|
+
module RepomapOptions
|
|
28
|
+
attr_accessor :map_tokens, :map_refresh, :map_multiplier_no_files
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Module to manage history options
|
|
32
|
+
module HistoryOptions
|
|
33
|
+
attr_accessor :input_history_file, :chat_history_file, :restore_chat_history,
|
|
34
|
+
:llm_history_file
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Module to manage output options
|
|
38
|
+
module OutputOptions
|
|
39
|
+
attr_accessor :dark_mode, :light_mode, :pretty, :stream, :user_input_color,
|
|
40
|
+
:tool_output_color, :tool_error_color, :tool_warning_color,
|
|
41
|
+
:assistant_output_color, :completion_menu_color, :completion_menu_bg_color,
|
|
42
|
+
:completion_menu_current_color, :completion_menu_current_bg_color,
|
|
43
|
+
:code_theme, :show_diffs
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Module to manage Git options
|
|
47
|
+
module GitOptions
|
|
48
|
+
attr_accessor :git, :gitignore, :add_gitignore_files, :aiderignore, :subtree_only,
|
|
49
|
+
:auto_commits, :dirty_commits, :attribute_author, :attribute_committer,
|
|
50
|
+
:attribute_commit_message_author, :attribute_commit_message_committer,
|
|
51
|
+
:attribute_co_authored_by, :git_commit_verify, :commit, :commit_prompt,
|
|
52
|
+
:dry_run, :skip_sanity_check_repo, :watch_files
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Module to manage linting and testing options
|
|
56
|
+
module LintTestOptions
|
|
57
|
+
attr_accessor :lint, :lint_cmd, :auto_lint, :test_cmd, :auto_test, :test
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Module to manage analytics options
|
|
61
|
+
module AnalyticsOptions
|
|
62
|
+
attr_accessor :analytics, :analytics_log, :analytics_disable, :analytics_posthog_host,
|
|
63
|
+
:analytics_posthog_project_api_key
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Module to manage voice options
|
|
67
|
+
module VoiceOptions
|
|
68
|
+
attr_accessor :voice_format, :voice_language, :voice_input_device
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Module to manage general options
|
|
72
|
+
module GeneralOptions
|
|
73
|
+
attr_accessor :disable_playwright, :vim, :chat_language, :commit_language,
|
|
74
|
+
:yes_always, :verbose, :encoding, :line_endings, :suggest_shell_commands,
|
|
75
|
+
:fancy_input, :multiline, :notifications, :notifications_command,
|
|
76
|
+
:detect_urls, :editor, :shell_completions
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Module to manage conventions and edit formats
|
|
80
|
+
module ConventionOptions
|
|
81
|
+
attr_accessor :conventions_files, :read_files
|
|
82
|
+
|
|
83
|
+
# Edit formats
|
|
84
|
+
attr_accessor :edit_format_whole, :edit_format_diff, :edit_format_diff_fenced,
|
|
85
|
+
:editor_edit_format_whole, :editor_edit_format_diff, :editor_edit_format_diff_fenced
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Main configuration class
|
|
89
|
+
class Configuration
|
|
90
|
+
include ModelOptions
|
|
91
|
+
include CacheOptions
|
|
92
|
+
include RepomapOptions
|
|
93
|
+
include HistoryOptions
|
|
94
|
+
include OutputOptions
|
|
95
|
+
include GitOptions
|
|
96
|
+
include LintTestOptions
|
|
97
|
+
include AnalyticsOptions
|
|
98
|
+
include VoiceOptions
|
|
99
|
+
include GeneralOptions
|
|
100
|
+
include ConventionOptions
|
|
101
|
+
|
|
102
|
+
class << self
|
|
103
|
+
attr_accessor :config_file, :env_file
|
|
104
|
+
|
|
105
|
+
def configure(&block)
|
|
106
|
+
instance_eval(&block) if block_given?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def load_from_file(file_path)
|
|
110
|
+
return unless File.exist?(file_path)
|
|
111
|
+
|
|
112
|
+
config = parse_config_file(file_path)
|
|
113
|
+
apply_config(config)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def load_from_env_file(file_path = '.env')
|
|
117
|
+
return unless File.exist?(file_path)
|
|
118
|
+
|
|
119
|
+
load_env_variables(file_path)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def parse_config_file(file_path)
|
|
125
|
+
case File.extname(file_path)
|
|
126
|
+
when '.yml', '.yaml'
|
|
127
|
+
YAML.load_file(file_path)
|
|
128
|
+
when '.json'
|
|
129
|
+
JSON.parse(File.read(file_path))
|
|
130
|
+
else
|
|
131
|
+
raise Error, "Unsupported config file format: #{File.extname(file_path)}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def load_env_variables(file_path)
|
|
136
|
+
File.readlines(file_path).each do |line|
|
|
137
|
+
next if line.strip.empty? || line.start_with?('#')
|
|
138
|
+
|
|
139
|
+
key, value = line.strip.split('=', 2)
|
|
140
|
+
next unless key && value
|
|
141
|
+
|
|
142
|
+
env_key = "AIDER_#{key.upcase}"
|
|
143
|
+
ENV[env_key] = value
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def apply_config(config)
|
|
148
|
+
config.each do |key, value|
|
|
149
|
+
method_name = "#{key}="
|
|
150
|
+
send(method_name, value) if respond_to?(method_name)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def initialize(options = {})
|
|
156
|
+
set_defaults
|
|
157
|
+
apply_options(options)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_aider_args
|
|
161
|
+
args = []
|
|
162
|
+
|
|
163
|
+
args.concat(model_args)
|
|
164
|
+
args.concat(cache_args)
|
|
165
|
+
args.concat(repomap_args)
|
|
166
|
+
args.concat(history_args)
|
|
167
|
+
args.concat(output_args)
|
|
168
|
+
args.concat(git_args)
|
|
169
|
+
args.concat(lint_test_args)
|
|
170
|
+
args.concat(analytics_args)
|
|
171
|
+
args.concat(voice_args)
|
|
172
|
+
args.concat(general_args)
|
|
173
|
+
args.concat(convention_args)
|
|
174
|
+
|
|
175
|
+
args
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
private
|
|
179
|
+
|
|
180
|
+
def apply_options(options)
|
|
181
|
+
options.each { |key, value| send("#{key}=", value) if respond_to?("#{key}=") }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def set_defaults
|
|
185
|
+
@encoding = 'utf-8'
|
|
186
|
+
@line_endings = 'platform'
|
|
187
|
+
@suggest_shell_commands = true
|
|
188
|
+
@fancy_input = true
|
|
189
|
+
@detect_urls = true
|
|
190
|
+
@voice_format = 'wav'
|
|
191
|
+
@voice_language = 'en'
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Methods to generate arguments by category
|
|
195
|
+
def model_args
|
|
196
|
+
args = []
|
|
197
|
+
|
|
198
|
+
args << '--model' << model if model
|
|
199
|
+
args << '--openai-api-key' << openai_api_key if openai_api_key
|
|
200
|
+
args << '--anthropic-api-key' << anthropic_api_key if anthropic_api_key
|
|
201
|
+
args << '--openai-api-base' << openai_api_base if openai_api_base
|
|
202
|
+
args << '--openai-api-type' << openai_api_type if openai_api_type
|
|
203
|
+
args << '--openai-api-version' << openai_api_version if openai_api_version
|
|
204
|
+
args << '--openai-api-deployment-id' << openai_api_deployment_id if openai_api_deployment_id
|
|
205
|
+
args << '--openai-organization-id' << openai_organization_id if openai_organization_id
|
|
206
|
+
args << '--reasoning-effort' << reasoning_effort.to_s if reasoning_effort
|
|
207
|
+
args << '--thinking-tokens' << thinking_tokens.to_s if thinking_tokens
|
|
208
|
+
args << '--verify-ssl' if verify_ssl
|
|
209
|
+
args << '--timeout' << timeout.to_s if timeout
|
|
210
|
+
args << '--edit-format' << edit_format if edit_format
|
|
211
|
+
args << '--architect' if architect
|
|
212
|
+
args << '--auto-accept-architect' if auto_accept_architect
|
|
213
|
+
args << '--weak-model' << weak_model if weak_model
|
|
214
|
+
args << '--editor-model' << editor_model if editor_model
|
|
215
|
+
args << '--editor-edit-format' << editor_edit_format if editor_edit_format
|
|
216
|
+
args << '--show-model-warnings' if show_model_warnings
|
|
217
|
+
args << '--check-model-accepts-settings' if check_model_accepts_settings
|
|
218
|
+
args << '--max-chat-history-tokens' << max_chat_history_tokens.to_s if max_chat_history_tokens
|
|
219
|
+
|
|
220
|
+
# Advanced parameters
|
|
221
|
+
args << '--model-settings-file' << model_settings_file if model_settings_file
|
|
222
|
+
args << '--model-metadata-file' << model_metadata_file if model_metadata_file
|
|
223
|
+
(alias_settings || []).each do |alias_setting|
|
|
224
|
+
args << '--alias' << "#{alias_setting[:alias]}:#{alias_setting[:model]}"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
args
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def cache_args
|
|
231
|
+
args = []
|
|
232
|
+
args << '--cache-prompts' if cache_prompts
|
|
233
|
+
args << '--cache-keepalive-pings' << cache_keepalive_pings.to_s if cache_keepalive_pings
|
|
234
|
+
args
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def repomap_args
|
|
238
|
+
args = []
|
|
239
|
+
args << '--map-tokens' << map_tokens.to_s if map_tokens
|
|
240
|
+
args << '--map-refresh' << map_refresh.to_s if map_refresh
|
|
241
|
+
args << '--map-multiplier-no-files' << map_multiplier_no_files.to_s if map_multiplier_no_files
|
|
242
|
+
args
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def history_args
|
|
246
|
+
args = []
|
|
247
|
+
args << '--input-history-file' << input_history_file if input_history_file
|
|
248
|
+
args << '--chat-history-file' << chat_history_file if chat_history_file
|
|
249
|
+
args << '--restore-chat-history' if restore_chat_history
|
|
250
|
+
args << '--llm-history-file' << llm_history_file if llm_history_file
|
|
251
|
+
args
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def output_args
|
|
255
|
+
args = []
|
|
256
|
+
args << '--dark-mode' if dark_mode
|
|
257
|
+
args << '--light-mode' if light_mode
|
|
258
|
+
args << '--pretty' if pretty
|
|
259
|
+
args << '--stream' if stream
|
|
260
|
+
args << '--user-input-color' << user_input_color if user_input_color
|
|
261
|
+
args << '--tool-output-color' << tool_output_color if tool_output_color
|
|
262
|
+
args << '--tool-error-color' << tool_error_color if tool_error_color
|
|
263
|
+
args << '--tool-warning-color' << tool_warning_color if tool_warning_color
|
|
264
|
+
args << '--assistant-output-color' << assistant_output_color if assistant_output_color
|
|
265
|
+
args << '--completion-menu-color' << completion_menu_color if completion_menu_color
|
|
266
|
+
args << '--completion-menu-bg-color' << completion_menu_bg_color if completion_menu_bg_color
|
|
267
|
+
args << '--completion-menu-current-color' << completion_menu_current_color if completion_menu_current_color
|
|
268
|
+
if completion_menu_current_bg_color
|
|
269
|
+
args << '--completion-menu-current-bg-color' << completion_menu_current_bg_color
|
|
270
|
+
end
|
|
271
|
+
args << '--code-theme' << code_theme if code_theme
|
|
272
|
+
args << '--show-diffs' if show_diffs
|
|
273
|
+
args
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def git_args
|
|
277
|
+
args = []
|
|
278
|
+
args << '--git' if git
|
|
279
|
+
args << '--gitignore' if gitignore
|
|
280
|
+
args << '--add-gitignore-files' if add_gitignore_files
|
|
281
|
+
args << '--aiderignore' << aiderignore if aiderignore
|
|
282
|
+
args << '--subtree-only' if subtree_only
|
|
283
|
+
args << '--auto-commits' if auto_commits
|
|
284
|
+
args << '--dirty-commits' if dirty_commits
|
|
285
|
+
args << '--attribute-author' if attribute_author
|
|
286
|
+
args << '--attribute-committer' if attribute_committer
|
|
287
|
+
args << '--attribute-commit-message-author' if attribute_commit_message_author
|
|
288
|
+
args << '--attribute-commit-message-committer' if attribute_commit_message_committer
|
|
289
|
+
args << '--attribute-co-authored-by' if attribute_co_authored_by
|
|
290
|
+
args << '--git-commit-verify' if git_commit_verify
|
|
291
|
+
args << '--commit' if commit
|
|
292
|
+
args << '--commit-prompt' << commit_prompt if commit_prompt
|
|
293
|
+
args << '--dry-run' if dry_run
|
|
294
|
+
args << '--skip-sanity-check-repo' if skip_sanity_check_repo
|
|
295
|
+
args << '--watch-files' if watch_files
|
|
296
|
+
args
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def lint_test_args
|
|
300
|
+
args = []
|
|
301
|
+
args << '--lint' if lint
|
|
302
|
+
args << '--lint-cmd' << lint_cmd if lint_cmd
|
|
303
|
+
args << '--auto-lint' if auto_lint
|
|
304
|
+
args << '--test-cmd' << test_cmd if test_cmd
|
|
305
|
+
args << '--auto-test' if auto_test
|
|
306
|
+
args << '--test' if test
|
|
307
|
+
args
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def analytics_args
|
|
311
|
+
args = []
|
|
312
|
+
args << '--analytics' if analytics
|
|
313
|
+
args << '--analytics-log' << analytics_log if analytics_log
|
|
314
|
+
args << '--analytics-disable' if analytics_disable
|
|
315
|
+
args << '--analytics-posthog-host' << analytics_posthog_host if analytics_posthog_host
|
|
316
|
+
if analytics_posthog_project_api_key
|
|
317
|
+
args << '--analytics-posthog-project-api-key' << analytics_posthog_project_api_key
|
|
318
|
+
end
|
|
319
|
+
args
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def voice_args
|
|
323
|
+
args = []
|
|
324
|
+
args << '--voice-format' << voice_format if voice_format
|
|
325
|
+
args << '--voice-language' << voice_language if voice_language
|
|
326
|
+
args << '--voice-input-device' << voice_input_device if voice_input_device
|
|
327
|
+
args
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def general_args
|
|
331
|
+
args = []
|
|
332
|
+
args << '--disable-playwright' if disable_playwright
|
|
333
|
+
args << '--vim' if vim
|
|
334
|
+
args << '--chat-language' << chat_language if chat_language
|
|
335
|
+
args << '--commit-language' << commit_language if commit_language
|
|
336
|
+
args << '--yes-always' if yes_always
|
|
337
|
+
args << '--verbose' if verbose
|
|
338
|
+
args << '--encoding' << encoding if encoding
|
|
339
|
+
args << '--line-endings' << line_endings if line_endings
|
|
340
|
+
args << '--suggest-shell-commands' if suggest_shell_commands
|
|
341
|
+
args << '--fancy-input' if fancy_input
|
|
342
|
+
args << '--multiline' if multiline
|
|
343
|
+
args << '--notifications' if notifications
|
|
344
|
+
args << '--notifications-command' << notifications_command if notifications_command
|
|
345
|
+
args << '--detect-urls' if detect_urls
|
|
346
|
+
args << '--editor' << editor if editor
|
|
347
|
+
args
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def convention_args
|
|
351
|
+
args = []
|
|
352
|
+
|
|
353
|
+
# Multiple conventions files
|
|
354
|
+
(conventions_files || []).each { |file| args << '--read' << file }
|
|
355
|
+
|
|
356
|
+
# Read files
|
|
357
|
+
(read_files || []).each { |file| args << '--read' << file }
|
|
358
|
+
|
|
359
|
+
# Edit formats
|
|
360
|
+
args << '--edit-format' << 'whole' if edit_format_whole
|
|
361
|
+
args << '--edit-format' << 'diff' if edit_format_diff
|
|
362
|
+
args << '--edit-format' << 'diff-fenced' if edit_format_diff_fenced
|
|
363
|
+
args << '--editor-edit-format' << 'whole' if editor_edit_format_whole
|
|
364
|
+
args << '--editor-edit-format' << 'diff' if editor_edit_format_diff
|
|
365
|
+
args << '--editor-edit-format' << 'diff-fenced' if editor_edit_format_diff_fenced
|
|
366
|
+
|
|
367
|
+
args
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module AiderRuby
|
|
2
|
+
module ErrorHandling
|
|
3
|
+
class ConfigurationError < AiderRuby::Error; end
|
|
4
|
+
class ModelError < AiderRuby::Error; end
|
|
5
|
+
class ExecutionError < AiderRuby::Error; end
|
|
6
|
+
class FileError < AiderRuby::Error; end
|
|
7
|
+
class ValidationError < AiderRuby::Error; end
|
|
8
|
+
|
|
9
|
+
def self.handle_configuration_error(error)
|
|
10
|
+
case error
|
|
11
|
+
when Errno::ENOENT
|
|
12
|
+
raise ConfigurationError, "Configuration file not found: #{error.message}"
|
|
13
|
+
when Psych::SyntaxError
|
|
14
|
+
raise ConfigurationError, "Invalid YAML syntax in configuration file: #{error.message}"
|
|
15
|
+
when JSON::ParserError
|
|
16
|
+
raise ConfigurationError, "Invalid JSON syntax in configuration file: #{error.message}"
|
|
17
|
+
else
|
|
18
|
+
raise ConfigurationError, "Configuration error: #{error.message}"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.handle_model_error(error)
|
|
23
|
+
case error
|
|
24
|
+
when ArgumentError
|
|
25
|
+
raise ModelError, "Invalid model configuration: #{error.message}"
|
|
26
|
+
else
|
|
27
|
+
raise ModelError, "Model error: #{error.message}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.handle_execution_error(error)
|
|
32
|
+
case error
|
|
33
|
+
when Errno::ENOENT
|
|
34
|
+
raise ExecutionError, "Aider command not found. Please install aider first."
|
|
35
|
+
when Errno::EACCES
|
|
36
|
+
raise ExecutionError, "Permission denied when executing aider command"
|
|
37
|
+
else
|
|
38
|
+
raise ExecutionError, "Execution error: #{error.message}"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.handle_file_error(error)
|
|
43
|
+
case error
|
|
44
|
+
when Errno::ENOENT
|
|
45
|
+
raise FileError, "File not found: #{error.message}"
|
|
46
|
+
when Errno::EACCES
|
|
47
|
+
raise FileError, "Permission denied accessing file: #{error.message}"
|
|
48
|
+
else
|
|
49
|
+
raise FileError, "File error: #{error.message}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.handle_validation_error(message)
|
|
54
|
+
raise ValidationError, message
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
module AiderRuby
|
|
2
|
+
class Models
|
|
3
|
+
# Supported model providers
|
|
4
|
+
PROVIDERS = {
|
|
5
|
+
openai: %w[
|
|
6
|
+
gpt-4o gpt-4o-mini gpt-4-turbo gpt-4 gpt-3.5-turbo
|
|
7
|
+
o1-preview o1-mini
|
|
8
|
+
],
|
|
9
|
+
anthropic: %w[
|
|
10
|
+
claude-3-5-sonnet-20241022 claude-3-5-haiku-20241022
|
|
11
|
+
claude-3-opus-20240229 claude-3-sonnet-20240229 claude-3-haiku-20240307
|
|
12
|
+
],
|
|
13
|
+
google: %w[
|
|
14
|
+
gemini-1.5-pro gemini-1.5-flash gemini-pro
|
|
15
|
+
],
|
|
16
|
+
groq: %w[
|
|
17
|
+
llama-3.1-70b-versatile llama-3.1-8b-instant
|
|
18
|
+
mixtral-8x7b-32768 gemma-7b-it
|
|
19
|
+
],
|
|
20
|
+
deepseek: %w[
|
|
21
|
+
deepseek-chat deepseek-coder
|
|
22
|
+
],
|
|
23
|
+
xai: %w[
|
|
24
|
+
grok-beta
|
|
25
|
+
],
|
|
26
|
+
cohere: %w[
|
|
27
|
+
command-r-plus command-r command-light
|
|
28
|
+
]
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
def list_providers
|
|
33
|
+
PROVIDERS.keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def list_models(provider = nil)
|
|
37
|
+
if provider
|
|
38
|
+
PROVIDERS[provider.to_sym] || []
|
|
39
|
+
else
|
|
40
|
+
PROVIDERS.values.flatten
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def supported_model?(model_name)
|
|
45
|
+
PROVIDERS.values.flatten.include?(model_name)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def provider_for_model(model_name)
|
|
49
|
+
PROVIDERS.find { |_provider, models| models.include?(model_name) }&.first
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def reasoning_models
|
|
53
|
+
%w[o1-preview o1-mini]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def is_reasoning_model?(model_name)
|
|
57
|
+
reasoning_models.include?(model_name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def vision_models
|
|
61
|
+
%w[
|
|
62
|
+
gpt-4o gpt-4o-mini gpt-4-turbo
|
|
63
|
+
claude-3-5-sonnet-20241022 claude-3-opus-20240229
|
|
64
|
+
gemini-1.5-pro gemini-1.5-flash
|
|
65
|
+
]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def has_vision?(model_name)
|
|
69
|
+
vision_models.include?(model_name)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def recommended_models
|
|
73
|
+
{
|
|
74
|
+
best_overall: 'claude-3-5-sonnet-20241022',
|
|
75
|
+
fastest: 'claude-3-5-haiku-20241022',
|
|
76
|
+
cheapest: 'gpt-4o-mini',
|
|
77
|
+
reasoning: 'o1-preview',
|
|
78
|
+
coding: 'deepseek-chat',
|
|
79
|
+
vision: 'gpt-4o'
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def model_info(model_name)
|
|
84
|
+
provider = provider_for_model(model_name)
|
|
85
|
+
return nil unless provider
|
|
86
|
+
|
|
87
|
+
{
|
|
88
|
+
name: model_name,
|
|
89
|
+
provider: provider,
|
|
90
|
+
reasoning: is_reasoning_model?(model_name),
|
|
91
|
+
vision: has_vision?(model_name),
|
|
92
|
+
context_window: context_window_for_model(model_name),
|
|
93
|
+
cost_per_token: cost_per_token_for_model(model_name)
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def context_window_for_model(model_name)
|
|
100
|
+
# Approximate context windows (in tokens)
|
|
101
|
+
case model_name
|
|
102
|
+
when /gpt-4o/
|
|
103
|
+
128_000
|
|
104
|
+
when /gpt-4-turbo/
|
|
105
|
+
128_000
|
|
106
|
+
when /gpt-4/
|
|
107
|
+
8_192
|
|
108
|
+
when /gpt-3.5-turbo/
|
|
109
|
+
4_096
|
|
110
|
+
when /o1/
|
|
111
|
+
200_000
|
|
112
|
+
when /claude-3-5-sonnet/
|
|
113
|
+
200_000
|
|
114
|
+
when /claude-3-5-haiku/
|
|
115
|
+
200_000
|
|
116
|
+
when /claude-3-opus/
|
|
117
|
+
200_000
|
|
118
|
+
when /claude-3-sonnet/
|
|
119
|
+
200_000
|
|
120
|
+
when /claude-3-haiku/
|
|
121
|
+
200_000
|
|
122
|
+
when /gemini-1.5/
|
|
123
|
+
1_000_000
|
|
124
|
+
when /gemini-pro/
|
|
125
|
+
32_768
|
|
126
|
+
when /llama-3.1-70b/
|
|
127
|
+
128_000
|
|
128
|
+
when /llama-3.1-8b/
|
|
129
|
+
128_000
|
|
130
|
+
when /mixtral/
|
|
131
|
+
32_768
|
|
132
|
+
when /deepseek/
|
|
133
|
+
64_000
|
|
134
|
+
when /grok/
|
|
135
|
+
128_000
|
|
136
|
+
when /command-r-plus/
|
|
137
|
+
128_000
|
|
138
|
+
when /command-r/
|
|
139
|
+
128_000
|
|
140
|
+
when /command-light/
|
|
141
|
+
100_000
|
|
142
|
+
else
|
|
143
|
+
4_096
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def cost_per_token_for_model(model_name)
|
|
148
|
+
# Approximate costs per 1M tokens (input/output average)
|
|
149
|
+
case model_name
|
|
150
|
+
when /gpt-4o/
|
|
151
|
+
{ input: 5.0, output: 15.0 }
|
|
152
|
+
when /gpt-4o-mini/
|
|
153
|
+
{ input: 0.15, output: 0.6 }
|
|
154
|
+
when /gpt-4-turbo/
|
|
155
|
+
{ input: 10.0, output: 30.0 }
|
|
156
|
+
when /gpt-4/
|
|
157
|
+
{ input: 30.0, output: 60.0 }
|
|
158
|
+
when /gpt-3.5-turbo/
|
|
159
|
+
{ input: 0.5, output: 1.5 }
|
|
160
|
+
when /o1-preview/
|
|
161
|
+
{ input: 15.0, output: 60.0 }
|
|
162
|
+
when /o1-mini/
|
|
163
|
+
{ input: 3.0, output: 12.0 }
|
|
164
|
+
when /claude-3-5-sonnet/
|
|
165
|
+
{ input: 3.0, output: 15.0 }
|
|
166
|
+
when /claude-3-5-haiku/
|
|
167
|
+
{ input: 0.8, output: 4.0 }
|
|
168
|
+
when /claude-3-opus/
|
|
169
|
+
{ input: 15.0, output: 75.0 }
|
|
170
|
+
when /claude-3-sonnet/
|
|
171
|
+
{ input: 3.0, output: 15.0 }
|
|
172
|
+
when /claude-3-haiku/
|
|
173
|
+
{ input: 0.25, output: 1.25 }
|
|
174
|
+
when /gemini-1.5-pro/
|
|
175
|
+
{ input: 1.25, output: 5.0 }
|
|
176
|
+
when /gemini-1.5-flash/
|
|
177
|
+
{ input: 0.075, output: 0.3 }
|
|
178
|
+
when /gemini-pro/
|
|
179
|
+
{ input: 0.5, output: 1.5 }
|
|
180
|
+
when /llama-3.1-70b/
|
|
181
|
+
{ input: 0.59, output: 0.79 }
|
|
182
|
+
when /llama-3.1-8b/
|
|
183
|
+
{ input: 0.05, output: 0.05 }
|
|
184
|
+
when /mixtral/
|
|
185
|
+
{ input: 0.27, output: 0.27 }
|
|
186
|
+
when /deepseek-chat/
|
|
187
|
+
{ input: 0.14, output: 0.28 }
|
|
188
|
+
when /deepseek-coder/
|
|
189
|
+
{ input: 0.14, output: 0.28 }
|
|
190
|
+
when /grok/
|
|
191
|
+
{ input: 0.01, output: 0.01 }
|
|
192
|
+
when /command-r-plus/
|
|
193
|
+
{ input: 3.0, output: 15.0 }
|
|
194
|
+
when /command-r/
|
|
195
|
+
{ input: 0.5, output: 1.5 }
|
|
196
|
+
when /command-light/
|
|
197
|
+
{ input: 0.3, output: 0.3 }
|
|
198
|
+
else
|
|
199
|
+
{ input: 1.0, output: 2.0 }
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|