Module: Legion::LLM::Router

Extended by:
Legion::Logging::Helper
Defined in:
lib/legion/llm/router.rb,
lib/legion/llm/router/rule.rb,
lib/legion/llm/router/arbitrage.rb,
lib/legion/llm/router/resolution.rb,
lib/legion/llm/router/health_tracker.rb,
lib/legion/llm/router/escalation/chain.rb,
lib/legion/llm/router/gateway_interceptor.rb

Defined Under Namespace

Modules: Arbitrage, GatewayInterceptor Classes: EscalationChain, HealthTracker, Resolution, Rule

Constant Summary collapse

PROVIDER_TIER =
{ bedrock: :cloud, anthropic: :frontier, openai: :frontier,
gemini: :cloud, azure: :cloud, ollama: :local, vllm: :fleet }.freeze
PROVIDER_ORDER =
%i[ollama vllm bedrock azure gemini anthropic openai].freeze
TIER_EXTERNAL =
Set[:cloud, :frontier, :openai_compat].freeze
OLLAMA_MODEL_PATTERN =
%r{[:/]}

Class Method Summary collapse

Class Method Details

.auto_rules_populated?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/legion/llm/router.rb', line 90

def auto_rules_populated?
  @auto_rules_populated == true
end

.health_trackerObject



79
80
81
# File 'lib/legion/llm/router.rb', line 79

def health_tracker
  @health_tracker ||= build_health_tracker
end

.infer_provider_for_model(model) ⇒ Object



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

def infer_provider_for_model(model)
  return nil if model.nil? || model.to_s.empty?

  model_s = model.to_s
  return :bedrock if model_s.start_with?('us.')
  return :openai if model_s.match?(/\Agpt-|\Ao[134]-/)
  return :anthropic if model_s.start_with?('claude-')
  return :gemini if model_s.start_with?('gemini-')
  return :ollama if model_s.match?(OLLAMA_MODEL_PATTERN)

  nil
end

.populate_auto_rules(discovered_instances) ⇒ Object



94
95
96
97
98
99
# File 'lib/legion/llm/router.rb', line 94

def populate_auto_rules(discovered_instances)
  raw = Discovery::RuleGenerator.generate(discovered_instances)
  @auto_rules = raw.map { |h| Rule.from_hash(h.transform_keys(&:to_sym)) }
  @auto_rules_populated = true
  log.info("[llm][router] auto_rules_populated count=#{@auto_rules.size}")
end

.reset!Object



101
102
103
104
105
# File 'lib/legion/llm/router.rb', line 101

def reset!
  @health_tracker = nil
  @auto_rules = []
  @auto_rules_populated = false
end

.resolve(intent: nil, tier: nil, model: nil, provider: nil, exclude: {}) ⇒ Resolution?

Resolve an LLM routing intent to a tier/provider/model decision.

Parameters:

  • intent (Hash, nil) (defaults to: nil)

    routing intent (capability, privacy, etc.)

  • tier (Symbol, nil) (defaults to: nil)

    explicit tier override — skips rule matching

  • model (String, nil) (defaults to: nil)

    explicit model override

  • provider (Symbol, nil) (defaults to: nil)

    explicit provider override

Returns:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/legion/llm/router.rb', line 49

def resolve(intent: nil, tier: nil, model: nil, provider: nil, exclude: {})
  log.debug "[llm][router] action=resolve.enter intent=#{intent} tier=#{tier} model=#{model} provider=#{provider}"
  return explicit_resolution(tier, provider, model) if tier

  return nil unless routing_enabled? && intent

  merged = merge_defaults(intent)
  rules = load_rules
  candidates = select_candidates(rules, merged, exclude: exclude)
  best = pick_best(candidates)
  resolution = best&.to_resolution

  if resolution
    log.info("Routed to tier=#{resolution.tier} provider=#{resolution.provider} model=#{resolution.model} via rule='#{resolution.rule}'")
  else
    log.debug('Router: no rules matched, resolution is nil')
  end

  resolution || arbitrage_fallback(intent)
end

.resolve_chain(intent: nil, tier: nil, model: nil, provider: nil, max_escalations: nil, exclude: {}) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/legion/llm/router.rb', line 70

def resolve_chain(intent: nil, tier: nil, model: nil, provider: nil, max_escalations: nil, exclude: {})
  log.debug "[llm][router] action=resolve_chain.enter intent=#{intent} tier=#{tier} max_escalations=#{max_escalations}"
  max = max_escalations || escalation_max_attempts
  return EscalationChain.new(resolutions: [explicit_resolution(tier, provider, model)], max_attempts: max) if tier
  return chain_from_defaults(model, provider, max) unless routing_enabled? && intent

  chain_from_intent(intent, max, exclude: exclude)
end

.routing_enabled?Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
# File 'lib/legion/llm/router.rb', line 83

def routing_enabled?
  settings = routing_settings
  return false if settings.nil? || settings.empty?

  settings[:enabled] == true && auto_rules_populated?
end

.tier_available?(tier) ⇒ Boolean

Check whether a tier can be used right now. :local — always available :direct — always available (remote self-hosted instances) :fleet — available when Legion::Transport is loaded :openai_compat — available when gateways are configured :cloud — available unless privacy mode :frontier — available unless privacy mode

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/legion/llm/router.rb', line 114

def tier_available?(tier)
  sym = tier.to_sym
  if external_tier?(sym) && privacy_mode?
    log.debug "[llm][router] action=tier_available tier=#{sym} available=false reason=privacy_mode"
    return false
  end
  if sym == :fleet
    available = Legion.const_defined?('Transport', false)
    log.debug "[llm][router] action=tier_available tier=fleet available=#{available}"
    return available
  end
  if sym == :openai_compat
    available = openai_compat_available?
    log.debug "[llm][router] action=tier_available tier=openai_compat available=#{available}"
    return available
  end

  true
end