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.
@@ -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