Class: Aidp::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/config.rb

Overview

Configuration management for both execute and analyze modes

Constant Summary collapse

DEFAULT_HARNESS_CONFIG =

Default configuration for harness

{
  harness: {
    max_retries: 2,
    default_provider: "cursor",
    fallback_providers: ["cursor"],
    no_api_keys_required: false,
    provider_weights: {
      "cursor" => 3,
      "anthropic" => 2
    },
    circuit_breaker: {
      enabled: true,
      failure_threshold: 5,
      timeout: 300,
      half_open_max_calls: 3
    },
    retry: {
      enabled: true,
      max_attempts: 3,
      base_delay: 1.0,
      max_delay: 60.0,
      exponential_base: 2.0,
      jitter: true
    },
    rate_limit: {
      enabled: true,
      default_reset_time: 3600,
      burst_limit: 10,
      sustained_limit: 5
    },
    load_balancing: {
      enabled: true,
      strategy: "weighted_round_robin",
      health_check_interval: 30,
      unhealthy_threshold: 3
    },
    model_switching: {
      enabled: true,
      auto_switch_on_error: true,
      auto_switch_on_rate_limit: true,
      fallback_strategy: "sequential"
    },
    health_check: {
      enabled: true,
      interval: 60,
      timeout: 10,
      failure_threshold: 3,
      success_threshold: 2
    },
    metrics: {
      enabled: true,
      retention_days: 30,
      aggregation_interval: 300,
      export_interval: 3600
    },
    session: {
      enabled: true,
      timeout: 1800,
      sticky_sessions: true,
      session_affinity: "provider_model"
    }
  },
  providers: {
    cursor: {
      type: "subscription",
      priority: 1,
      model_family: "auto",
      default_flags: [],
      models: ["cursor-default", "cursor-fast", "cursor-precise"],
      model_weights: {
        "cursor-default" => 3,
        "cursor-fast" => 2,
        "cursor-precise" => 1
      },
      models_config: {
        "cursor-default" => {
          flags: [],
          timeout: 600
        },
        "cursor-fast" => {
          flags: ["--fast"],
          timeout: 300
        },
        "cursor-precise" => {
          flags: ["--precise"],
          timeout: 900
        }
      },
      features: {
        file_upload: true,
        code_generation: true,
        analysis: true
      },
      monitoring: {
        enabled: true,
        metrics_interval: 60
      }
    },
    anthropic: {
      type: "usage_based",
      priority: 2,
      model_family: "claude",
      max_tokens: 100_000,
      default_flags: ["--dangerously-skip-permissions"],
      models: ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", "claude-3-opus-20240229"],
      model_weights: {
        "claude-3-5-sonnet-20241022" => 3,
        "claude-3-5-haiku-20241022" => 2,
        "claude-3-opus-20240229" => 1
      },
      models_config: {
        "claude-3-5-sonnet-20241022" => {
          flags: ["--dangerously-skip-permissions"],
          max_tokens: 200_000,
          timeout: 300
        },
        "claude-3-5-haiku-20241022" => {
          flags: ["--dangerously-skip-permissions"],
          max_tokens: 200_000,
          timeout: 180
        },
        "claude-3-opus-20240229" => {
          flags: ["--dangerously-skip-permissions"],
          max_tokens: 200_000,
          timeout: 600
        }
      },
      auth: {
        api_key_env: "ANTHROPIC_API_KEY"
      },
      endpoints: {
        default: "https://api.anthropic.com/v1/messages"
      },
      features: {
        file_upload: true,
        code_generation: true,
        analysis: true,
        vision: true
      },
      monitoring: {
        enabled: true,
        metrics_interval: 60
      }
    }
  },
  skills: {
    search_paths: [],
    default_provider_filter: true,
    enable_custom_skills: true
  }
}.freeze

Class Method Summary collapse

Class Method Details

.aidp_dir(project_dir = Dir.pwd) ⇒ Object



299
300
301
# File 'lib/aidp/config.rb', line 299

def self.aidp_dir(project_dir = Dir.pwd)
  ConfigPaths.aidp_dir(project_dir)
end

.config_dir(project_dir = Dir.pwd) ⇒ Object



295
296
297
# File 'lib/aidp/config.rb', line 295

def self.config_dir(project_dir = Dir.pwd)
  ConfigPaths.config_dir(project_dir)
end

.config_exists?(project_dir = Dir.pwd) ⇒ Boolean

Check if configuration file exists

Returns:

  • (Boolean)


250
251
252
# File 'lib/aidp/config.rb', line 250

def self.config_exists?(project_dir = Dir.pwd)
  ConfigPaths.config_exists?(project_dir)
end

.config_file(project_dir = Dir.pwd) ⇒ Object

Expose path methods for convenience



