aidp 0.28.0 → 0.29.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/provider_manager.rb +5 -3
- data/lib/aidp/harness/ruby_llm_registry.rb +239 -0
- 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 +6 -4
- 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 +24 -2
- data/lib/aidp/harness/model_discovery_service.rb +0 -259
|
@@ -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
|