aidp 0.34.1 → 0.39.3
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 +4 -4
- data/README.md +14 -14
- data/lib/aidp/agent_harness_adapter.rb +309 -0
- data/lib/aidp/cli/issue_importer.rb +20 -6
- data/lib/aidp/cli/jobs_command.rb +1 -1
- data/lib/aidp/cli/models_command.rb +9 -16
- data/lib/aidp/cli/prompts_command.rb +253 -0
- data/lib/aidp/cli/security_command.rb +4 -10
- data/lib/aidp/cli/storage_command.rb +277 -0
- data/lib/aidp/cli/temporal_command.rb +460 -0
- data/lib/aidp/cli.rb +24 -2
- data/lib/aidp/config.rb +15 -0
- data/lib/aidp/config_paths.rb +12 -2
- data/lib/aidp/database/migrations.rb +114 -0
- data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
- data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
- data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
- data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
- data/lib/aidp/database/repositories/job_repository.rb +278 -0
- data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
- data/lib/aidp/database/repositories/progress_repository.rb +210 -0
- data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
- data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
- data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
- data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
- data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
- data/lib/aidp/database/repositories/task_repository.rb +231 -0
- data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
- data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
- data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
- data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
- data/lib/aidp/database/repository.rb +166 -0
- data/lib/aidp/database/schema.rb +347 -0
- data/lib/aidp/database/storage_migrator.rb +693 -0
- data/lib/aidp/database.rb +139 -0
- data/lib/aidp/debug_mixin.rb +2 -2
- data/lib/aidp/execute/checkpoint.rb +26 -0
- data/lib/aidp/execute/checkpoint_display.rb +16 -0
- data/lib/aidp/execute/prompt_evaluator.rb +30 -57
- data/lib/aidp/execute/work_loop_runner.rb +294 -46
- data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
- data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
- data/lib/aidp/harness/ai_decision_engine.rb +78 -296
- data/lib/aidp/harness/config_schema.rb +59 -0
- data/lib/aidp/harness/configuration.rb +229 -10
- data/lib/aidp/harness/deprecation_cache.rb +53 -11
- data/lib/aidp/harness/enhanced_runner.rb +1 -1
- data/lib/aidp/harness/error_handler.rb +11 -5
- data/lib/aidp/harness/provider_config.rb +36 -0
- data/lib/aidp/harness/provider_factory.rb +26 -24
- data/lib/aidp/harness/provider_info.rb +9 -5
- data/lib/aidp/harness/provider_manager.rb +2 -2
- data/lib/aidp/harness/provider_metrics.rb +103 -1
- data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
- data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
- data/lib/aidp/harness/runner.rb +7 -2
- data/lib/aidp/harness/state_manager.rb +1 -1
- data/lib/aidp/harness/test_runner.rb +167 -11
- data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
- data/lib/aidp/harness/usage_limit.rb +179 -0
- data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
- data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
- data/lib/aidp/harness/usage_period.rb +234 -0
- data/lib/aidp/init/agent_instructions_generator.rb +317 -0
- data/lib/aidp/init/runner.rb +25 -4
- data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
- data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
- data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
- data/lib/aidp/interfaces/logger_interface.rb +150 -0
- data/lib/aidp/interfaces/ui_interface.rb +254 -0
- data/lib/aidp/logger.rb +25 -7
- data/lib/aidp/orchestration_adapter.rb +279 -0
- data/lib/aidp/pr_worktree_manager.rb +4 -15
- data/lib/aidp/prompts/feedback_collector.rb +198 -0
- data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
- data/lib/aidp/prompts/template_evolver.rb +305 -0
- data/lib/aidp/prompts/template_version_manager.rb +325 -0
- data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
- data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
- data/lib/aidp/setup/wizard.rb +616 -176
- data/lib/aidp/shell_executor.rb +58 -0
- data/lib/aidp/style_guide/selector.rb +56 -1
- data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
- data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
- data/lib/aidp/temporal/activities/base_activity.rb +78 -0
- data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
- data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
- data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
- data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
- data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
- data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
- data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
- data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
- data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
- data/lib/aidp/temporal/configuration.rb +169 -0
- data/lib/aidp/temporal/connection.rb +93 -0
- data/lib/aidp/temporal/worker.rb +119 -0
- data/lib/aidp/temporal/workflow_client.rb +171 -0
- data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
- data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
- data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
- data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
- data/lib/aidp/temporal.rb +120 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/base_processor.rb +35 -0
- data/lib/aidp/watch/build_processor.rb +132 -0
- data/lib/aidp/watch/github_state_extractor.rb +0 -8
- data/lib/aidp/watch/rebase_processor.rb +245 -0
- data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
- data/lib/aidp/watch/runner.rb +366 -285
- data/lib/aidp/watch/state_store.rb +80 -0
- data/lib/aidp/watch/work_item.rb +96 -0
- data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
- data/lib/aidp/watch/worktree_reconciler.rb +643 -0
- data/lib/aidp/workflows/guided_agent.rb +1 -1
- data/lib/aidp/worktree.rb +62 -0
- data/templates/aidp.yml.example +70 -0
- metadata +117 -16
- data/lib/aidp/analyze/json_file_storage.rb +0 -292
- data/lib/aidp/execute/future_work_backlog.rb +0 -411
- data/lib/aidp/providers/adapter.rb +0 -239
- data/lib/aidp/providers/aider.rb +0 -264
- data/lib/aidp/providers/anthropic.rb +0 -614
- data/lib/aidp/providers/base.rb +0 -617
- data/lib/aidp/providers/capability_registry.rb +0 -204
- data/lib/aidp/providers/codex.rb +0 -267
- data/lib/aidp/providers/cursor.rb +0 -304
- data/lib/aidp/providers/error_taxonomy.rb +0 -195
- data/lib/aidp/providers/gemini.rb +0 -110
- data/lib/aidp/providers/github_copilot.rb +0 -227
- data/lib/aidp/providers/kilocode.rb +0 -157
- data/lib/aidp/providers/opencode.rb +0 -139
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aidp
|
|
4
|
+
module Setup
|
|
5
|
+
# Adapter that wraps in-memory configuration from the wizard
|
|
6
|
+
# to provide the same interface as Harness::Configuration.
|
|
7
|
+
#
|
|
8
|
+
# This enables AGD (AI-Generated Determinism) to work during
|
|
9
|
+
# the interactive configuration wizard before the config file
|
|
10
|
+
# is written to disk.
|
|
11
|
+
#
|
|
12
|
+
# @example Usage in wizard
|
|
13
|
+
# adapter = InMemoryConfigAdapter.new(@config, project_dir)
|
|
14
|
+
# factory = AIFilterFactory.new(adapter)
|
|
15
|
+
#
|
|
16
|
+
class InMemoryConfigAdapter
|
|
17
|
+
attr_reader :project_dir
|
|
18
|
+
|
|
19
|
+
# Initialize with wizard's in-memory config hash
|
|
20
|
+
#
|
|
21
|
+
# @param config [Hash] The wizard's @config hash (symbolized keys)
|
|
22
|
+
# @param project_dir [String] Project directory path
|
|
23
|
+
def initialize(config, project_dir)
|
|
24
|
+
@config = config || {}
|
|
25
|
+
@project_dir = project_dir
|
|
26
|
+
|
|
27
|
+
Aidp.log_debug("in_memory_config_adapter", "initialized",
|
|
28
|
+
providers: configured_providers,
|
|
29
|
+
default_provider: default_provider)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Get harness-specific configuration
|
|
33
|
+
def harness_config
|
|
34
|
+
config = @config[:harness] || {}
|
|
35
|
+
Aidp.log_debug("in_memory_config_adapter", "harness_config_retrieved",
|
|
36
|
+
keys: config.keys)
|
|
37
|
+
config
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Get provider configuration
|
|
41
|
+
def provider_config(provider_name)
|
|
42
|
+
providers = @config[:providers] || {}
|
|
43
|
+
config = providers[provider_name.to_sym] || providers[provider_name.to_s] || {}
|
|
44
|
+
Aidp.log_debug("in_memory_config_adapter", "provider_config_retrieved",
|
|
45
|
+
provider: provider_name, found: !config.empty?)
|
|
46
|
+
config
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Get all configured providers
|
|
50
|
+
def configured_providers
|
|
51
|
+
providers = @config[:providers] || {}
|
|
52
|
+
providers.keys.map(&:to_s)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get default provider
|
|
56
|
+
def default_provider
|
|
57
|
+
harness_config[:default_provider]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get fallback providers
|
|
61
|
+
def fallback_providers
|
|
62
|
+
Array(harness_config[:fallback_providers])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get provider type (usage_based, subscription, passthrough)
|
|
66
|
+
def provider_type(provider_name)
|
|
67
|
+
provider_config(provider_name)[:type] || "unknown"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get model family for a provider
|
|
71
|
+
def model_family(provider_name)
|
|
72
|
+
provider_config(provider_name)[:model_family] || "auto"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get thinking tiers configuration for a provider
|
|
76
|
+
def provider_thinking_tiers(provider_name)
|
|
77
|
+
cfg = provider_config(provider_name)
|
|
78
|
+
cfg[:thinking_tiers] || cfg["thinking_tiers"] || {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get models configured for a specific tier and provider
|
|
82
|
+
def models_for_tier(tier, provider_name)
|
|
83
|
+
unless provider_name
|
|
84
|
+
Aidp.log_debug("in_memory_config_adapter", "models_for_tier_skipped",
|
|
85
|
+
reason: "no_provider_name")
|
|
86
|
+
return []
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
tier_config = provider_thinking_tiers(provider_name)[tier] ||
|
|
90
|
+
provider_thinking_tiers(provider_name)[tier.to_sym]
|
|
91
|
+
unless tier_config
|
|
92
|
+
Aidp.log_debug("in_memory_config_adapter", "models_for_tier_not_found",
|
|
93
|
+
tier: tier, provider: provider_name)
|
|
94
|
+
return []
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
models = tier_config[:models] || tier_config["models"]
|
|
98
|
+
result = models ? Array(models).map(&:to_s).compact : []
|
|
99
|
+
Aidp.log_debug("in_memory_config_adapter", "models_for_tier_retrieved",
|
|
100
|
+
tier: tier, provider: provider_name, count: result.size)
|
|
101
|
+
result
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Get all configured tiers for a provider
|
|
105
|
+
def configured_tiers(provider_name)
|
|
106
|
+
provider_thinking_tiers(provider_name).keys.map(&:to_s)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Check if provider switching for tier is allowed
|
|
110
|
+
def allow_provider_switch_for_tier?
|
|
111
|
+
thinking_config[:allow_provider_switch] != false
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get default thinking tier
|
|
115
|
+
def default_tier
|
|
116
|
+
thinking_config[:default_tier] || "mini"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Get maximum thinking tier
|
|
120
|
+
def max_tier
|
|
121
|
+
thinking_config[:max_tier] || "pro"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Get thinking configuration
|
|
125
|
+
def thinking_config
|
|
126
|
+
@config[:thinking] || default_thinking_config
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get tier override for a skill or template
|
|
130
|
+
def tier_override_for(key)
|
|
131
|
+
thinking_overrides[key] || thinking_overrides[key.to_sym]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Get thinking tier overrides
|
|
135
|
+
def thinking_overrides
|
|
136
|
+
thinking_config[:overrides] || {}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Get permission level for a tier
|
|
140
|
+
def permission_for_tier(tier)
|
|
141
|
+
permissions = thinking_config[:permissions_by_tier] || {}
|
|
142
|
+
permissions[tier] || permissions[tier.to_sym] || "tools"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Get escalation configuration
|
|
146
|
+
def escalation_config
|
|
147
|
+
thinking_config[:escalation] || default_escalation_config
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Get fail attempts threshold for escalation
|
|
151
|
+
def escalation_fail_attempts
|
|
152
|
+
escalation_config[:on_fail_attempts] || 2
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Get complexity threshold configuration for escalation
|
|
156
|
+
def escalation_complexity_threshold
|
|
157
|
+
escalation_config[:on_complexity_threshold] || {}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Issue #375: Get maximum tier for autonomous contexts (watch mode, work loops)
|
|
161
|
+
def autonomous_max_tier
|
|
162
|
+
thinking_config[:autonomous_max_tier] || default_thinking_config[:autonomous_max_tier]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Issue #375: Get escalation configuration for autonomous contexts
|
|
166
|
+
def autonomous_escalation_config
|
|
167
|
+
thinking_config[:autonomous_escalation] || default_autonomous_escalation_config
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Issue #375: Minimum attempts per model before trying next model in tier
|
|
171
|
+
def min_attempts_per_model
|
|
172
|
+
autonomous_escalation_config[:min_attempts_per_model] || 2
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Issue #375: Minimum total attempts across all models before tier escalation
|
|
176
|
+
def min_total_attempts_before_escalation
|
|
177
|
+
autonomous_escalation_config[:min_total_attempts] || 10
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Issue #375: Whether to retry failed models after exhausting untested models
|
|
181
|
+
def retry_failed_models?
|
|
182
|
+
autonomous_escalation_config[:retry_failed_models] != false
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Get provider models configuration
|
|
186
|
+
def provider_models(provider_name)
|
|
187
|
+
provider_config(provider_name)[:models] || []
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Get default flags for a provider
|
|
191
|
+
def default_flags(provider_name)
|
|
192
|
+
provider_config(provider_name)[:default_flags] || []
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Get maximum tokens for a provider
|
|
196
|
+
def max_tokens(provider_name)
|
|
197
|
+
provider_config(provider_name)[:max_tokens]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Check if provider is configured
|
|
201
|
+
def provider_configured?(provider_name)
|
|
202
|
+
result = configured_providers.include?(provider_name.to_s)
|
|
203
|
+
Aidp.log_debug("in_memory_config_adapter", "provider_configured_check",
|
|
204
|
+
provider: provider_name, configured: result)
|
|
205
|
+
result
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Get work loop configuration
|
|
209
|
+
def work_loop_config
|
|
210
|
+
harness_config[:work_loop] || {}
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Get output filtering configuration
|
|
214
|
+
def output_filtering_config
|
|
215
|
+
work_loop_config[:output_filtering] || {}
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Get filter definitions
|
|
219
|
+
def filter_definitions
|
|
220
|
+
output_filtering_config[:filter_definitions] || {}
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Get raw configuration
|
|
224
|
+
def raw_config
|
|
225
|
+
@config.dup
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def default_thinking_config
|
|
231
|
+
{
|
|
232
|
+
default_tier: "mini",
|
|
233
|
+
max_tier: "pro",
|
|
234
|
+
autonomous_max_tier: "standard", # Issue #375: restrict autonomous operations
|
|
235
|
+
allow_provider_switch: true,
|
|
236
|
+
autonomous_escalation: default_autonomous_escalation_config
|
|
237
|
+
}
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def default_escalation_config
|
|
241
|
+
{
|
|
242
|
+
on_fail_attempts: 2,
|
|
243
|
+
on_complexity_threshold: {}
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Issue #375: Default autonomous escalation configuration
|
|
248
|
+
def default_autonomous_escalation_config
|
|
249
|
+
{
|
|
250
|
+
min_attempts_per_model: 2,
|
|
251
|
+
min_total_attempts: 10,
|
|
252
|
+
retry_failed_models: true
|
|
253
|
+
}
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aidp
|
|
4
|
+
module Setup
|
|
5
|
+
# ConfigManager-compatible wrapper for in-memory wizard configuration.
|
|
6
|
+
#
|
|
7
|
+
# This class provides the same interface as Harness::ConfigManager but
|
|
8
|
+
# reads from the wizard's in-memory @config hash instead of from disk.
|
|
9
|
+
# This enables ProviderFactory to work during the wizard before
|
|
10
|
+
# the config file is written.
|
|
11
|
+
#
|
|
12
|
+
# @example Usage
|
|
13
|
+
# config_manager = InMemoryConfigManager.new(@config, project_dir)
|
|
14
|
+
# factory = ProviderFactory.new(config_manager)
|
|
15
|
+
#
|
|
16
|
+
class InMemoryConfigManager
|
|
17
|
+
attr_reader :project_dir
|
|
18
|
+
|
|
19
|
+
# Initialize with wizard's in-memory config hash
|
|
20
|
+
#
|
|
21
|
+
# @param config [Hash] The wizard's @config hash (symbolized keys)
|
|
22
|
+
# @param project_dir [String] Project directory path
|
|
23
|
+
def initialize(config, project_dir)
|
|
24
|
+
@config = config || {}
|
|
25
|
+
@project_dir = project_dir
|
|
26
|
+
|
|
27
|
+
Aidp.log_debug("in_memory_config_manager", "initialized",
|
|
28
|
+
providers: provider_names)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get complete configuration (returns a copy to prevent mutation)
|
|
32
|
+
def config(_options = {})
|
|
33
|
+
deep_dup(@config)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Get harness configuration
|
|
37
|
+
def harness_config(_options = {})
|
|
38
|
+
@config[:harness] || {}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Get provider configuration
|
|
42
|
+
def provider_config(provider_name, _options = {})
|
|
43
|
+
providers = @config[:providers] || {}
|
|
44
|
+
config = providers[provider_name.to_sym] || providers[provider_name.to_s]
|
|
45
|
+
Aidp.log_debug("in_memory_config_manager", "provider_config_retrieved",
|
|
46
|
+
provider: provider_name, found: !config.nil?)
|
|
47
|
+
config
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get all provider configurations
|
|
51
|
+
def all_providers(_options = {})
|
|
52
|
+
@config[:providers] || {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get configured provider names
|
|
56
|
+
def provider_names(_options = {})
|
|
57
|
+
providers = all_providers
|
|
58
|
+
providers.keys.map(&:to_s)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get default provider
|
|
62
|
+
def default_provider(_options = {})
|
|
63
|
+
harness_config[:default_provider] || harness_config["default_provider"]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get fallback providers
|
|
67
|
+
def fallback_providers(_options = {})
|
|
68
|
+
fallbacks = harness_config[:fallback_providers] || harness_config["fallback_providers"] || []
|
|
69
|
+
configured = provider_names
|
|
70
|
+
fallbacks.select { |p| configured.include?(p.to_s) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Check if configuration is valid
|
|
74
|
+
def config_valid?
|
|
75
|
+
result = !!(default_provider && provider_config(default_provider))
|
|
76
|
+
Aidp.log_debug("in_memory_config_manager", "config_valid_check",
|
|
77
|
+
valid: result, default_provider: default_provider)
|
|
78
|
+
result
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get validation errors (empty for in-memory config)
|
|
82
|
+
def validation_errors
|
|
83
|
+
[]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Get validation warnings (empty for in-memory config)
|
|
87
|
+
def validation_warnings
|
|
88
|
+
[]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Reload configuration (no-op for in-memory)
|
|
92
|
+
def reload_config
|
|
93
|
+
# No-op: in-memory config doesn't need reloading
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Get configuration summary
|
|
97
|
+
def config_summary
|
|
98
|
+
{
|
|
99
|
+
providers: provider_names.size,
|
|
100
|
+
default_provider: default_provider,
|
|
101
|
+
fallback_providers: fallback_providers.size
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Get provider type
|
|
106
|
+
def provider_type(provider_name, _options = {})
|
|
107
|
+
cfg = provider_config(provider_name)
|
|
108
|
+
return nil unless cfg
|
|
109
|
+
cfg[:type] || cfg["type"]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Get provider models
|
|
113
|
+
def provider_models(provider_name, _options = {})
|
|
114
|
+
cfg = provider_config(provider_name)
|
|
115
|
+
return [] unless cfg
|
|
116
|
+
models = cfg[:models] || cfg["models"] || []
|
|
117
|
+
models.map(&:to_s)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Get provider features
|
|
121
|
+
def provider_features(provider_name, _options = {})
|
|
122
|
+
cfg = provider_config(provider_name)
|
|
123
|
+
return {} unless cfg
|
|
124
|
+
features = cfg[:features] || cfg["features"] || {}
|
|
125
|
+
{
|
|
126
|
+
file_upload: features[:file_upload] == true,
|
|
127
|
+
code_generation: features[:code_generation] != false,
|
|
128
|
+
analysis: features[:analysis] != false,
|
|
129
|
+
vision: features[:vision] == true
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check if provider supports feature
|
|
134
|
+
def provider_supports_feature?(provider_name, feature, options = {})
|
|
135
|
+
features = provider_features(provider_name, options)
|
|
136
|
+
result = features[feature.to_sym] == true
|
|
137
|
+
Aidp.log_debug("in_memory_config_manager", "feature_support_check",
|
|
138
|
+
provider: provider_name, feature: feature, supported: result)
|
|
139
|
+
result
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Get provider priority
|
|
143
|
+
def provider_priority(provider_name, _options = {})
|
|
144
|
+
cfg = provider_config(provider_name)
|
|
145
|
+
return 1 unless cfg
|
|
146
|
+
cfg[:priority] || cfg["priority"] || 1
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Get retry configuration
|
|
150
|
+
def retry_config(_options = {})
|
|
151
|
+
cfg = harness_config[:retry] || {}
|
|
152
|
+
{
|
|
153
|
+
enabled: cfg[:enabled] != false,
|
|
154
|
+
max_attempts: cfg[:max_attempts] || 3,
|
|
155
|
+
base_delay: cfg[:base_delay] || 1.0,
|
|
156
|
+
max_delay: cfg[:max_delay] || 60.0,
|
|
157
|
+
exponential_base: cfg[:exponential_base] || 2.0,
|
|
158
|
+
jitter: cfg[:jitter] != false
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Get circuit breaker configuration
|
|
163
|
+
def circuit_breaker_config(_options = {})
|
|
164
|
+
cfg = harness_config[:circuit_breaker] || {}
|
|
165
|
+
{
|
|
166
|
+
enabled: cfg[:enabled] != false,
|
|
167
|
+
failure_threshold: cfg[:failure_threshold] || 5,
|
|
168
|
+
timeout: cfg[:timeout] || 300,
|
|
169
|
+
half_open_max_calls: cfg[:half_open_max_calls] || 3
|
|
170
|
+
}
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Get rate limit configuration
|
|
174
|
+
def rate_limit_config(_options = {})
|
|
175
|
+
cfg = harness_config[:rate_limit] || {}
|
|
176
|
+
{
|
|
177
|
+
enabled: cfg[:enabled] != false,
|
|
178
|
+
default_reset_time: cfg[:default_reset_time] || 3600,
|
|
179
|
+
burst_limit: cfg[:burst_limit] || 10,
|
|
180
|
+
sustained_limit: cfg[:sustained_limit] || 5
|
|
181
|
+
}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Get provider monitoring configuration
|
|
185
|
+
def provider_monitoring_config(provider_name, _options = {})
|
|
186
|
+
cfg = provider_config(provider_name)
|
|
187
|
+
return {} unless cfg
|
|
188
|
+
monitoring = cfg[:monitoring] || cfg["monitoring"] || {}
|
|
189
|
+
{
|
|
190
|
+
enabled: monitoring[:enabled] != false,
|
|
191
|
+
metrics_interval: monitoring[:metrics_interval] || 60
|
|
192
|
+
}
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Get provider model weights
|
|
196
|
+
def provider_model_weights(provider_name, _options = {})
|
|
197
|
+
cfg = provider_config(provider_name)
|
|
198
|
+
return {} unless cfg
|
|
199
|
+
weights = cfg[:model_weights] || cfg["model_weights"] || {}
|
|
200
|
+
weights.transform_values { |w| [w.to_i, 1].max }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Get provider max tokens
|
|
204
|
+
def provider_max_tokens(provider_name, _options = {})
|
|
205
|
+
cfg = provider_config(provider_name)
|
|
206
|
+
return nil unless cfg
|
|
207
|
+
cfg[:max_tokens] || cfg["max_tokens"]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Get provider default flags
|
|
211
|
+
def provider_default_flags(provider_name, _options = {})
|
|
212
|
+
cfg = provider_config(provider_name)
|
|
213
|
+
return [] unless cfg
|
|
214
|
+
flags = cfg[:default_flags] || cfg["default_flags"] || []
|
|
215
|
+
flags.map(&:to_s)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Get provider auth configuration (redacts sensitive values)
|
|
219
|
+
def provider_auth_config(provider_name, _options = {})
|
|
220
|
+
cfg = provider_config(provider_name)
|
|
221
|
+
return {} unless cfg
|
|
222
|
+
auth = cfg[:auth] || cfg["auth"] || {}
|
|
223
|
+
{
|
|
224
|
+
# Only expose the env var name, not actual keys
|
|
225
|
+
api_key_env: auth[:api_key_env] || auth["api_key_env"],
|
|
226
|
+
# Redact actual API key - callers should use api_key_env
|
|
227
|
+
api_key: (auth[:api_key] || auth["api_key"]) ? "[REDACTED]" : nil
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Get provider endpoints
|
|
232
|
+
def provider_endpoints(provider_name, _options = {})
|
|
233
|
+
cfg = provider_config(provider_name)
|
|
234
|
+
return {} unless cfg
|
|
235
|
+
endpoints = cfg[:endpoints] || cfg["endpoints"] || {}
|
|
236
|
+
{
|
|
237
|
+
default: endpoints[:default] || endpoints["default"]
|
|
238
|
+
}
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
private
|
|
242
|
+
|
|
243
|
+
# Deep duplicate a hash/array structure to prevent mutation
|
|
244
|
+
def deep_dup(obj)
|
|
245
|
+
case obj
|
|
246
|
+
when Hash
|
|
247
|
+
obj.each_with_object({}) { |(k, v), h| h[k] = deep_dup(v) }
|
|
248
|
+
when Array
|
|
249
|
+
obj.map { |v| deep_dup(v) }
|
|
250
|
+
else
|
|
251
|
+
begin
|
|
252
|
+
obj.dup
|
|
253
|
+
rescue TypeError => e
|
|
254
|
+
Aidp.log_debug("in_memory_config_manager", "deep_dup_type_error",
|
|
255
|
+
object_class: obj.class.name, error: e.message)
|
|
256
|
+
obj
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|