Module: Legion::LLM::Inventory

Extended by:
Legion::Logging::Helper
Defined in:
lib/legion/llm/inventory.rb,
lib/legion/llm/inventory/sweeper.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 Classes: Sweeper

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).



47
48
49
50
51
# File 'lib/legion/llm/inventory.rb', line 47

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).



69
70
71
72
# File 'lib/legion/llm/inventory.rb', line 69

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

.lane(id:) ⇒ Object

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



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

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.



63
64
65
66
# File 'lib/legion/llm/inventory.rb', line 63

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<lane> from the Concurrent::Map store. Filters are AND-combined; nil = match-all. TTL-aware (expired lanes excluded).



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

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

.offerings(filters = {}) ⇒ Object

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



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

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



109
110
111
# File 'lib/legion/llm/inventory.rb', line 109

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

.reset_live_store!Object

Reset the live store (for test isolation).



75
76
77
78
# File 'lib/legion/llm/inventory.rb', line 75

def reset_live_store!
  @live_map = Concurrent::Map.new
  @policy_sets = Concurrent::Map.new
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
# 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)

  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