291
292
293
# File 'lib/aidp/config.rb', line 291

def self.config_file(project_dir = Dir.pwd)
  ConfigPaths.config_file(project_dir)
end

.configured_providers(project_dir = Dir.pwd) ⇒ Object

Get all configured providers



234
235
236
237
238
# File 'lib/aidp/config.rb', line 234

def self.configured_providers(project_dir = Dir.pwd)
  config = load_harness_config(project_dir)
  providers_section = config[:providers] || config["providers"] || {}
  providers_section.keys.map(&:to_s)
end

.create_example_config(project_dir = Dir.pwd) ⇒ Object

Create example configuration file



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/aidp/config.rb', line 255

def self.create_example_config(project_dir = Dir.pwd)
  config_path = ConfigPaths.config_file(project_dir)
  return false if File.exist?(config_path)

  ConfigPaths.ensure_config_dir(project_dir)

  example_config = {
    harness: {
      max_retries: 2,
      default_provider: "cursor",
      fallback_providers: ["cursor"],
      no_api_keys_required: false
    },
    providers: {
      cursor: {
        type: "subscription",
        default_flags: []
      },
      claude: {
        type: "usage_based",
        max_tokens: 100_000,
        default_flags: ["--dangerously-skip-permissions"]
      },
      gemini: {
        type: "usage_based",
        max_tokens: 50_000,
        default_flags: []
      }
    }
  }

  File.write(config_path, YAML.dump(example_config))
  true
end

.harness_config(project_dir = Dir.pwd) ⇒ Object

Get harness configuration



216
217
218
219
220
221
222
# File 'lib/aidp/config.rb', line 216

def self.harness_config(project_dir = Dir.pwd)
  config = load_harness_config(project_dir)
  harness_section = config[:harness] || config["harness"] || {}

  # Convert string keys to symbols for consistency
  symbolize_keys(harness_section)
end

.load(project_dir = Dir.pwd) ⇒ Object



162
163
164
165
166
167
168
169
170
# File 'lib/aidp/config.rb', line 162

def self.load(project_dir = Dir.pwd)
  config_file = ConfigPaths.config_file(project_dir)

  if File.exist?(config_file)
    load_yaml_config(config_file)
  else
    {}
  end
end

.load_harness_config(project_dir = Dir.pwd) ⇒ Object

Load harness configuration with defaults



173
174
175
176
# File 'lib/aidp/config.rb', line 173

def self.load_harness_config(project_dir = Dir.pwd)
  config = load(project_dir)
  merge_harness_defaults(config)
end

.provider_config(provider_name, project_dir = Dir.pwd) ⇒ Object

Get provider configuration



225
226
227
228
229
230
231
# File 'lib/aidp/config.rb', line 225

def self.provider_config(provider_name, project_dir = Dir.pwd)
  config = load_harness_config(project_dir)
  providers_section = config[:providers] || config["providers"] || {}
  provider_config = providers_section[provider_name.to_s] || providers_section[provider_name.to_sym] || {}

  symbolize_keys(provider_config)
end

.skills_config(project_dir = Dir.pwd) ⇒ Object

Get skills configuration



241
242
243
244
245
246
247
# File 'lib/aidp/config.rb', line 241

def self.skills_config(project_dir = Dir.pwd)
  config = load_harness_config(project_dir)
  skills_section = config[:skills] || config["skills"] || {}

  # Convert string keys to symbols for consistency
  symbolize_keys(skills_section)
end

.validate_harness_config(config, project_dir = Dir.pwd) ⇒ Object

Validate harness configuration



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/aidp/config.rb', line 179

def self.validate_harness_config(config, project_dir = Dir.pwd)
  errors = []

  # Validate harness section (check the merged config, not original)
  harness_config = config[:harness] || config["harness"]
  if harness_config
    unless harness_config[:default_provider] || harness_config["default_provider"]
      errors << "Default provider not specified in harness config"
    end
  end

  # Validate providers section using config_validator
  # Only validate providers that exist in the original YAML file, not merged defaults
  original_config = load(project_dir)
  original_providers = original_config[:providers] || original_config["providers"]
  if original_providers&.any?
    require_relative "harness/config_validator"
    validator = Aidp::Harness::ConfigValidator.new(project_dir)

    # Only validate if the config file exists
    # Skip validation if we're validating a simple test config (no project_dir specified or simple config)
    should_validate = validator.config_exists? &&
      (project_dir != Dir.pwd || config[:harness]&.keys&.size.to_i > 2)
    if should_validate
      original_providers.each do |provider_name, _provider_config|
        validation_result = validator.validate_provider(provider_name)
        unless validation_result[:valid]
          errors.concat(validation_result[:errors])
        end
      end
    end
  end

  errors
end