Module: Legion::LLM::Inventory

Extended by:
Legion::Logging::Helper
Defined in:
lib/legion/llm/inventory.rb,
lib/legion/llm/inventory/discovery.rb,
lib/legion/llm/inventory/capabilities.rb,
lib/legion/llm/inventory/discovery/system.rb,
lib/legion/llm/inventory/settings_observer.rb,
lib/legion/llm/inventory/discovery/memory_gate.rb

Defined Under Namespace

Modules: Capabilities, Discovery, SettingsObserver

Constant Summary collapse

LANE_TIERS =

Taxonomy enums — inline until lex-llm ships Legion::Extensions::Llm::Taxonomies (commit 6a). :fleet is first-class per the SSOT plan (operators can set x-legion-tiers: fleet).

%i[direct local fleet cloud frontier].freeze
LANE_TYPES =
%i[inference embedding image audio embed].freeze

Class Method Summary collapse

Class Method Details

.delete_lane(id:) ⇒ Object

Remove a lane. Warn-logs (not debug) if the id is not present (standing rule).



49
50
51
52
53
# File 'lib/legion/llm/inventory.rb', line 49

def delete_lane(id:, **)
  removed = live_map.delete(id)
  log.warn("[llm][inventory] action=delete_lane.miss id=#{id}") if removed.nil?
  removed
end

.expired_idsObject

IDs of lanes whose expires_at is in the past. Used by Sweeper — never .send(:map).



71
72
73
74
# File 'lib/legion/llm/inventory.rb', line 71

def expired_ids(**)
  now = Time.now.to_f
  live_map.each_pair.filter_map { |id, entry| id if entry[:expires_at] && entry[:expires_at] < now }
end

.instances(snapshot:, filters: {}) ⇒ Object

Projects frozen Array of instance data from the supplied snapshot in canonical InstanceKey order. Filters: :provider_family, :instance_id, :availability. Unknown filter keys raise ArgumentError.



166
167
168
169
170
171
172
173
174
175
# File 'lib/legion/llm/inventory.rb', line 166

def instances(snapshot:, filters: {}, **)
  validate_filter_keys!(filters)
  result = []
  snapshot.each_instance do |inst|
    next if filter_mismatch_instance?(inst, filters)

    result << project_instance(inst)
  end
  result.freeze
end

.lane(id:) ⇒ Object

Single lane lookup (TTL-aware). Returns nil if absent or expired.



56
57
58
59
60
61
62
# File 'lib/legion/llm/inventory.rb', line 56

def lane(id:, **)
  entry = live_map[id]
  return nil if entry.nil?
  return nil if entry[:expires_at] && entry[:expires_at] < Time.now.to_f

  entry
end

.lanesObject

All live (non-expired) lanes as an Array snapshot.



65
66
67
68
# File 'lib/legion/llm/inventory.rb', line 65

def lanes(**)
  now = Time.now.to_f
  live_map.each_pair.filter_map { |_id, entry| entry unless entry[:expires_at] && entry[:expires_at] < now }
end

.lanes_for(provider: nil, instance: nil, type: nil, model: nil) ⇒ Object

Filtered read of live lanes. Returns Array from the Concurrent::Map store. Filters are AND-combined; nil = match-all. TTL-aware (expired lanes excluded).



117
118
119
120
121
122
123
124
125
126
# File 'lib/legion/llm/inventory.rb', line 117

def lanes_for(provider: nil, instance: nil, type: nil, model: nil, **)
  lanes.select do |l|
    next false if provider && l[:provider_family].to_sym != provider.to_sym
    next false if instance && l[:instance_id].to_sym != instance.to_sym
    next false if type     && l[:type].to_sym != type.to_sym
    next false if model    && l[:model].to_s != model.to_s

    true
  end
end

.models(snapshot:, filters: {}) ⇒ Object

Projects distinct, sorted, frozen Array of model names from the supplied snapshot. Filters: :provider_family, :instance_id, :tier, :operation. Unknown filter keys raise ArgumentError.



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

def models(snapshot:, filters: {}, **)
  validate_filter_keys!(filters)
  result = []
  snapshot.each_offering do |offering|
    next if filter_mismatch_offering?(offering, filters)

    model = offering.model
    result << model unless result.include?(model)
  end
  result.sort.freeze
end

.offerings(filters = {}) ⇒ Object

Filtered read of the catalog. Reads the live Concurrent::Map store. Returns Array (dups so callers cannot mutate the live store).



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/legion/llm/inventory.rb', line 86

