aidp 0.28.0 → 0.30.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.
- checksums.yaml +4 -4
- data/lib/aidp/cli/models_command.rb +75 -117
- data/lib/aidp/cli/tools_command.rb +333 -0
- data/lib/aidp/cli.rb +8 -1
- data/lib/aidp/config.rb +9 -0
- data/lib/aidp/execute/work_loop_runner.rb +6 -3
- data/lib/aidp/harness/capability_registry.rb +4 -4
- data/lib/aidp/harness/deprecation_cache.rb +177 -0
- data/lib/aidp/harness/provider_manager.rb +5 -3
- data/lib/aidp/harness/ruby_llm_registry.rb +327 -0
- data/lib/aidp/harness/thinking_depth_manager.rb +47 -5
- data/lib/aidp/metadata/cache.rb +201 -0
- data/lib/aidp/metadata/compiler.rb +229 -0
- data/lib/aidp/metadata/parser.rb +204 -0
- data/lib/aidp/metadata/query.rb +237 -0
- data/lib/aidp/metadata/scanner.rb +191 -0
- data/lib/aidp/metadata/tool_metadata.rb +245 -0
- data/lib/aidp/metadata/validator.rb +187 -0
- data/lib/aidp/providers/aider.rb +1 -1
- data/lib/aidp/providers/anthropic.rb +189 -13
- data/lib/aidp/providers/base.rb +105 -35
- data/lib/aidp/providers/codex.rb +1 -1
- data/lib/aidp/providers/cursor.rb +1 -1
- data/lib/aidp/providers/gemini.rb +1 -1
- data/lib/aidp/providers/github_copilot.rb +1 -1
- data/lib/aidp/providers/kilocode.rb +1 -1
- data/lib/aidp/providers/opencode.rb +1 -1
- data/lib/aidp/setup/wizard.rb +35 -107
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +11 -0
- data/templates/implementation/implement_features.md +4 -1
- metadata +25 -2
- data/lib/aidp/harness/model_discovery_service.rb +0 -259
data/lib/aidp/setup/wizard.rb
CHANGED
|
@@ -59,9 +59,6 @@ module Aidp
|
|
|
59
59
|
configure_modes
|
|
60
60
|
configure_devcontainer
|
|
61
61
|
|
|
62
|
-
# Finalize any background model discovery
|
|
63
|
-
finalize_background_discovery
|
|
64
|
-
|
|
65
62
|
yaml_content = generate_yaml
|
|
66
63
|
display_preview(yaml_content)
|
|
67
64
|
display_diff(yaml_content) if @existing_config.any?
|
|
@@ -355,110 +352,14 @@ module Aidp
|
|
|
355
352
|
end
|
|
356
353
|
end
|
|
357
354
|
|
|
358
|
-
# Trigger background model discovery for a provider
|
|
359
|
-
# Runs asynchronously and caches results without blocking the wizard
|
|
360
|
-
def trigger_background_discovery(provider_name)
|
|
361
|
-
return unless provider_available_for_discovery?(provider_name)
|
|
362
|
-
|
|
363
|
-
# Store reference to display notification later
|
|
364
|
-
@discovery_threads ||= []
|
|
365
|
-
|
|
366
|
-
thread = Thread.new do
|
|
367
|
-
discover_and_cache_models(provider_name)
|
|
368
|
-
rescue => e
|
|
369
|
-
Aidp.log_debug("setup_wizard", "background discovery failed",
|
|
370
|
-
provider: provider_name, error: e.message)
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
@discovery_threads << {thread: thread, provider: provider_name}
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Check if provider CLI is available for discovery
|
|
377
|
-
def provider_available_for_discovery?(provider_name)
|
|
378
|
-
provider_class = get_provider_class(provider_name)
|
|
379
|
-
return false unless provider_class
|
|
380
|
-
|
|
381
|
-
provider_class.respond_to?(:available?) && provider_class.available?
|
|
382
|
-
rescue => e
|
|
383
|
-
Aidp.log_debug("setup_wizard", "provider availability check failed",
|
|
384
|
-
provider: provider_name, error: e.message)
|
|
385
|
-
false
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
# Perform model discovery and cache results
|
|
389
|
-
def discover_and_cache_models(provider_name)
|
|
390
|
-
require_relative "../harness/model_discovery_service"
|
|
391
|
-
|
|
392
|
-
service = Aidp::Harness::ModelDiscoveryService.new
|
|
393
|
-
models = service.discover_models(provider_name, use_cache: false)
|
|
394
|
-
|
|
395
|
-
if models.any?
|
|
396
|
-
Aidp.log_info("setup_wizard", "discovered models in background",
|
|
397
|
-
provider: provider_name, count: models.size)
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
models
|
|
401
|
-
rescue => e
|
|
402
|
-
Aidp.log_debug("setup_wizard", "background discovery failed",
|
|
403
|
-
provider: provider_name, error: e.message)
|
|
404
|
-
[]
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
# Get provider class for discovery
|
|
408
|
-
def get_provider_class(provider_name)
|
|
409
|
-
class_name = "Aidp::Providers::#{provider_name.capitalize}"
|
|
410
|
-
Object.const_get(class_name)
|
|
411
|
-
rescue NameError
|
|
412
|
-
nil
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
# Wait for background discovery to complete and show notifications
|
|
416
|
-
#
|
|
417
|
-
# @param timeout [Numeric] Maximum seconds to wait per thread (default: 5)
|
|
418
|
-
def finalize_background_discovery(timeout: 5)
|
|
419
|
-
return unless @discovery_threads&.any?
|
|
420
|
-
|
|
421
|
-
@discovery_threads.each do |entry|
|
|
422
|
-
thread = entry[:thread]
|
|
423
|
-
provider = entry[:provider]
|
|
424
|
-
|
|
425
|
-
# Wait up to timeout seconds for discovery to complete
|
|
426
|
-
thread.join(timeout)
|
|
427
|
-
|
|
428
|
-
if thread.alive?
|
|
429
|
-
Aidp.log_debug("setup_wizard", "discovery timeout, killing thread",
|
|
430
|
-
provider: provider)
|
|
431
|
-
# Kill thread to prevent hanging
|
|
432
|
-
thread.kill
|
|
433
|
-
thread.join(0.1) # Brief wait for cleanup
|
|
434
|
-
else
|
|
435
|
-
# Discovery completed - show notification
|
|
436
|
-
begin
|
|
437
|
-
require_relative "../harness/model_cache"
|
|
438
|
-
cache = Aidp::Harness::ModelCache.new
|
|
439
|
-
cached_models = cache.get_cached_models(provider)
|
|
440
|
-
|
|
441
|
-
if cached_models&.any?
|
|
442
|
-
prompt.say(" 💾 Discovered #{cached_models.size} model#{"s" unless cached_models.size == 1} for #{provider}")
|
|
443
|
-
end
|
|
444
|
-
rescue => e
|
|
445
|
-
Aidp.log_debug("setup_wizard", "failed to check cached models",
|
|
446
|
-
provider: provider, error: e.message)
|
|
447
|
-
end
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
@discovery_threads = []
|
|
452
|
-
end
|
|
453
|
-
|
|
454
355
|
def discover_models_for_providers(providers)
|
|
455
|
-
require_relative "../harness/
|
|
356
|
+
require_relative "../harness/ruby_llm_registry"
|
|
456
357
|
|
|
457
|
-
|
|
358
|
+
registry = Aidp::Harness::RubyLLMRegistry.new
|
|
458
359
|
all_models = {}
|
|
459
360
|
|
|
460
361
|
providers.each do |provider|
|
|
461
|
-
models =
|
|
362
|
+
models = discover_models_from_registry(provider, registry)
|
|
462
363
|
all_models[provider] = models if models.any?
|
|
463
364
|
rescue => e
|
|
464
365
|
Aidp.log_debug("setup_wizard", "discovery failed", provider: provider, error: e.message)
|
|
@@ -468,6 +369,30 @@ module Aidp
|
|
|
468
369
|
all_models
|
|
469
370
|
end
|
|
470
371
|
|
|
372
|
+
# Discover models for a provider from RubyLLM registry
|
|
373
|
+
#
|
|
374
|
+
# @param provider [String] Provider name (e.g., "anthropic")
|
|
375
|
+
# @param registry [RubyLLMRegistry] Registry instance
|
|
376
|
+
# @return [Array<Hash>] Models with tier classification
|
|
377
|
+
def discover_models_from_registry(provider, registry)
|
|
378
|
+
model_ids = registry.models_for_provider(provider)
|
|
379
|
+
|
|
380
|
+
# Convert to model info hashes with tier classification
|
|
381
|
+
model_ids.map do |model_id|
|
|
382
|
+
info = registry.get_model_info(model_id)
|
|
383
|
+
{
|
|
384
|
+
name: model_id,
|
|
385
|
+
tier: info[:tier],
|
|
386
|
+
context_window: info[:context_window],
|
|
387
|
+
capabilities: info[:capabilities]
|
|
388
|
+
}
|
|
389
|
+
end
|
|
390
|
+
rescue => e
|
|
391
|
+
Aidp.log_error("setup_wizard", "failed to discover models from registry",
|
|
392
|
+
provider: provider, error: e.message)
|
|
393
|
+
[]
|
|
394
|
+
end
|
|
395
|
+
|
|
471
396
|
def display_discovered_models(discovered_models)
|
|
472
397
|
discovered_models.each do |provider, models|
|
|
473
398
|
prompt.say("\n✓ Found #{models.size} models for #{provider}:")
|
|
@@ -510,13 +435,19 @@ module Aidp
|
|
|
510
435
|
def find_model_for_tier(models, target_tier)
|
|
511
436
|
return nil unless models
|
|
512
437
|
|
|
513
|
-
|
|
438
|
+
# Map pro -> advanced for registry compatibility
|
|
439
|
+
registry_tier = (target_tier == "pro") ? "advanced" : target_tier
|
|
440
|
+
|
|
441
|
+
models.find { |m| m[:tier] == registry_tier }
|
|
514
442
|
end
|
|
515
443
|
|
|
516
444
|
def find_models_for_tier(models, target_tier)
|
|
517
445
|
return [] unless models
|
|
518
446
|
|
|
519
|
-
|
|
447
|
+
# Map pro -> advanced for registry compatibility
|
|
448
|
+
registry_tier = (target_tier == "pro") ? "advanced" : target_tier
|
|
449
|
+
|
|
450
|
+
models.select { |m| m[:tier] == registry_tier }
|
|
520
451
|
end
|
|
521
452
|
|
|
522
453
|
def display_provider_tier_preview(provider_configs)
|
|
@@ -1717,9 +1648,6 @@ module Aidp
|
|
|
1717
1648
|
# Enhance messaging with display name when available
|
|
1718
1649
|
display_name = discover_available_providers.invert.fetch(provider_name, provider_name)
|
|
1719
1650
|
prompt.say(" • #{action_word.capitalize} provider '#{display_name}' (#{provider_name}) with billing type '#{provider_type}' and model family '#{model_family}'")
|
|
1720
|
-
|
|
1721
|
-
# Trigger background model discovery for newly added/updated provider
|
|
1722
|
-
trigger_background_discovery(provider_name) unless @dry_run
|
|
1723
1651
|
end
|
|
1724
1652
|
|
|
1725
1653
|
def edit_or_remove_provider(provider_name, primary_provider, fallbacks)
|
data/lib/aidp/version.rb
CHANGED
data/lib/aidp.rb
CHANGED
|
@@ -49,6 +49,15 @@ require_relative "aidp/analyze/seams"
|
|
|
49
49
|
require_relative "aidp/analyze/tree_sitter_scan"
|
|
50
50
|
require_relative "aidp/analyze/kb_inspector"
|
|
51
51
|
|
|
52
|
+
# Metadata system
|
|
53
|
+
require_relative "aidp/metadata/tool_metadata"
|
|
54
|
+
require_relative "aidp/metadata/validator"
|
|
55
|
+
require_relative "aidp/metadata/parser"
|
|
56
|
+
require_relative "aidp/metadata/scanner"
|
|
57
|
+
require_relative "aidp/metadata/compiler"
|
|
58
|
+
require_relative "aidp/metadata/query"
|
|
59
|
+
require_relative "aidp/metadata/cache"
|
|
60
|
+
|
|
52
61
|
# Workflows
|
|
53
62
|
require_relative "aidp/workflows/definitions"
|
|
54
63
|
require_relative "aidp/workflows/selector"
|
|
@@ -83,6 +92,8 @@ require_relative "aidp/harness/config_schema"
|
|
|
83
92
|
require_relative "aidp/harness/config_validator"
|
|
84
93
|
require_relative "aidp/harness/config_loader"
|
|
85
94
|
require_relative "aidp/harness/config_manager"
|
|
95
|
+
require_relative "aidp/harness/ruby_llm_registry"
|
|
96
|
+
require_relative "aidp/harness/model_registry"
|
|
86
97
|
require_relative "aidp/harness/condition_detector"
|
|
87
98
|
require_relative "aidp/harness/user_interface"
|
|
88
99
|
require_relative "aidp/harness/simple_user_interface"
|
|
@@ -23,7 +23,10 @@ You are a senior engineer writing **implementation guidance** for domain agents.
|
|
|
23
23
|
|
|
24
24
|
## Output
|
|
25
25
|
|
|
26
|
-
- `docs
|
|
26
|
+
- `docs/<FEATURE_NAME>_IMPLEMENTATION.md` with examples and a pattern-to-use-case matrix.
|
|
27
|
+
- Use a descriptive name based on the feature/issue (e.g., `METADATA_TOOL_DISCOVERY.md`, `WORKTREE_PR_CHANGE_REQUESTS.md`)
|
|
28
|
+
- Do NOT use generic names like `ImplementationGuide.md`, `IMPLEMENTATION_SUMMARY.md`, or `SESSION_SUMMARY.md`
|
|
29
|
+
- Do NOT create files at the project root
|
|
27
30
|
|
|
28
31
|
## Regeneration Policy
|
|
29
32
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aidp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.30.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bart Agapinan
|
|
@@ -219,6 +219,20 @@ dependencies:
|
|
|
219
219
|
- - "~>"
|
|
220
220
|
- !ruby/object:Gem::Version
|
|
221
221
|
version: '0.10'
|
|
222
|
+
- !ruby/object:Gem::Dependency
|
|
223
|
+
name: ruby_llm
|
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
|
225
|
+
requirements:
|
|
226
|
+
- - "~>"
|
|
227
|
+
- !ruby/object:Gem::Version
|
|
228
|
+
version: '1.9'
|
|
229
|
+
type: :runtime
|
|
230
|
+
prerelease: false
|
|
231
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
232
|
+
requirements:
|
|
233
|
+
- - "~>"
|
|
234
|
+
- !ruby/object:Gem::Version
|
|
235
|
+
version: '1.9'
|
|
222
236
|
description: The AI-Dev-Pipeline (AIDP) CLI provides a powerful, markdown-driven workflow
|
|
223
237
|
for software development. It supports in-depth project analysis to understand existing
|
|
224
238
|
codebases and an execution mode to systematically implement new features.
|
|
@@ -269,6 +283,7 @@ files:
|
|
|
269
283
|
- lib/aidp/cli/models_command.rb
|
|
270
284
|
- lib/aidp/cli/providers_command.rb
|
|
271
285
|
- lib/aidp/cli/terminal_io.rb
|
|
286
|
+
- lib/aidp/cli/tools_command.rb
|
|
272
287
|
- lib/aidp/concurrency.rb
|
|
273
288
|
- lib/aidp/concurrency/backoff.rb
|
|
274
289
|
- lib/aidp/concurrency/exec.rb
|
|
@@ -309,12 +324,12 @@ files:
|
|
|
309
324
|
- lib/aidp/harness/config_schema.rb
|
|
310
325
|
- lib/aidp/harness/config_validator.rb
|
|
311
326
|
- lib/aidp/harness/configuration.rb
|
|
327
|
+
- lib/aidp/harness/deprecation_cache.rb
|
|
312
328
|
- lib/aidp/harness/enhanced_runner.rb
|
|
313
329
|
- lib/aidp/harness/error_handler.rb
|
|
314
330
|
- lib/aidp/harness/filter_strategy.rb
|
|
315
331
|
- lib/aidp/harness/generic_filter_strategy.rb
|
|
316
332
|
- lib/aidp/harness/model_cache.rb
|
|
317
|
-
- lib/aidp/harness/model_discovery_service.rb
|
|
318
333
|
- lib/aidp/harness/model_registry.rb
|
|
319
334
|
- lib/aidp/harness/output_filter.rb
|
|
320
335
|
- lib/aidp/harness/provider_config.rb
|
|
@@ -324,6 +339,7 @@ files:
|
|
|
324
339
|
- lib/aidp/harness/provider_metrics.rb
|
|
325
340
|
- lib/aidp/harness/provider_type_checker.rb
|
|
326
341
|
- lib/aidp/harness/rspec_filter_strategy.rb
|
|
342
|
+
- lib/aidp/harness/ruby_llm_registry.rb
|
|
327
343
|
- lib/aidp/harness/runner.rb
|
|
328
344
|
- lib/aidp/harness/simple_user_interface.rb
|
|
329
345
|
- lib/aidp/harness/state/errors.rb
|
|
@@ -365,6 +381,13 @@ files:
|
|
|
365
381
|
- lib/aidp/jobs/background_runner.rb
|
|
366
382
|
- lib/aidp/logger.rb
|
|
367
383
|
- lib/aidp/message_display.rb
|
|
384
|
+
- lib/aidp/metadata/cache.rb
|
|
385
|
+
- lib/aidp/metadata/compiler.rb
|
|
386
|
+
- lib/aidp/metadata/parser.rb
|
|
387
|
+
- lib/aidp/metadata/query.rb
|
|
388
|
+
- lib/aidp/metadata/scanner.rb
|
|
389
|
+
- lib/aidp/metadata/tool_metadata.rb
|
|
390
|
+
- lib/aidp/metadata/validator.rb
|
|
368
391
|
- lib/aidp/planning/analyzers/feedback_analyzer.rb
|
|
369
392
|
- lib/aidp/planning/builders/agile_plan_builder.rb
|
|
370
393
|
- lib/aidp/planning/builders/project_plan_builder.rb
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "model_cache"
|
|
4
|
-
require_relative "model_registry"
|
|
5
|
-
|
|
6
|
-
module Aidp
|
|
7
|
-
module Harness
|
|
8
|
-
# Service for discovering available models from providers
|
|
9
|
-
#
|
|
10
|
-
# Orchestrates model discovery across multiple providers:
|
|
11
|
-
# 1. Checks cache first (with TTL)
|
|
12
|
-
# 2. Falls back to dynamic discovery via provider.discover_models
|
|
13
|
-
# 3. Merges with static registry for comprehensive results
|
|
14
|
-
# 4. Caches results for future use
|
|
15
|
-
#
|
|
16
|
-
# Usage:
|
|
17
|
-
# service = ModelDiscoveryService.new
|
|
18
|
-
# models = service.discover_models("anthropic")
|
|
19
|
-
# all_models = service.discover_all_models
|
|
20
|
-
class ModelDiscoveryService
|
|
21
|
-
attr_reader :cache, :registry
|
|
22
|
-
|
|
23
|
-
def initialize(cache: nil, registry: nil)
|
|
24
|
-
@cache = cache || ModelCache.new
|
|
25
|
-
@registry = registry || ModelRegistry.new
|
|
26
|
-
@provider_classes = discover_provider_classes
|
|
27
|
-
Aidp.log_debug("model_discovery_service", "initialized",
|
|
28
|
-
providers: @provider_classes.keys)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Discover models for a specific provider
|
|
32
|
-
#
|
|
33
|
-
# @param provider [String] Provider name (e.g., "anthropic", "cursor")
|
|
34
|
-
# @param use_cache [Boolean] Whether to use cached results (default: true)
|
|
35
|
-
# @return [Array<Hash>] Discovered models
|
|
36
|
-
def discover_models(provider, use_cache: true)
|
|
37
|
-
Aidp.log_info("model_discovery_service", "discovering models",
|
|
38
|
-
provider: provider, use_cache: use_cache)
|
|
39
|
-
|
|
40
|
-
# Check cache first
|
|
41
|
-
if use_cache
|
|
42
|
-
cached = @cache.get_cached_models(provider)
|
|
43
|
-
if cached
|
|
44
|
-
Aidp.log_debug("model_discovery_service", "using cached models",
|
|
45
|
-
provider: provider, count: cached.size)
|
|
46
|
-
return cached
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Perform discovery
|
|
51
|
-
models = perform_discovery(provider)
|
|
52
|
-
|
|
53
|
-
# Cache the results
|
|
54
|
-
@cache.cache_models(provider, models) if models.any?
|
|
55
|
-
|
|
56
|
-
models
|
|
57
|
-
rescue => e
|
|
58
|
-
Aidp.log_error("model_discovery_service", "discovery failed",
|
|
59
|
-
provider: provider, error: e.message, backtrace: e.backtrace.first(3))
|
|
60
|
-
[]
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Discover models from all available providers
|
|
64
|
-
#
|
|
65
|
-
# @param use_cache [Boolean] Whether to use cached results
|
|
66
|
-
# @return [Hash] Hash of provider => models array
|
|
67
|
-
def discover_all_models(use_cache: true)
|
|
68
|
-
results = {}
|
|
69
|
-
|
|
70
|
-
@provider_classes.each_key do |provider|
|
|
71
|
-
models = discover_models(provider, use_cache: use_cache)
|
|
72
|
-
results[provider] = models if models.any?
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
Aidp.log_info("model_discovery_service", "discovered all models",
|
|
76
|
-
providers: results.keys, total_models: results.values.flatten.size)
|
|
77
|
-
results
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Discover models concurrently from multiple providers
|
|
81
|
-
#
|
|
82
|
-
# @param providers [Array<String>] List of provider names
|
|
83
|
-
# @param use_cache [Boolean] Whether to use cached results
|
|
84
|
-
# @return [Hash] Hash of provider => models array
|
|
85
|
-
def discover_concurrent(providers, use_cache: true)
|
|
86
|
-
require "concurrent"
|
|
87
|
-
|
|
88
|
-
results = {}
|
|
89
|
-
mutex = Mutex.new
|
|
90
|
-
|
|
91
|
-
# Create a thread pool
|
|
92
|
-
pool = Concurrent::FixedThreadPool.new(providers.size)
|
|
93
|
-
|
|
94
|
-
# Submit discovery tasks
|
|
95
|
-
futures = providers.map do |provider|
|
|
96
|
-
Concurrent::Future.execute(executor: pool) do
|
|
97
|
-
models = discover_models(provider, use_cache: use_cache)
|
|
98
|
-
mutex.synchronize { results[provider] = models }
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Wait for all to complete
|
|
103
|
-
futures.each(&:wait)
|
|
104
|
-
|
|
105
|
-
pool.shutdown
|
|
106
|
-
pool.wait_for_termination(30)
|
|
107
|
-
|
|
108
|
-
Aidp.log_info("model_discovery_service", "concurrent discovery complete",
|
|
109
|
-
providers: results.keys, total_models: results.values.flatten.size)
|
|
110
|
-
results
|
|
111
|
-
rescue LoadError => e
|
|
112
|
-
# Fallback to sequential if concurrent gem not available
|
|
113
|
-
Aidp.log_warn("model_discovery_service", "concurrent gem not available, using sequential",
|
|
114
|
-
error: e.message)
|
|
115
|
-
providers.each_with_object({}) do |provider, hash|
|
|
116
|
-
hash[provider] = discover_models(provider, use_cache: use_cache)
|
|
117
|
-
end
|
|
118
|
-
rescue => e
|
|
119
|
-
Aidp.log_error("model_discovery_service", "concurrent discovery failed",
|
|
120
|
-
error: e.message)
|
|
121
|
-
{}
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Get all available models (discovery + static registry)
|
|
125
|
-
#
|
|
126
|
-
# Combines dynamically discovered models with static registry
|
|
127
|
-
#
|
|
128
|
-
# @param use_cache [Boolean] Whether to use cached results
|
|
129
|
-
# @return [Hash] Hash with :discovered and :registry keys
|
|
130
|
-
def all_available_models(use_cache: true)
|
|
131
|
-
discovered = discover_all_models(use_cache: use_cache)
|
|
132
|
-
registry_families = @registry.all_families
|
|
133
|
-
|
|
134
|
-
{
|
|
135
|
-
discovered: discovered,
|
|
136
|
-
registry: registry_families.map { |family| @registry.get_model_info(family) }.compact
|
|
137
|
-
}
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Find which providers support a given model family
|
|
141
|
-
#
|
|
142
|
-
# @param family_name [String] Model family name
|
|
143
|
-
# @return [Array<String>] List of provider names
|
|
144
|
-
def providers_supporting(family_name)
|
|
145
|
-
providers = []
|
|
146
|
-
|
|
147
|
-
@provider_classes.each do |provider_name, class_name|
|
|
148
|
-
provider_class = constantize_provider(class_name)
|
|
149
|
-
next unless provider_class
|
|
150
|
-
|
|
151
|
-
if provider_class.respond_to?(:supports_model_family?)
|
|
152
|
-
providers << provider_name if provider_class.supports_model_family?(family_name)
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
providers
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Refresh cache for all providers
|
|
160
|
-
def refresh_all_caches
|
|
161
|
-
@cache.invalidate_all
|
|
162
|
-
discover_all_models(use_cache: false)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Refresh cache for specific provider
|
|
166
|
-
#
|
|
167
|
-
# @param provider [String] Provider name
|
|
168
|
-
def refresh_cache(provider)
|
|
169
|
-
@cache.invalidate(provider)
|
|
170
|
-
discover_models(provider, use_cache: false)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
private
|
|
174
|
-
|
|
175
|
-
def perform_discovery(provider)
|
|
176
|
-
provider_class = get_provider_class(provider)
|
|
177
|
-
unless provider_class
|
|
178
|
-
Aidp.log_warn("model_discovery_service", "unknown provider",
|
|
179
|
-
provider: provider)
|
|
180
|
-
return []
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
unless provider_class.respond_to?(:available?) && provider_class.available?
|
|
184
|
-
Aidp.log_debug("model_discovery_service", "provider not available",
|
|
185
|
-
provider: provider)
|
|
186
|
-
return []
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
unless provider_class.respond_to?(:discover_models)
|
|
190
|
-
Aidp.log_warn("model_discovery_service", "provider missing discover_models",
|
|
191
|
-
provider: provider)
|
|
192
|
-
return []
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
models = provider_class.discover_models
|
|
196
|
-
Aidp.log_info("model_discovery_service", "discovered models",
|
|
197
|
-
provider: provider, count: models.size)
|
|
198
|
-
models
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def get_provider_class(provider)
|
|
202
|
-
class_name = @provider_classes[provider]
|
|
203
|
-
return nil unless class_name
|
|
204
|
-
|
|
205
|
-
constantize_provider(class_name)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def constantize_provider(class_name)
|
|
209
|
-
# Safely constantize the provider class
|
|
210
|
-
parts = class_name.split("::")
|
|
211
|
-
parts.reduce(Object) { |mod, name| mod.const_get(name) }
|
|
212
|
-
rescue NameError => e
|
|
213
|
-
Aidp.log_debug("model_discovery_service", "provider class not found",
|
|
214
|
-
class: class_name, error: e.message)
|
|
215
|
-
nil
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Dynamically discover all provider classes from the providers directory
|
|
219
|
-
#
|
|
220
|
-
# @return [Hash] Hash of provider_name => class_name
|
|
221
|
-
def discover_provider_classes
|
|
222
|
-
providers_dir = File.join(__dir__, "../providers")
|
|
223
|
-
provider_files = Dir.glob("*.rb", base: providers_dir)
|
|
224
|
-
|
|
225
|
-
# Exclude base classes and utility files
|
|
226
|
-
excluded_files = ["base.rb", "adapter.rb", "error_taxonomy.rb", "capability_registry.rb"]
|
|
227
|
-
provider_files -= excluded_files
|
|
228
|
-
|
|
229
|
-
providers = {}
|
|
230
|
-
|
|
231
|
-
provider_files.each do |file|
|
|
232
|
-
provider_name = File.basename(file, ".rb")
|
|
233
|
-
# Convert to class name (e.g., "anthropic" -> "Anthropic", "github_copilot" -> "GithubCopilot")
|
|
234
|
-
class_name = provider_name.split("_").map(&:capitalize).join
|
|
235
|
-
full_class_name = "Aidp::Providers::#{class_name}"
|
|
236
|
-
|
|
237
|
-
# Try to load and verify the provider class exists
|
|
238
|
-
begin
|
|
239
|
-
require_relative "../providers/#{provider_name}"
|
|
240
|
-
provider_class = constantize_provider(full_class_name)
|
|
241
|
-
if provider_class&.respond_to?(:discover_models)
|
|
242
|
-
providers[provider_name] = full_class_name
|
|
243
|
-
end
|
|
244
|
-
rescue => e
|
|
245
|
-
# Skip providers that can't be loaded or don't implement discover_models
|
|
246
|
-
if ENV["DEBUG"]
|
|
247
|
-
Aidp.log_debug("model_discovery_service", "skipping provider",
|
|
248
|
-
provider: provider_name, reason: e.message)
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
Aidp.log_debug("model_discovery_service", "discovered provider classes",
|
|
254
|
-
count: providers.size, providers: providers.keys)
|
|
255
|
-
providers
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
end
|