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

EMBEDDING_TIER_ORDER =
%w[local direct fleet openai_compat cloud frontier].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.embedding_fallback_chainObject (readonly)

Returns the value of attribute embedding_fallback_chain.



23
24
25
# File 'lib/legion/llm/discovery.rb', line 23

def embedding_fallback_chain
  @embedding_fallback_chain
end

.embedding_instanceObject (readonly)

Returns the value of attribute embedding_instance.



23
24
25
# File 'lib/legion/llm/discovery.rb', line 23

def embedding_instance
  @embedding_instance
end

.embedding_modelObject (readonly)

Returns the value of attribute embedding_model.



23
24
25
# File 'lib/legion/llm/discovery.rb', line 23

def embedding_model
  @embedding_model
end

.embedding_providerObject (readonly)

Returns the value of attribute embedding_provider.



23
24
25
# File 'lib/legion/llm/discovery.rb', line 23

def embedding_provider
  @embedding_provider
end

Class Method Details

.cached_discovered_modelsObject



94
95
96
# File 'lib/legion/llm/discovery.rb', line 94

def cached_discovered_models
  @discovered_models_cache || []
end

.can_embed?Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/legion/llm/discovery.rb', line 25

def can_embed?
  @can_embed == true
end

.detect_embedding_capabilityObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/legion/llm/discovery.rb', line 43

def detect_embedding_capability
  log.debug '[llm][discovery] action=detect_embedding_capability.enter'

  if detect_embedding_from_registry
    log.debug '[llm][discovery] action=detect_embedding_capability registry_hit=true'
    return
  end

  embedding_settings = self.embedding_settings
  found = find_embedding_provider(embedding_settings)
  if found
    @can_embed = true
    @embedding_provider = found[:provider]
    @embedding_model = found[:model]
    @embedding_fallback_chain = build_embedding_fallback_chain(embedding_settings)
    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_instancesObject

Returns discovered instances grouped by provider for RuleGenerator compatibility. Each provider maps to a hash of instance_id => { models: […], base_url: … }



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/legion/llm/discovery.rb', line 72

def discovered_instances
  models = discovered_models
  result = {}
  models.each do |m|
    provider = m[:provider]
    instance = m[:instance] || :default
    result[provider] ||= {}
    result[provider][instance] ||= { models: [] }
    result[provider][instance][:models] << normalize_model_for_rules(m)
  end
  result
end

.discovered_modelsObject

Flat list of all discovered models across all registry adapters. TTL-cached; call refresh_discovered_models! to force a refresh.



87
88
89
90
91
92
# File 'lib/legion/llm/discovery.rb', line 87

def discovered_models
  return @discovered_models_cache if @discovered_models_cache && !discovered_models_stale?

  refresh_discovered_models!
  @discovered_models_cache || []
end

.fetch_offering_models(entry) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/legion/llm/discovery.rb', line 158

def fetch_offering_models(entry)
  adapter = entry[:adapter]
  return [] unless adapter.respond_to?(:offerings)

  begin
    Array(adapter.offerings(live: true)).map do |offering|
      data = normalize_offering(offering)
      {
        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:    data[:capabilities] || [],
        context_length:  data[:context_length] || data[:max_model_len] || data.dig(:limits, :context_window),
        parameter_count: data[:parameter_count] || data.dig(:metadata, :parameter_count)
      }
    end
  rescue StandardError => e
    report_discovery_failure(entry, e)
    []
  end
end

.model_available?(model, provider: nil, instance: nil) ⇒ Boolean

Check whether a specific model is available from any registered provider.

Returns:

  • (Boolean)


99
100
101
102
103
104
105
106
107
# File 'lib/legion/llm/discovery.rb', line 99

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.



110
111
112
113
114
115
116
117
118
119
# File 'lib/legion/llm/discovery.rb', line 110

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

.refresh_all_provider_modelsObject



148
149
150
151
152
153
154
155
156
# File 'lib/legion/llm/discovery.rb', line 148

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



121
122
123
124
125
126
127
128
129
130
# File 'lib/legion/llm/discovery.rb', line 121

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.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/legion/llm/discovery.rb', line 133

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



182
183
184
185
186
187
188
189
190
191
# File 'lib/legion/llm/discovery.rb', line 182

def reset!
  log.debug '[llm][discovery] reset'
  @can_embed = nil
  @embedding_provider = nil
  @embedding_model = nil
  @embedding_instance = nil
  @embedding_fallback_chain = nil
  @discovered_models_cache = nil
  @discovered_models_at = nil
end

.runObject



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/legion/llm/discovery.rb', line 29

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