Class: Aidp::Harness::ThinkingDepthManager
- Inherits:
-
Object
- Object
- Aidp::Harness::ThinkingDepthManager
- Includes:
- MessageDisplay
- Defined in:
- lib/aidp/harness/thinking_depth_manager.rb
Overview
Manages thinking depth tier selection and escalation Integrates with CapabilityRegistry and Configuration to select appropriate models
Constant Summary
Constants included from MessageDisplay
MessageDisplay::COLOR_MAP, MessageDisplay::CRITICAL_TYPES
Instance Attribute Summary collapse
-
#configuration ⇒ Object
readonly
Returns the value of attribute configuration.
-
#escalation_count ⇒ Object
readonly
Get escalation attempt count.
-
#registry ⇒ Object
readonly
Returns the value of attribute registry.
Instance Method Summary collapse
-
#can_escalate? ⇒ Boolean
Check if we can escalate to next tier.
-
#current_tier ⇒ Object
Get current tier (defaults to config default_tier if not set).
-
#current_tier=(tier) ⇒ Object
Set current tier (validates against max_tier).
-
#de_escalate_tier(reason: nil) ⇒ Object
De-escalate to next lower tier Returns new tier or nil if already at minimum.
-
#default_tier ⇒ Object
Get default tier from configuration.
-
#escalate_tier(reason: nil) ⇒ Object
Escalate to next higher tier Returns new tier or nil if already at max.
-
#initialize(configuration, registry: nil, root_dir: nil) ⇒ ThinkingDepthManager
constructor
A new instance of ThinkingDepthManager.
-
#max_tier ⇒ Object
Get maximum allowed tier (respects session override).
-
#max_tier=(tier) ⇒ Object
Set maximum tier for this session (temporary override).
-
#permission_for_current_tier ⇒ Object
Get permission level for current tier.
-
#recommend_tier_for_complexity(complexity_score) ⇒ Object
Get tier recommendation based on complexity score (0.0-1.0).
-
#reset_to_default ⇒ Object
Reset to default tier.
-
#select_model_for_tier(tier = nil, provider: nil) ⇒ Object
Select best model for current tier and provider Returns [provider_name, model_name, model_data] or nil.
-
#should_escalate_on_complexity?(context) ⇒ Boolean
Check if should escalate based on complexity thresholds.
-
#should_escalate_on_failures?(failure_count) ⇒ Boolean
Check if should escalate based on failure count.
-
#tier_for_model(provider, model) ⇒ Object
Get tier for a specific model.
-
#tier_history ⇒ Object
Get tier change history.
-
#tier_info(tier) ⇒ Object
Get information about a specific tier.
-
#tier_override_for(key) ⇒ Object
Check if tier override exists for skill/template.
Methods included from MessageDisplay
#display_message, included, #message_display_prompt, #quiet_mode?
Constructor Details
#initialize(configuration, registry: nil, root_dir: nil) ⇒ ThinkingDepthManager
Returns a new instance of ThinkingDepthManager.
16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 16 def initialize(configuration, registry: nil, root_dir: nil) @configuration = configuration @registry = registry || CapabilityRegistry.new(root_dir: root_dir || configuration.project_dir) @current_tier = nil @session_max_tier = nil @tier_history = [] @escalation_count = 0 Aidp.log_debug("thinking_depth_manager", "Initialized", default_tier: default_tier, max_tier: max_tier) end |
Instance Attribute Details
#configuration ⇒ Object (readonly)
Returns the value of attribute configuration.
14 15 16 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 14 def configuration @configuration end |
#escalation_count ⇒ Object (readonly)
Get escalation attempt count
363 364 365 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 363 def escalation_count @escalation_count end |
#registry ⇒ Object (readonly)
Returns the value of attribute registry.
14 15 16 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 14 def registry @registry end |
Instance Method Details
#can_escalate? ⇒ Boolean
Check if we can escalate to next tier
94 95 96 97 98 99 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 94 def can_escalate? next_tier = @registry.next_tier(current_tier) return false unless next_tier @registry.compare_tiers(next_tier, max_tier) <= 0 end |
#current_tier ⇒ Object
Get current tier (defaults to config default_tier if not set)
30 31 32 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 30 def current_tier @current_tier || default_tier end |
#current_tier=(tier) ⇒ Object
Set current tier (validates against max_tier)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 35 def current_tier=(tier) validate_tier!(tier) old_tier = current_tier @current_tier = enforce_max_tier(tier) if @current_tier != tier Aidp.log_warn("thinking_depth_manager", "Tier capped at max", requested: tier, applied: @current_tier, max: max_tier) end if @current_tier != old_tier log_tier_change(old_tier, @current_tier, "manual_set") end end |
#de_escalate_tier(reason: nil) ⇒ Object
De-escalate to next lower tier Returns new tier or nil if already at minimum
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 129 def de_escalate_tier(reason: nil) prev_tier = @registry.previous_tier(current_tier) unless prev_tier Aidp.log_debug("thinking_depth_manager", "Cannot de-escalate", current: current_tier) return nil end old_tier = current_tier @current_tier = prev_tier @escalation_count = [@escalation_count - 1, 0].max log_tier_change(old_tier, prev_tier, reason || "de-escalation") Aidp.log_info("thinking_depth_manager", "De-escalated tier", from: old_tier, to: prev_tier, reason: reason) prev_tier end |
#default_tier ⇒ Object
Get default tier from configuration
75 76 77 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 75 def default_tier configuration.default_tier end |
#escalate_tier(reason: nil) ⇒ Object
Escalate to next higher tier Returns new tier or nil if already at max
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/aidp/harness/thinking_depth_manager.rb', line 103 def escalate_tier(reason: nil) unless can_escalate? Aidp.log_warn("thinking_depth_manager", "Cannot escalate", current: current_tier, max: max_tier, reason: reason) return nil end old_tier = current_tier new_tier = @registry.next_tier(current_tier) @current_tier = new_tier @escalation_count += 1 log_tier_change(old_tier, new_tier, reason || "escalation") Aidp.log_info("thinking_depth_manager", "Escalated tier", from: old_tier, to: new_tier, reason: reason, count: @escalation_count) new_tier end |
#max_tier ⇒ Object
Get maximum allowed tier (respects session override)
53 54 55 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 53 def max_tier @session_max_tier || configuration.max_tier end |
#max_tier=(tier) ⇒ Object
Set maximum tier for this session (temporary override)
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 58 def max_tier=(tier) validate_tier!(tier) old_max = max_tier @session_max_tier = tier # If current tier exceeds new max, cap it if @registry.compare_tiers(current_tier, tier) > 0 self.current_tier = tier end Aidp.log_info("thinking_depth_manager", "Max tier updated", old: old_max, new: tier, current: current_tier) end |
#permission_for_current_tier ⇒ Object
Get permission level for current tier
358 359 360 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 358 def configuration.(current_tier) end |
#recommend_tier_for_complexity(complexity_score) ⇒ Object
Get tier recommendation based on complexity score (0.0-1.0)
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 320 def recommend_tier_for_complexity(complexity_score) tier = @registry.recommend_tier_for_complexity(complexity_score) # Cap at max_tier if @registry.compare_tiers(tier, max_tier) > 0 Aidp.log_debug("thinking_depth_manager", "Recommended tier capped", recommended: tier, complexity: complexity_score, capped_to: max_tier) return max_tier end Aidp.log_debug("thinking_depth_manager", "Recommended tier", tier: tier, complexity: complexity_score) tier end |
#reset_to_default ⇒ Object
Reset to default tier
80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 80 def reset_to_default old_tier = current_tier @current_tier = nil @session_max_tier = nil @escalation_count = 0 Aidp.log_info("thinking_depth_manager", "Reset to default", old: old_tier, new: current_tier) current_tier end |
#select_model_for_tier(tier = nil, provider: nil) ⇒ Object
Select best model for current tier and provider Returns [provider_name, model_name, model_data] or nil
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 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 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 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 289 290 291 292 293 294 295 296 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 152 def select_model_for_tier(tier = nil, provider: nil) tier ||= current_tier validate_tier!(tier) provider_has_no_tiers = provider && configuration.configured_tiers(provider).empty? provider_has_catalog_models = provider && !@registry.models_for_provider(provider).empty? if provider_has_no_tiers && !provider_has_catalog_models Aidp.log_info("thinking_depth_manager", "No configured tiers for provider, deferring to provider auto model selection", requested_tier: tier, provider: provider) return [provider, nil, {auto_model: true, reason: "provider_has_no_tiers"}] end # First, try to get models from user's configuration for this tier and provider if provider configured_models = configuration.models_for_tier(tier, provider) if configured_models.any? # Use first configured model for this provider and tier model_name = configured_models.first # Check if model is deprecated and try to upgrade require_relative "ruby_llm_registry" unless defined?(Aidp::Harness::RubyLLMRegistry) llm_registry = Aidp::Harness::RubyLLMRegistry.new if llm_registry.model_deprecated?(model_name, provider) Aidp.log_warn("thinking_depth_manager", "Configured model is deprecated", tier: tier, provider: provider, model: model_name) # Try to find replacement replacement = llm_registry.find_replacement_model(model_name, provider: provider) if replacement Aidp.log_info("thinking_depth_manager", "Auto-upgrading to non-deprecated model", tier: tier, provider: provider, old_model: model_name, new_model: replacement) model_name = replacement else # Try next model in config list non_deprecated = configured_models.find { |m| !llm_registry.model_deprecated?(m, provider) } if non_deprecated Aidp.log_info("thinking_depth_manager", "Using alternate configured model", tier: tier, provider: provider, skipped: model_name, selected: non_deprecated) model_name = non_deprecated else Aidp.log_warn("thinking_depth_manager", "All configured models deprecated, falling back to catalog", tier: tier, provider: provider) # Fall through to catalog selection model_name = nil end end end if model_name Aidp.log_debug("thinking_depth_manager", "Selected model from user config", tier: tier, provider: provider, model: model_name) return [provider, model_name, {}] end end # Provider specified but has no models for this tier in config # Try catalog for the specified provider before switching providers Aidp.log_debug("thinking_depth_manager", "Provider has no configured models for tier, trying catalog", tier: tier, provider: provider) # Continue to catalog-based selection below (will try specified provider first) else # No provider specified - this should not happen in normal flow # Log warning and fall through to catalog-based selection Aidp.log_warn("thinking_depth_manager", "select_model_for_tier called without provider", tier: tier) end # Fall back to catalog-based selection if no models in user config # If provider specified, try to find model for that provider in catalog if provider model_name, model_data = @registry.best_model_for_tier(tier, provider) if model_name Aidp.log_debug("thinking_depth_manager", "Selected model from catalog", tier: tier, provider: provider, model: model_name) return [provider, model_name, model_data] end # Per issue #323: Don't return nil here - let fallback logic handle missing tiers end # Try all providers in catalog if provider switching is allowed if configuration.allow_provider_switch_for_tier? providers_to_try = provider ? (@registry.provider_names - [provider]) : @registry.provider_names providers_to_try.each do |prov_name| model_name, model_data = @registry.best_model_for_tier(tier, prov_name) if model_name Aidp.log_info("thinking_depth_manager", "Selected model from catalog (alternate provider)", tier: tier, original_provider: provider, selected_provider: prov_name, model: model_name) return [prov_name, model_name, model_data] end end end # No model found for requested tier - try fallback to other tiers # Per issue #323: fallback events log at debug level Aidp.log_debug("thinking_depth_manager", "tier_not_found_trying_fallback", tier: tier, provider: provider) result = try_fallback_tiers(tier, provider) # If no model found after fallback, defer to provider auto model selection # This allows providers to select their own model when no explicit tier config exists # Per issue #323: log at debug level, don't constrain model selection if result.nil? && provider Aidp.log_debug("thinking_depth_manager", "no_model_for_tier_deferring_to_provider", requested_tier: tier, provider: provider, reason: provider_has_no_tiers ? "provider_has_no_tiers" : "tier_not_configured") return [provider, nil, {auto_model: true, reason: provider_has_no_tiers ? "provider_has_no_tiers" : "tier_not_configured"}] end unless result # This path should only be reached when no provider is specified # Enhanced error message with discovery hints display_enhanced_tier_error(tier, provider) Aidp.log_error("thinking_depth_manager", "No model found for tier or fallback tiers", tier: tier, provider: provider) end result end |
#should_escalate_on_complexity?(context) ⇒ Boolean
Check if should escalate based on complexity thresholds
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 377 def should_escalate_on_complexity?(context) thresholds = configuration.escalation_complexity_threshold return false if thresholds.empty? files_changed = context[:files_changed] || 0 modules_touched = context[:modules_touched] || 0 exceeds_threshold = false if thresholds[:files_changed] && files_changed >= thresholds[:files_changed] exceeds_threshold = true end if thresholds[:modules_touched] && modules_touched >= thresholds[:modules_touched] exceeds_threshold = true end if exceeds_threshold Aidp.log_debug("thinking_depth_manager", "Complexity check", files: files_changed, modules: modules_touched, exceeds: exceeds_threshold) end exceeds_threshold end |
#should_escalate_on_failures?(failure_count) ⇒ Boolean
Check if should escalate based on failure count
371 372 373 374 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 371 def should_escalate_on_failures?(failure_count) threshold = configuration.escalation_fail_attempts failure_count >= threshold end |
#tier_for_model(provider, model) ⇒ Object
Get tier for a specific model
299 300 301 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 299 def tier_for_model(provider, model) @registry.tier_for_model(provider, model) end |
#tier_history ⇒ Object
Get tier change history
366 367 368 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 366 def tier_history @tier_history.dup end |
#tier_info(tier) ⇒ Object
Get information about a specific tier
304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 304 def tier_info(tier) validate_tier!(tier) { tier: tier, priority: @registry.tier_priority(tier), next_tier: @registry.next_tier(tier), previous_tier: @registry.previous_tier(tier), available_models: @registry.models_by_tier(tier), at_max: tier == max_tier, at_min: @registry.previous_tier(tier).nil?, can_escalate: can_escalate_to?(tier) } end |
#tier_override_for(key) ⇒ Object
Check if tier override exists for skill/template
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/aidp/harness/thinking_depth_manager.rb', line 339 def tier_override_for(key) override = configuration.tier_override_for(key) return nil unless override validate_tier!(override) # Cap at max_tier if @registry.compare_tiers(override, max_tier) > 0 Aidp.log_warn("thinking_depth_manager", "Override tier exceeds max", key: key, override: override, max: max_tier) return max_tier end override end |