Class: Aidp::Harness::RubyLLMRegistry
- Inherits:
-
Object
- Object
- Aidp::Harness::RubyLLMRegistry
- Defined in:
- lib/aidp/harness/ruby_llm_registry.rb
Overview
RubyLLMRegistry wraps the ruby_llm gem's model registry to provide AIDP-specific functionality while leveraging ruby_llm's comprehensive and actively maintained model database
Defined Under Namespace
Classes: ModelNotFound, RegistryError
Constant Summary collapse
- PROVIDER_NAME_MAPPING =
Map AIDP provider names to RubyLLM provider names Some AIDP providers use different names than the upstream APIs
{ "codex" => "openai", # Codex is AIDP's OpenAI adapter "anthropic" => "anthropic", "gemini" => "gemini", # Gemini provider name matches "aider" => nil, # Aider aggregates multiple providers "cursor" => nil, # Cursor has its own models "openai" => "openai", "google" => "gemini", # Google's API uses gemini provider name "azure" => "bedrock", # Azure OpenAI uses bedrock in registry "bedrock" => "bedrock", "openrouter" => "openrouter" }.freeze
- TIER_CLASSIFICATION =
Tier classification based on model characteristics These are heuristics since ruby_llm doesn't classify tiers
{ # Mini tier: fast, cost-effective models mini: ->(model) { return true if model.id.to_s.match?(/haiku|mini|flash|small/i) # Check pricing if available if model.pricing pricing_hash = model.pricing.to_h input_cost = pricing_hash.dig(:text_tokens, :standard, :input_per_million) return true if input_cost && input_cost < 1.0 end false }, # Advanced tier: high-capability, expensive models advanced: ->(model) { return true if model.id.to_s.match?(/opus|turbo|pro|preview|o1/i) # Check pricing if available if model.pricing pricing_hash = model.pricing.to_h input_cost = pricing_hash.dig(:text_tokens, :standard, :input_per_million) return true if input_cost && input_cost > 10.0 end false }, # Standard tier: everything else (default) standard: ->(model) { true } }.freeze
Instance Method Summary collapse
-
#classify_tier(model) ⇒ String
Classify a model's tier.
-
#deprecation_cache ⇒ Object
Get deprecation cache instance (lazy loaded).
-
#find_replacement_model(deprecated_model, provider: nil) ⇒ String?
Find replacement for a deprecated model Returns the cached replacement if available, otherwise finds the latest non-deprecated model in the same family/tier.
-
#get_model_info(model_id, provider: nil) ⇒ Hash?
Get model information.
-
#initialize(deprecation_cache: nil) ⇒ RubyLLMRegistry
constructor
A new instance of RubyLLMRegistry.
-
#model_deprecated?(model_id, provider = nil) ⇒ Boolean
Check if a model is deprecated.
-
#models_for_provider(provider) ⇒ Array<String>
Get all models for a provider.
-
#models_for_tier(tier, provider: nil, skip_deprecated: true) ⇒ Array<String>
Get all models for a specific tier.
-
#refresh! ⇒ Object
Refresh the model registry from ruby_llm.
-
#resolve_model(model_name, provider: nil, skip_deprecated: true) ⇒ String?
Resolve a model name (family or versioned) to the canonical API model.
Constructor Details
#initialize(deprecation_cache: nil) ⇒ RubyLLMRegistry
Returns a new instance of RubyLLMRegistry.
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 71 def initialize(deprecation_cache: nil) @deprecation_cache = deprecation_cache @models = load_models_with_utf8_fallback @index_by_id = build_id_index(@models) # Build family index for mapping versioned names to families @family_index = build_family_index Aidp.log_info("ruby_llm_registry", "initialized", models: @models.size) end |
Instance Method Details
#classify_tier(model) ⇒ String
Classify a model's tier
212 213 214 215 216 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 212 def classify_tier(model) return "advanced" if TIER_CLASSIFICATION[:advanced].call(model) return "mini" if TIER_CLASSIFICATION[:mini].call(model) "standard" end |
#deprecation_cache ⇒ Object
Get deprecation cache instance (lazy loaded)
34 35 36 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 34 def deprecation_cache @deprecation_cache ||= Aidp::Harness::DeprecationCache.new end |
#find_replacement_model(deprecated_model, provider: nil) ⇒ String?
Find replacement for a deprecated model Returns the cached replacement if available, otherwise finds the latest non-deprecated model in the same family/tier
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 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 243 def find_replacement_model(deprecated_model, provider: nil) registry_provider = provider ? PROVIDER_NAME_MAPPING[provider] : nil return nil unless registry_provider # First check the deprecation cache for a stored replacement cached_replacement = deprecation_cache.replacement_for(provider: registry_provider, model_id: deprecated_model) if cached_replacement Aidp.log_info("ruby_llm_registry", "found cached replacement", deprecated: deprecated_model, replacement: cached_replacement) return cached_replacement end # Determine tier of deprecated model for dynamic lookup deprecated_info = @index_by_id[deprecated_model] return nil unless deprecated_info tier = classify_tier(deprecated_info) # Get all non-deprecated models for this tier and provider candidates = models_for_tier(tier, provider: provider, skip_deprecated: true) # Prefer models in the same family (e.g., both "sonnet") family_keyword = extract_family_keyword(deprecated_model) same_family = candidates.select { |m| m.to_s.include?(family_keyword) } if family_keyword # Return first match from same family, or first candidate overall replacement = same_family&.first || candidates.first if replacement Aidp.log_info("ruby_llm_registry", "found replacement", deprecated: deprecated_model, replacement: replacement, tier: tier) end replacement end |
#get_model_info(model_id, provider: nil) ⇒ Hash?
Get model information
135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 135 def get_model_info(model_id, provider: nil) model = find_model_entry(model_id, provider: provider) return nil unless model { id: model.id, name: model.name || model.display_name, provider: model.provider.to_s, tier: classify_tier(model), context_window: model.context_window, capabilities: extract_capabilities(model), pricing: model.pricing } end |
#model_deprecated?(model_id, provider = nil) ⇒ Boolean
Check if a model is deprecated
231 232 233 234 235 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 231 def model_deprecated?(model_id, provider = nil) return false unless provider deprecation_cache.deprecated?(provider: provider, model_id: model_id.to_s) end |
#models_for_provider(provider) ⇒ Array<String>
Get all models for a provider
198 199 200 201 202 203 204 205 206 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 198 def models_for_provider(provider) # Map AIDP provider name to RubyLLM provider name registry_provider = PROVIDER_NAME_MAPPING[provider] # Return empty if provider doesn't map to a registry provider return [] if registry_provider.nil? @models.select { |m| m.provider.to_s == registry_provider }.map(&:id) end |
#models_for_tier(tier, provider: nil, skip_deprecated: true) ⇒ Array<String>
Get all models for a specific tier
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 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 156 def models_for_tier(tier, provider: nil, skip_deprecated: true) tier_sym = tier.to_sym classifier = TIER_CLASSIFICATION[tier_sym] unless classifier Aidp.log_warn("ruby_llm_registry", "invalid tier", tier: tier) return [] end # Map AIDP provider to registry provider if filtering registry_provider = provider ? PROVIDER_NAME_MAPPING[provider] : nil return [] if provider && registry_provider.nil? models = @models.select do |model| (registry_provider.nil? || model.provider.to_s == registry_provider) && classifier.call(model) end # For mini and standard tiers, exclude if advanced classification matches if tier_sym == :mini models.reject! { |m| TIER_CLASSIFICATION[:advanced].call(m) } elsif tier_sym == :standard models.reject! do |m| TIER_CLASSIFICATION[:mini].call(m) || TIER_CLASSIFICATION[:advanced].call(m) end end # Filter out deprecated models if requested if skip_deprecated models.reject! { |m| deprecation_cache.deprecated?(provider: registry_provider, model_id: m.id.to_s) } end model_ids = models.map(&:id).uniq Aidp.log_debug("ruby_llm_registry", "found models for tier", tier: tier, provider: provider, count: model_ids.size) model_ids end |
#refresh! ⇒ Object
Refresh the model registry from ruby_llm
219 220 221 222 223 224 225 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 219 def refresh! RubyLLM::Models.refresh! @models = load_models_with_utf8_fallback @index_by_id = build_id_index(@models) @family_index = build_family_index Aidp.log_info("ruby_llm_registry", "refreshed", models: @models.size) end |
#resolve_model(model_name, provider: nil, skip_deprecated: true) ⇒ String?
Resolve a model name (family or versioned) to the canonical API model
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/aidp/harness/ruby_llm_registry.rb', line 88 def resolve_model(model_name, provider: nil, skip_deprecated: true) # Map AIDP provider to registry provider if filtering registry_provider = provider ? PROVIDER_NAME_MAPPING[provider] : nil # Check if model is deprecated if skip_deprecated && model_deprecated?(model_name, registry_provider) Aidp.log_warn("ruby_llm_registry", "skipping deprecated model", model: model_name, provider: provider) return nil end # Try exact match first model = @index_by_id[model_name] return model.id if model && (registry_provider.nil? || model.provider.to_s == registry_provider) # Try family mapping family_models = @family_index[model_name] if family_models # Filter by provider if specified family_models = family_models.select { |m| m.provider.to_s == registry_provider } if registry_provider # Filter out deprecated models if requested if skip_deprecated family_models = family_models.reject do |m| deprecation_cache.deprecated?(provider: registry_provider, model_id: m.id.to_s) end end # Return the latest version (first non-"latest" model, or the latest one) model = family_models.reject { |m| m.id.to_s.include?("-latest") }.first || family_models.first return model.id if model end # Try fuzzy matching for common patterns fuzzy_match = find_fuzzy_match(model_name, registry_provider, skip_deprecated: skip_deprecated) return fuzzy_match.id if fuzzy_match Aidp.log_warn("ruby_llm_registry", "model not found", model: model_name, provider: provider) nil end |