Class: RailsConsoleAi::Configuration
- Inherits:
-
Object
- Object
- RailsConsoleAi::Configuration
- Defined in:
- lib/rails_console_ai/configuration.rb
Constant Summary collapse
- PROVIDERS =
%i[anthropic openai local bedrock].freeze
- MODEL_FAMILIES =
Per-family model attributes, matched by substring so one entry covers every ID variant of a family: bare Anthropic IDs (claude-sonnet-5), dated snapshots (claude-haiku-4-5-20251001), and Bedrock inference profiles (us.anthropic.claude-sonnet-5, global.anthropic.claude-opus-4-6-v1).
input/output are $ per MTok (converted to per-token in .pricing_for). Cache pricing is derived: read = 0.1x input, write = 1.25x input. temperature: false marks families that reject the
temperatureparameter (removed on opus-4-7+, sonnet-5, and fable-5). { 'claude-fable-5' => { input: 10.0, output: 50.0, max_tokens: 16_000, temperature: false }, 'claude-opus-5' => { input: 5.0, output: 25.0, max_tokens: 16_000, temperature: false }, 'claude-opus-4-8' => { input: 5.0, output: 25.0, max_tokens: 16_000, temperature: false }, 'claude-opus-4-7' => { input: 5.0, output: 25.0, max_tokens: 16_000, temperature: false }, 'claude-opus-4-6' => { input: 5.0, output: 25.0, max_tokens: 16_000, temperature: true }, 'claude-sonnet-5' => { input: 3.0, output: 15.0, max_tokens: 16_000, temperature: false }, 'claude-sonnet-4-6' => { input: 3.0, output: 15.0, max_tokens: 16_000, temperature: true }, 'claude-haiku-4-5' => { input: 1.0, output: 5.0, max_tokens: 16_000, temperature: true }, }.freeze
- MODEL_FAMILY_KEYS =
Family keys sorted longest-first so a more specific family always wins if keys ever overlap (e.g. a future 'claude-sonnet-5-5' entry would match before 'claude-sonnet-5').
MODEL_FAMILIES.keys.sort_by { |k| -k.length }.freeze
- DEFAULT_ERROR_HINTS =
Known environment-level failures the executor recognizes and explains to the LLM on the FIRST occurrence, so it doesn't burn rounds rediscovering them through trial and error. Each entry: { name:, pattern:, hint: }. The pattern is matched against the execution error AND the captured output (to catch errors rescued and printed by the generated code itself).
[ { name: :decryption_failure, pattern: /OpenSSL::Cipher::CipherError|bad decrypt|ActiveRecord::Encryption::Errors/i, hint: "Decryption failed — this console process's encryption key (e.g. ENV['ENCRYPTION_KEY']) " \ "appears to be missing, invalid, or a placeholder for the data you are reading. This is an " \ "environment configuration issue, not a data issue, and it affects EVERY encrypted field in " \ "this session. Retrying with different code (reloading records, toggling encryption flags, " \ "decrypting other fields or records) will fail the same way. Do NOT retry. Report this " \ "limitation to the user, tell them the encryption key needs to be configured for this " \ "environment, and answer using only what you can determine without decrypting." } ].freeze
Instance Attribute Summary collapse
-
#admin_password ⇒ Object
Returns the value of attribute admin_password.
-
#admin_username ⇒ Object
Returns the value of attribute admin_username.
-
#api_key ⇒ Object
Returns the value of attribute api_key.
-
#authenticate ⇒ Object
Returns the value of attribute authenticate.
-
#auto_execute ⇒ Object
Returns the value of attribute auto_execute.
-
#bedrock_region ⇒ Object
Returns the value of attribute bedrock_region.
-
#bypass_guards_for_methods ⇒ Object
Returns the value of attribute bypass_guards_for_methods.
-
#channels ⇒ Object
Returns the value of attribute channels.
-
#code_search_paths ⇒ Object
Returns the value of attribute code_search_paths.
-
#connection_class ⇒ Object
Returns the value of attribute connection_class.
-
#debug ⇒ Object
Returns the value of attribute debug.
-
#error_hints ⇒ Object
Returns the value of attribute error_hints.
-
#local_api_key ⇒ Object
Returns the value of attribute local_api_key.
-
#local_model ⇒ Object
Returns the value of attribute local_model.
-
#local_url ⇒ Object
Returns the value of attribute local_url.
-
#max_tokens ⇒ Object
Returns the value of attribute max_tokens.
-
#max_tool_rounds ⇒ Object
Returns the value of attribute max_tool_rounds.
-
#memories_enabled ⇒ Object
Returns the value of attribute memories_enabled.
-
#model ⇒ Object
Returns the value of attribute model.
-
#provider ⇒ Object
Returns the value of attribute provider.
-
#session_logging ⇒ Object
Returns the value of attribute session_logging.
-
#slack_allowed_usernames ⇒ Object
Returns the value of attribute slack_allowed_usernames.
-
#slack_app_token ⇒ Object
Returns the value of attribute slack_app_token.
-
#slack_bot_token ⇒ Object
Returns the value of attribute slack_bot_token.
-
#slack_channel_ids ⇒ Object
Returns the value of attribute slack_channel_ids.
-
#storage_adapter ⇒ Object
Returns the value of attribute storage_adapter.
-
#sub_agent_max_rounds ⇒ Object
Returns the value of attribute sub_agent_max_rounds.
-
#sub_agent_model ⇒ Object
Returns the value of attribute sub_agent_model.
-
#temperature ⇒ Object
Returns the value of attribute temperature.
-
#thinking_model ⇒ Object
Returns the value of attribute thinking_model.
-
#timeout ⇒ Object
Returns the value of attribute timeout.
-
#token_nudge_threshold ⇒ Object
Returns the value of attribute token_nudge_threshold.
-
#token_stop_threshold ⇒ Object
Returns the value of attribute token_stop_threshold.
-
#user_extra_info ⇒ Object
Returns the value of attribute user_extra_info.
Class Method Summary collapse
-
.model_family(model_id) ⇒ Object
Returns the family attributes for a model ID, or nil for unknown models.
-
.pricing_for(model_id) ⇒ Object
Per-token pricing for a model ID, matched by family.
Instance Method Summary collapse
-
#channel_setting(mode, key) ⇒ Object
Look up a per-channel setting with backward compatibility.
-
#initialize ⇒ Configuration
constructor
A new instance of Configuration.
- #resolve_user_extra_info(username) ⇒ Object
- #resolved_api_key ⇒ Object
- #resolved_max_tokens ⇒ Object
- #resolved_model ⇒ Object
-
#resolved_temperature ⇒ Object
Returns nil for model families that reject the
temperatureparameter (opus-4-7+, sonnet-5, fable-5) so providers omit the field from the request. - #resolved_thinking_model ⇒ Object
- #resolved_timeout ⇒ Object
-
#safety_guard(name, &block) ⇒ Object
Register a custom safety guard by name with an around-block.
- #safety_guards ⇒ Object
-
#use_builtin_safety_guard(name, allow: nil) ⇒ Object
Register a built-in safety guard by name.
-
#username_allowed?(mode, key, username) ⇒ Boolean
Check if a username is permitted by a channel setting.
- #validate! ⇒ Object
Constructor Details
#initialize ⇒ Configuration
Returns a new instance of Configuration.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/rails_console_ai/configuration.rb', line 89 def initialize @provider = :anthropic @api_key = nil @model = nil @thinking_model = nil @max_tokens = nil @auto_execute = false @temperature = 0.2 @timeout = 30 @debug = false @max_tool_rounds = 200 @error_hints = DEFAULT_ERROR_HINTS.dup @token_nudge_threshold = 500_000 # input tokens in one tool loop → nudge model to wrap up (nil disables) @token_stop_threshold = 1_000_000 # input tokens in one tool loop → force a final answer (nil disables) @storage_adapter = nil @memories_enabled = true @session_logging = true @connection_class = nil @admin_username = nil @admin_password = nil @authenticate = nil @safety_guards = nil @slack_bot_token = nil @slack_app_token = nil @slack_channel_ids = nil @slack_allowed_usernames = nil @local_url = 'http://localhost:11434' @local_model = 'qwen2.5:7b' @local_api_key = nil @bedrock_region = nil @code_search_paths = %w[app] @channels = {} @bypass_guards_for_methods = [] @user_extra_info = {} @sub_agent_max_rounds = 15 @sub_agent_model = nil end |
Instance Attribute Details
#admin_password ⇒ Object
Returns the value of attribute admin_password.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def admin_password @admin_password end |
#admin_username ⇒ Object
Returns the value of attribute admin_username.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def admin_username @admin_username end |
#api_key ⇒ Object
Returns the value of attribute api_key.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def api_key @api_key end |
#authenticate ⇒ Object
Returns the value of attribute authenticate.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def authenticate @authenticate end |
#auto_execute ⇒ Object
Returns the value of attribute auto_execute.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def auto_execute @auto_execute end |
#bedrock_region ⇒ Object
Returns the value of attribute bedrock_region.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def bedrock_region @bedrock_region end |
#bypass_guards_for_methods ⇒ Object
Returns the value of attribute bypass_guards_for_methods.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def bypass_guards_for_methods @bypass_guards_for_methods end |
#channels ⇒ Object
Returns the value of attribute channels.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def channels @channels end |
#code_search_paths ⇒ Object
Returns the value of attribute code_search_paths.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def code_search_paths @code_search_paths end |
#connection_class ⇒ Object
Returns the value of attribute connection_class.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def connection_class @connection_class end |
#debug ⇒ Object
Returns the value of attribute debug.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def debug @debug end |
#error_hints ⇒ Object
Returns the value of attribute error_hints.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def error_hints @error_hints end |
#local_api_key ⇒ Object
Returns the value of attribute local_api_key.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def local_api_key @local_api_key end |
#local_model ⇒ Object
Returns the value of attribute local_model.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def local_model @local_model end |
#local_url ⇒ Object
Returns the value of attribute local_url.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def local_url @local_url end |
#max_tokens ⇒ Object
Returns the value of attribute max_tokens.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def max_tokens @max_tokens end |
#max_tool_rounds ⇒ Object
Returns the value of attribute max_tool_rounds.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def max_tool_rounds @max_tool_rounds end |
#memories_enabled ⇒ Object
Returns the value of attribute memories_enabled.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def memories_enabled @memories_enabled end |
#model ⇒ Object
Returns the value of attribute model.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def model @model end |
#provider ⇒ Object
Returns the value of attribute provider.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def provider @provider end |
#session_logging ⇒ Object
Returns the value of attribute session_logging.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def session_logging @session_logging end |
#slack_allowed_usernames ⇒ Object
Returns the value of attribute slack_allowed_usernames.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def slack_allowed_usernames @slack_allowed_usernames end |
#slack_app_token ⇒ Object
Returns the value of attribute slack_app_token.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def slack_app_token @slack_app_token end |
#slack_bot_token ⇒ Object
Returns the value of attribute slack_bot_token.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def slack_bot_token @slack_bot_token end |
#slack_channel_ids ⇒ Object
Returns the value of attribute slack_channel_ids.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def slack_channel_ids @slack_channel_ids end |
#storage_adapter ⇒ Object
Returns the value of attribute storage_adapter.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def storage_adapter @storage_adapter end |
#sub_agent_max_rounds ⇒ Object
Returns the value of attribute sub_agent_max_rounds.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def sub_agent_max_rounds @sub_agent_max_rounds end |
#sub_agent_model ⇒ Object
Returns the value of attribute sub_agent_model.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def sub_agent_model @sub_agent_model end |
#temperature ⇒ Object
Returns the value of attribute temperature.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def temperature @temperature end |
#thinking_model ⇒ Object
Returns the value of attribute thinking_model.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def thinking_model @thinking_model end |
#timeout ⇒ Object
Returns the value of attribute timeout.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def timeout @timeout end |
#token_nudge_threshold ⇒ Object
Returns the value of attribute token_nudge_threshold.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def token_nudge_threshold @token_nudge_threshold end |
#token_stop_threshold ⇒ Object
Returns the value of attribute token_stop_threshold.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def token_stop_threshold @token_stop_threshold end |
#user_extra_info ⇒ Object
Returns the value of attribute user_extra_info.
70 71 72 |
# File 'lib/rails_console_ai/configuration.rb', line 70 def user_extra_info @user_extra_info end |
Class Method Details
.model_family(model_id) ⇒ Object
Returns the family attributes for a model ID, or nil for unknown models.
31 32 33 34 35 |
# File 'lib/rails_console_ai/configuration.rb', line 31 def self.model_family(model_id) return nil unless model_id key = MODEL_FAMILY_KEYS.find { |k| model_id.include?(k) } key && MODEL_FAMILIES[key] end |
.pricing_for(model_id) ⇒ Object
Per-token pricing for a model ID, matched by family. Returns { input:, output:, cache_read:, cache_write: } or nil for unknown models.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rails_console_ai/configuration.rb', line 39 def self.pricing_for(model_id) family = model_family(model_id) return nil unless family input = family[:input] / 1_000_000 { input: input, output: family[:output] / 1_000_000, cache_read: input * 0.1, cache_write: input * 1.25, } end |
Instance Method Details
#channel_setting(mode, key) ⇒ Object
Look up a per-channel setting with backward compatibility. Falls back to top-level slack_* config when channels hash doesn't have the key.
134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/rails_console_ai/configuration.rb', line 134 def channel_setting(mode, key) channel_cfg = @channels[mode.to_s] || {} value = channel_cfg[key.to_s] # Backward compatibility: slack_allowed_usernames → channels.slack.allowed_usernames if value.nil? && mode.to_s == 'slack' && key.to_s == 'allowed_usernames' value = @slack_allowed_usernames end value end |
#resolve_user_extra_info(username) ⇒ Object
127 128 129 130 |
# File 'lib/rails_console_ai/configuration.rb', line 127 def resolve_user_extra_info(username) return nil if @user_extra_info.nil? || @user_extra_info.empty? || username.nil? @user_extra_info[username.to_s.downcase] end |
#resolved_api_key ⇒ Object
200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/rails_console_ai/configuration.rb', line 200 def resolved_api_key return @api_key if @api_key && !@api_key.empty? case @provider when :anthropic ENV['ANTHROPIC_API_KEY'] when :openai ENV['OPENAI_API_KEY'] when :local @local_api_key || 'no-key' when :bedrock 'aws-sdk' end end |
#resolved_max_tokens ⇒ Object
230 231 232 233 234 235 |
# File 'lib/rails_console_ai/configuration.rb', line 230 def resolved_max_tokens return @max_tokens if @max_tokens family = self.class.model_family(resolved_model) family ? family[:max_tokens] : 4096 end |
#resolved_model ⇒ Object
215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/rails_console_ai/configuration.rb', line 215 def resolved_model return @model if @model && !@model.empty? case @provider when :anthropic 'claude-sonnet-5' when :openai 'gpt-5.3-codex' when :local @local_model when :bedrock 'us.anthropic.claude-sonnet-5' end end |
#resolved_temperature ⇒ Object
Returns nil for model families that reject the temperature parameter
(opus-4-7+, sonnet-5, fable-5) so providers omit the field from the request.
239 240 241 242 243 |
# File 'lib/rails_console_ai/configuration.rb', line 239 def resolved_temperature family = self.class.model_family(resolved_model) return nil if family && family[:temperature] == false @temperature end |
#resolved_thinking_model ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/rails_console_ai/configuration.rb', line 245 def resolved_thinking_model return @thinking_model if @thinking_model && !@thinking_model.empty? case @provider when :anthropic 'claude-opus-5' when :openai 'gpt-5.3-codex' when :local @local_model when :bedrock 'us.anthropic.claude-opus-5' end end |
#resolved_timeout ⇒ Object
260 261 262 |
# File 'lib/rails_console_ai/configuration.rb', line 260 def resolved_timeout @provider == :local ? [@timeout, 300].max : @timeout end |
#safety_guard(name, &block) ⇒ Object
Register a custom safety guard by name with an around-block.
config.safety_guard :mailers do |&execute|
ActionMailer::Base.perform_deliveries = false
execute.call
ensure
ActionMailer::Base.perform_deliveries = true
end
170 171 172 |
# File 'lib/rails_console_ai/configuration.rb', line 170 def safety_guard(name, &block) safety_guards.add(name, &block) end |
#safety_guards ⇒ Object
155 156 157 158 159 160 |
# File 'lib/rails_console_ai/configuration.rb', line 155 def safety_guards @safety_guards ||= begin require 'rails_console_ai/safety_guards' SafetyGuards.new end end |
#use_builtin_safety_guard(name, allow: nil) ⇒ Object
Register a built-in safety guard by name. Available: :database_writes, :http_mutations, :mailers
Options:
allow: Array of strings or regexps to allowlist for this guard.
- :http_mutations → hosts (e.g. "s3.amazonaws.com", /googleapis\.com/)
- :database_writes → table names (e.g. "rails_console_ai_sessions")
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/rails_console_ai/configuration.rb', line 181 def use_builtin_safety_guard(name, allow: nil) require 'rails_console_ai/safety_guards' guard_name = name.to_sym case guard_name when :database_writes safety_guards.add(:database_writes, &BuiltinGuards.database_writes) when :http_mutations safety_guards.add(:http_mutations, &BuiltinGuards.http_mutations) when :mailers safety_guards.add(:mailers, &BuiltinGuards.mailers) else raise ConfigurationError, "Unknown built-in safety guard: #{name}. Available: database_writes, http_mutations, mailers" end if allow Array(allow).each { |key| safety_guards.allow_global(guard_name, key) } end end |
#username_allowed?(mode, key, username) ⇒ Boolean
Check if a username is permitted by a channel setting. Returns true when the setting is nil (not configured = no restriction).
148 149 150 151 152 153 |
# File 'lib/rails_console_ai/configuration.rb', line 148 def username_allowed?(mode, key, username) list = channel_setting(mode, key) return true if list.nil? normalized = Array(list).map(&:to_s).map(&:downcase) normalized.include?('all') || normalized.include?(username.to_s.downcase) end |
#validate! ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/rails_console_ai/configuration.rb', line 264 def validate! unless PROVIDERS.include?(@provider) raise ConfigurationError, "Unknown provider: #{@provider}. Valid: #{PROVIDERS.join(', ')}" end if @provider == :local raise ConfigurationError, "No local_url configured for :local provider." unless @local_url && !@local_url.empty? elsif @provider == :bedrock begin require 'aws-sdk-bedrockruntime' rescue LoadError raise ConfigurationError, "aws-sdk-bedrockruntime gem is required for the :bedrock provider. Add it to your Gemfile." end else unless resolved_api_key env_var = @provider == :anthropic ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY' raise ConfigurationError, "No API key. Set config.api_key or #{env_var} env var." end end end |