Module: Legion::LLM::Discovery
- Extended by:
- Legion::Logging::Helper
- Defined in:
- lib/legion/llm/discovery.rb,
lib/legion/llm/discovery/system.rb,
lib/legion/llm/discovery/memory_gate.rb,
lib/legion/llm/discovery/rule_generator.rb
Defined Under Namespace
Modules: MemoryGate, RuleGenerator, System
Constant Summary collapse
- DISCOVERED_MODELS_SCHEMA_VERSION =
3- EMBEDDING_TIER_ORDER =
%w[local direct fleet cloud frontier].freeze
- MODEL_FAMILY_DELIMITERS =
Version/tag delimiters that separate a model family base from its concrete discovered id (Ollama “model:tag”, Bedrock “family-YYYYMMDD-v1:0” / “family-6”).
%w[: -].freeze
- MODEL_DIVERGENCE_SAMPLE_SIZE =
Cap how many discovered ids a divergence warning prints — multi-model cloud providers (Bedrock lists ~90) otherwise dump an unreadable single log line.
10
Class Attribute Summary collapse
-
.embedding_fallback_chain ⇒ Object
readonly
Returns the value of attribute embedding_fallback_chain.
-
.embedding_instance ⇒ Object
readonly
Returns the value of attribute embedding_instance.
-
.embedding_model ⇒ Object
readonly
Returns the value of attribute embedding_model.
-
.embedding_provider ⇒ Object
readonly
Returns the value of attribute embedding_provider.
Class Method Summary collapse
- .cached_discovered_models ⇒ Object
- .can_embed? ⇒ Boolean
- .detect_embedding_capability ⇒ Object
-
.discovered_instances ⇒ Object
Returns discovered instances grouped by provider for RuleGenerator compatibility.
-
.discovered_models ⇒ Object
Flat list of all discovered models across all registry adapters.
- .discovery_status(provider:, instance: nil) ⇒ Object
- .fetch_offering_models(entry) ⇒ Object
- .merge_registry_instance_capabilities!(grouped) ⇒ Object
-
.model_available?(model, provider: nil, instance: nil) ⇒ Boolean
Check whether a specific model is available from any registered provider.
-
.model_size(model, provider: nil, instance: nil) ⇒ Object
Return the size in bytes for a discovered model, or nil if unknown.
- .record_discovery_status(provider:, status:, instance: nil) ⇒ Object
- .refresh_all_provider_models ⇒ Object
- .refresh_discovered_models!(provider: nil) ⇒ Object
-
.refresh_provider_models(provider) ⇒ Object
Refresh models for a single provider, preserving other providers’ cached models.
- .reset! ⇒ Object
- .run ⇒ Object
Class Attribute Details
.embedding_fallback_chain ⇒ Object (readonly)
Returns the value of attribute embedding_fallback_chain.
32 33 34 |
# File 'lib/legion/llm/discovery.rb', line 32 def @embedding_fallback_chain end |
.embedding_instance ⇒ Object (readonly)
Returns the value of attribute embedding_instance.
32 33 34 |
# File 'lib/legion/llm/discovery.rb', line 32 def @embedding_instance end |
.embedding_model ⇒ Object (readonly)
Returns the value of attribute embedding_model.
32 33 34 |
# File 'lib/legion/llm/discovery.rb', line 32 def @embedding_model end |
.embedding_provider ⇒ Object (readonly)
Returns the value of attribute embedding_provider.
32 33 34 |
# File 'lib/legion/llm/discovery.rb', line 32 def @embedding_provider end |
Class Method Details
.cached_discovered_models ⇒ Object
142 143 144 145 146 |
# File 'lib/legion/llm/discovery.rb', line 142 def cached_discovered_models return [] if discovered_models_cache_schema_stale? @discovered_models_cache || [] end |
.can_embed? ⇒ Boolean
34 35 36 |
# File 'lib/legion/llm/discovery.rb', line 34 def @can_embed == true end |
.detect_embedding_capability ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/legion/llm/discovery.rb', line 62 def log.debug '[llm][discovery] action=detect_embedding_capability.enter' if log.debug '[llm][discovery] action=detect_embedding_capability registry_hit=true' return end = self. found = () if found @can_embed = true @embedding_provider = found[:provider] @embedding_model = found[:model] @embedding_fallback_chain = () log.info "[llm][discovery] embedding available provider=#{@embedding_provider} model=#{@embedding_model}" else @can_embed = false @embedding_fallback_chain = [] log.info '[llm][discovery] no embedding provider available' end rescue StandardError => e @can_embed = false @embedding_fallback_chain = [] handle_exception(e, level: :warn, operation: 'llm.discovery.detect_embedding_capability') end |
.discovered_instances ⇒ Object
Returns discovered instances grouped by provider for RuleGenerator compatibility. Each provider maps to a hash of instance_id => { models: […], capabilities: […] }. Instance-level capabilities come from the Call::Registry metadata that the provider extension’s ‘discover_instances` populates (e.g. lex-llm-vllm declares `capabilities: %i[completion streaming vision tools]` on its DEFAULT_INSTANCE_TIER). Threading them through here means RuleGenerator’s chat rules carry ‘:tools` even when the per-model offerings hash only surfaces `:completion` — without it, the router logs `resolve.no_rules_matched required_capabilities=` on every tool request and falls through to the default-provider fallback.
98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/legion/llm/discovery.rb', line 98 def discovered_instances models = discovered_models result = {} models.each do |m| provider = m[:provider] instance = m[:instance] || :default result[provider] ||= {} result[provider][instance] ||= { models: [], capabilities: [] } result[provider][instance][:models] << normalize_model_for_rules(m) end merge_registry_instance_capabilities!(result) result end |
.discovered_models ⇒ Object
Flat list of all discovered models across all registry adapters. TTL-cached; call refresh_discovered_models! to force a refresh.
135 136 137 138 139 140 |
# File 'lib/legion/llm/discovery.rb', line 135 def discovered_models return @discovered_models_cache if @discovered_models_cache && !discovered_models_stale? refresh_discovered_models! @discovered_models_cache || [] end |
.discovery_status(provider:, instance: nil) ⇒ Object
38 39 40 41 |
# File 'lib/legion/llm/discovery.rb', line 38 def discovery_status(provider:, instance: nil) key = discovery_status_key(provider, instance) @discovery_mutex.synchronize { @discovery_status[key] || :unknown } end |
.fetch_offering_models(entry) ⇒ Object
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 |
# File 'lib/legion/llm/discovery.rb', line 208 def fetch_offering_models(entry) adapter = entry[:adapter] return [] unless adapter.respond_to?(:offerings) begin models = Array(adapter.offerings(live: true)).map do |offering| data = normalize_offering(offering) report_discovery_health(entry, data) { schema_version: DISCOVERED_MODELS_SCHEMA_VERSION, model: (data[:id] || data[:name] || data[:model]).to_s, provider: entry[:provider], instance: normalize_instance_id(data[:instance_id] || data[:provider_instance] || entry[:instance]), tier: data[:tier] || entry.dig(:metadata, :tier), size_bytes: data[:size_bytes] || data[:size], capabilities: source_aware_capabilities(data, entry), capability_sources: data[:capability_sources], context_length: data[:context_length] || data[:max_model_len] || data.dig(:limits, :context_window), parameter_count: data[:parameter_count] || data.dig(:metadata, :parameter_count), health: data[:health] || data['health'] || data.dig(:metadata, :health), loaded: extract_loaded_field(data) } end record_discovery_status( provider: entry[:provider], instance: entry[:instance], status: models.empty? ? :empty : :ok ) warn_on_model_divergence(entry, models) models rescue StandardError => e report_discovery_failure(entry, e) [] end end |
.merge_registry_instance_capabilities!(grouped) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/legion/llm/discovery.rb', line 112 def merge_registry_instance_capabilities!(grouped) return grouped unless defined?(Call::Registry) Call::Registry.all_instances.each do |entry| provider = entry[:provider] instance = entry[:instance] = entry[:metadata] || {} capabilities = Array([:capabilities] || ['capabilities']) next if capabilities.empty? grouped[provider] ||= {} grouped[provider][instance] ||= { models: [], capabilities: [] } existing = Array(grouped[provider][instance][:capabilities]) grouped[provider][instance][:capabilities] = (existing + capabilities).uniq end grouped rescue StandardError => e handle_exception(e, level: :warn, handled: true, operation: 'llm.discovery.merge_registry_capabilities') grouped end |
.model_available?(model, provider: nil, instance: nil) ⇒ Boolean
Check whether a specific model is available from any registered provider.
149 150 151 152 153 154 155 156 157 |
# File 'lib/legion/llm/discovery.rb', line 149 def model_available?(model, provider: nil, instance: nil) psym = provider&.to_sym isym = instance&.to_sym discovered_models.any? do |m| name_matches?(m[:model], model) && (psym.nil? || m[:provider] == psym) && (isym.nil? || m[:instance] == isym) end end |
.model_size(model, provider: nil, instance: nil) ⇒ Object
Return the size in bytes for a discovered model, or nil if unknown.
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/legion/llm/discovery.rb', line 160 def model_size(model, provider: nil, instance: nil) psym = provider&.to_sym isym = instance&.to_sym entry = discovered_models.find do |m| name_matches?(m[:model], model) && (psym.nil? || m[:provider] == psym) && (isym.nil? || m[:instance] == isym) end entry&.dig(:size_bytes) end |
.record_discovery_status(provider:, status:, instance: nil) ⇒ Object
43 44 45 46 |
# File 'lib/legion/llm/discovery.rb', line 43 def record_discovery_status(provider:, status:, instance: nil) key = discovery_status_key(provider, instance) @discovery_mutex.synchronize { @discovery_status[key] = status.to_sym } end |
.refresh_all_provider_models ⇒ Object
198 199 200 201 202 203 204 205 206 |
# File 'lib/legion/llm/discovery.rb', line 198 def refresh_all_provider_models return unless defined?(Call::Registry) models = Call::Registry.all_instances.flat_map { |entry| fetch_offering_models(entry) } @discovered_models_cache = models @discovered_models_at = Time.now log.debug "[llm][discovery] action=refresh_discovered_models count=#{models.size}" end |
.refresh_discovered_models!(provider: nil) ⇒ Object
171 172 173 174 175 176 177 178 179 180 |
# File 'lib/legion/llm/discovery.rb', line 171 def refresh_discovered_models!(provider: nil) log.debug "[llm][discovery] action=refresh_discovered_models provider=#{provider || :all}" return unless defined?(Call::Registry) if provider refresh_provider_models(provider) else refresh_all_provider_models end end |
.refresh_provider_models(provider) ⇒ Object
Refresh models for a single provider, preserving other providers’ cached models.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/legion/llm/discovery.rb', line 183 def refresh_provider_models(provider) return unless defined?(Call::Registry) fresh_models = Call::Registry.all_instances .select { |e| (e[:provider] || '').to_sym == provider } .flat_map { |entry| fetch_offering_models(entry) } existing = @discovered_models_cache || [] other_models = existing.reject { |m| (m[:provider] || '').to_sym == provider } @discovered_models_cache = other_models + fresh_models @discovered_models_at = Time.now log.debug "[llm][discovery] action=refresh_provider_models provider=#{provider} count=#{@discovered_models_cache.size}" end |
.reset! ⇒ Object
245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/legion/llm/discovery.rb', line 245 def reset! log.debug '[llm][discovery] reset' @discovery_mutex.synchronize do @can_embed = nil @embedding_provider = nil @embedding_model = nil @embedding_instance = nil @embedding_fallback_chain = nil @discovered_models_cache = nil @discovered_models_at = nil @discovery_status = {} end end |
.run ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/legion/llm/discovery.rb', line 48 def run log.debug '[llm][discovery] run.enter' System.refresh! if discovery_enabled? refresh_discovered_models! models = discovered_models log.info "[llm][discovery] model_count=#{models.size} " \ "models=#{models.map { |m| m[:model] }.join(', ')}" log.info "[llm][discovery] system total_mb=#{System.total_memory_mb} " \ "available_mb=#{System.available_memory_mb}" rescue StandardError => e handle_exception(e, level: :warn, operation: 'llm.discovery.run') end |