def offerings(filters = {}, **)
  normalized = normalize_filter_hash(filters.merge(**))
  total_in_map = live_map.size
  list = lanes
  provider_matches = normalized[:provider] ? list.count { |l| l[:provider_family]&.to_s == normalized[:provider]&.to_s } : 0
  all_providers = list.group_by { |l| l[:provider_family]&.to_s }.transform_values(&:count)
  log.unknown(
    "[llm][inventory] action=offerings provider=#{normalized[:provider] || 'all'} " \
    "total_in_map=#{total_in_map} after_ttl=#{list.size} " \
    "provider_matches=#{provider_matches} all_providers=#{all_providers}"
  )
  list = list.select { it[:provider_family].to_s == normalized[:provider].to_s } if normalized[:provider]
  list = list.select { [it[:instance_id].to_s, it[:provider_instance].to_s].include?(normalized[:instance].to_s) } if normalized[:instance]
  list = list.select { it[:type].to_s == normalize_type(normalized[:type]).to_s } if normalized[:type]
  if normalized[:model] && normalized[:offering_id]
    list = list.select { it[:model].to_s == normalized[:model].to_s || it[:offering_id].to_s == normalized[:offering_id].to_s }
  elsif normalized[:model]
    list = list.select { it[:model].to_s == normalized[:model].to_s }
  elsif normalized[:offering_id]
    list = list.select { it[:offering_id].to_s == normalized[:offering_id].to_s || it[:id].to_s == normalized[:offering_id].to_s }
  end
  list = list.select { it[:enabled] } unless normalized[:include_disabled]
  list.map(&:dup)
end

.providersObject



111
112
113
# File 'lib/legion/llm/inventory.rb', line 111

def providers
  lanes.group_by { |l| l[:provider_family].to_s }
end

.providers_from(snapshot:, filters: {}) ⇒ Object

Projects distinct, frozen, sorted provider-family Strings from the supplied snapshot. Filters: :provider_family, :instance_id, :tier, :operation, :availability. Unknown filter keys raise ArgumentError.

NOTE: a providers method with a different signature already exists on this module (returns a Hash grouped from the live Concurrent::Map store). This method is named providers_from as a non-colliding bridge until the old writer is removed and providers can be reassigned to this signature.



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/legion/llm/inventory.rb', line 151

def providers_from(snapshot:, filters: {}, **)
  validate_filter_keys!(filters)
  result = []
  snapshot.each_instance do |inst|
    next if filter_mismatch_instance?(inst, filters)

    family = inst.instance_key.provider_family.to_s
    result << family unless result.include?(family)
  end
  result.sort.freeze
end

.reset_live_store!Object

Reset the live store (for test isolation).



77
78
79
80
# File 'lib/legion/llm/inventory.rb', line 77

def reset_live_store!
  @live_map = Concurrent::Map.new
  @policy_sets = Concurrent::Map.new
end

.snapshotObject

Returns the live Phase 1 snapshot (no args; generation-tagged).



139
140
141
# File 'lib/legion/llm/inventory.rb', line 139

def snapshot
  Legion::Extensions::Llm::Inventory::Registry.snapshot
end

.write_lane(lane:, ttl: nil, health: :preserve) ⇒ Object

Upsert a lane into the live Concurrent::Map store. Validates shape (G22), applies policy filter, preserves existing health unless explicit health: kwarg is given (G21 / health: :preserve sentinel), computes lane_weight (G23), freezes the entry on write (M10).



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/legion/llm/inventory.rb', line 24

def write_lane(lane:, ttl: nil, health: :preserve, **)
  validate_lane!(lane: lane)
  return policy_skip(lane: lane) if policy_denied?(lane: lane)

  lane = enrich_lane_range(lane: lane)

  existing = live_map[lane[:id]]
  resolved_health = if health == :preserve
                      existing&.dig(:health) || default_health
                    else
                      health
                    end

  enriched = lane.merge(
    lane_weight: compute_lane_weight(lane: lane, health: resolved_health),
    health:      resolved_health.freeze,
    expires_at:  ttl ? Time.now.to_f + ttl : nil
  ).freeze

  live_map.put(lane[:id], enriched)
  log.debug("[llm][inventory] action=write_lane.written provider=#{lane[:provider_family]} model=#{lane[:model]} instance=#{lane[:instance_id]} tier=#{lane[:tier]}")
  enriched
end