Module: Legion::LLM::Routing::Filter
- Includes:
- Legion::Logging::Helper, Settings::Helper
- Included in:
- Legion::LLM::Router
- Defined in:
- lib/legion/llm/routing/filter.rb
Overview
Lane eligibility: a lane is eligible iff every applicable filter passes. Each filter is a pure (lane, fact) -> pass/fail check — stateless, individually RSpec-testable.
Instance Method Summary collapse
-
#body_model_hint_decision_for(body_model:, trusted_model:) ⇒ Object
The SOLE body-model hint decision (SSOT v3 §17.1 / D19).
-
#evaluate_capabilities(lane:, required_capabilities:) ⇒ Object
§9.7 step 4 — capability reduction with the operator's enable_* routing override (fail-forward decision 2).
-
#evaluate_context(lane:, budget:) ⇒ Object
§9.7 step 5 — context budget.
-
#evaluate_dimensions(lane:, requested_dimensions:) ⇒ Object
§9.7 step 6 — embedding dimensions.
-
#filter_availability(instance:) ⇒ Object
Availability: the exact-instance availability state.
-
#filter_capability(**opts) ⇒ Object
Capability constraint: the capabilities the request requires, or nil when none.
-
#filter_context(**opts) ⇒ Object
Context constraint: the required context size (tokens), or nil when unconstrained.
-
#filter_embedding_dimensions(**opts) ⇒ Object
Embedding-dimension constraint: the requested dimensionality, or nil when unconstrained.
-
#filter_exclusions(lane:, exclusions:) ⇒ Object
§9.7 step 8 — typed exclusions.
-
#filter_fleet(lane:) ⇒ Object
Fleet contract: the fleet execution contract (fleet tier only).
-
#filter_instance(**opts) ⇒ Object
Instance constraint: the instance the request asks for, or nil when unconstrained.
-
#filter_operation(lane:, operation:) ⇒ Object
§9.7 step 1 — operation axis: requested operation's coarse type vs lane type.
-
#filter_pins(lane:, provider_pin: nil, instance_pin: nil, model_pin: nil, tier_constraint: nil) ⇒ Object
§9.7 step 2 — provider/instance/model/tier pins.
-
#filter_policy(lane:, whitelist:, blacklist:) ⇒ Object
Policy: model whitelist/blacklist (substring, case-insensitive); blacklist wins.
-
#filter_provider(**opts) ⇒ Object
Provider constraint: the provider the request asks for, or nil when unconstrained.
-
#filter_tier(**opts) ⇒ Object
Tier constraint: the tier the request asks for, or nil when unconstrained.
-
#filter_type(**opts) ⇒ Object
Type constraint: the lane type/modality the request asks for, or nil when unconstrained.
-
#filter_weight(lane:) ⇒ Object
Weight: the stored write-time weight; any zero component disables the lane.
-
#model_policy_for(lane:) ⇒ Object
Returns { whitelist: Array
, blacklist: Array } using the §9.5 specificity cascade: exact provider+instance → provider → global. -
#preferred_context_range_for(lane:) ⇒ Object
Returns { min: Integer_or_nil, max: Integer_or_nil } or nil when no preferred range is configured.
Instance Method Details
#body_model_hint_decision_for(body_model:, trusted_model:) ⇒ Object
The SOLE body-model hint decision (SSOT v3 §17.1 / D19). Given the untrusted request-body model value and any trusted explicit model, it returns one immutable BodyModelHintDecision. It never returns a lane, substitute model, or alias; only a :honored decision carries a model constraint. The Router calls this ONCE in initialize.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/legion/llm/routing/filter.rb', line 285 def body_model_hint_decision_for(body_model:, trusted_model:, **) requested = normalize(body_model) router_cfg = Legion::Settings[:llm][:router] # 1. missing/blank body model → absent if requested.nil? return build_hint_decision(requested_model: nil, disposition: :absent, settings_generation: 0) end # 2. body + trusted explicit model → trusted wins, body is metadata only unless normalize(trusted_model).nil? return build_hint_decision(requested_model: requested, disposition: :superseded_by_explicit_model, settings_generation: 0) end # 3. auto-routing alias → auto (you-pick intent, no constraint) aliases = router_cfg[:auto_routing_model_aliases] if auto_alias?(requested, aliases) return build_hint_decision(requested_model: requested, disposition: :auto, settings_generation: 0) end # 4. body hints globally disabled → ignored unless router_cfg[:allow_body_routing_hints] return build_hint_decision(requested_model: requested, disposition: :ignored_disabled, settings_generation: 0) end whitelist = router_cfg[:body_model_hint_whitelist] blacklist = router_cfg[:body_model_hint_blacklist] # 5. nonempty whitelist with no match → ignored_not_whitelisted if !whitelist.empty? && substring_match(requested, whitelist).nil? return build_hint_decision(requested_model: requested, disposition: :ignored_not_whitelisted, settings_generation: 0) end # 6. any blacklist match → ignored_blacklisted (blacklist wins over whitelist) matched_black = substring_match(requested, blacklist) unless matched_black.nil? return build_hint_decision(requested_model: requested, disposition: :ignored_blacklisted, matched_blacklist: matched_black, matched_whitelist: substring_match(requested, whitelist), settings_generation: 0) end # 7. honored → exact body model becomes the model constraint build_hint_decision(requested_model: requested, disposition: :honored, model_constraint: requested, matched_whitelist: substring_match(requested, whitelist), settings_generation: 0) end |
#evaluate_capabilities(lane:, required_capabilities:) ⇒ Object
§9.7 step 4 — capability reduction with the operator's enable_* routing override (fail-forward decision 2). All satisfied → :supported; any unknown → :unknown (highest priority); any not-ready with no unknown → :unsupported.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/legion/llm/routing/filter.rb', line 147 def evaluate_capabilities(lane:, required_capabilities:, **) return :supported if required_capabilities.empty? any_unknown = false any_unsupported = false required_capabilities.each do |cap| case resolved_capability_status(lane: lane, capability: cap) when :unknown then any_unknown = true when :unsupported then any_unsupported = true end end return :unknown if any_unknown return :unsupported if any_unsupported :supported end |
#evaluate_context(lane:, budget:) ⇒ Object
§9.7 step 5 — context budget. Zero budget → :not_applicable (no context requirement). Authoritative limit: fits when budget <= (limit * headroom_ppm) / 1_000_000. Absent or unknown context evidence → :unknown.
170 171 172 173 174 175 176 177 178 179 |
# File 'lib/legion/llm/routing/filter.rb', line 170 def evaluate_context(lane:, budget:, **) return :not_applicable if budget.zero? ctx_ev = lane.context_evidence return :unknown unless ctx_ev.known? limit = ctx_ev.value headroom = Legion::Settings[:llm][:router][:context_headroom_ppm] budget <= (limit * headroom) / 1_000_000 ? :fits : :rejected end |
#evaluate_dimensions(lane:, requested_dimensions:) ⇒ Object
§9.7 step 6 — embedding dimensions. Nil requested → :not_applicable. Authoritative evidence is a sorted Array of positive Integers. :match when the requested dimension appears in the supported set; :rejected when the set is known but excludes the requested value. Unknown evidence → :unknown.
187 188 189 190 191 192 193 194 |
# File 'lib/legion/llm/routing/filter.rb', line 187 def evaluate_dimensions(lane:, requested_dimensions:, **) return :not_applicable if requested_dimensions.nil? dim_ev = lane. return :unknown unless dim_ev.known? Array(dim_ev.value).include?(requested_dimensions) ? :match : :rejected end |
#filter_availability(instance:) ⇒ Object
Availability: the exact-instance availability state.
74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/legion/llm/routing/filter.rb', line 74 def filter_availability(instance:, **) return :unknown if instance.nil? avail = instance.availability return :unknown if avail.nil? case avail.state when :available then :available when :unavailable then :unavailable else :unknown end end |
#filter_capability(**opts) ⇒ Object
Capability constraint: the capabilities the request requires, or nil when none.
56 57 58 59 |
# File 'lib/legion/llm/routing/filter.rb', line 56 def filter_capability(**opts) caps = Array(opts[:capabilities]).compact caps.empty? ? nil : caps.freeze end |
#filter_context(**opts) ⇒ Object
Context constraint: the required context size (tokens), or nil when unconstrained.
62 63 64 65 |
# File 'lib/legion/llm/routing/filter.rb', line 62 def filter_context(**opts) context = opts[:context] context if context.is_a?(Integer) end |
#filter_embedding_dimensions(**opts) ⇒ Object
Embedding-dimension constraint: the requested dimensionality, or nil when unconstrained.
68 69 70 71 |
# File 'lib/legion/llm/routing/filter.rb', line 68 def (**opts) dims = opts[:embedding_dimensions] dims if dims.is_a?(Integer) end |
#filter_exclusions(lane:, exclusions:) ⇒ Object
§9.7 step 8 — typed exclusions. attempt_target compares (provider_family, instance_id, model) only. :lane and :offering both name the 5-tuple lane id (D2).
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/legion/llm/routing/filter.rb', line 199 def filter_exclusions(lane:, exclusions:, **) return :clear if exclusions.empty? ik = lane.instance_key pf = ik.provider_family iid = ik.instance_id model = lane.model excluded = exclusions.any? do |excl| case excl.target_kind when :attempt_target t = excl.target t.provider_family == pf && t.instance_id == iid && t.model == model when :instance excl.target == ik when :lane, :offering excl.target == lane.lane_id when :model excl.target == model when :provider excl.target == pf when :quota_domain qd = lane.quota_domain qd && excl.target == qd else false end end excluded ? :excluded : :clear end |
#filter_fleet(lane:) ⇒ Object
Fleet contract: the fleet execution contract (fleet tier only).
88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/legion/llm/routing/filter.rb', line 88 def filter_fleet(lane:, **) return :not_applicable unless lane.tier == :fleet contract = lane.[:fleet_execution_contract] if contract == 'exact_offering_v1' :supported elsif contract.nil? || contract.to_s.empty? :legacy else :unknown end end |
#filter_instance(**opts) ⇒ Object
Instance constraint: the instance the request asks for, or nil when unconstrained.
33 34 35 36 |
# File 'lib/legion/llm/routing/filter.rb', line 33 def filter_instance(**opts) instance = opts[:instance] instance if instance.is_a?(String) end |
#filter_operation(lane:, operation:) ⇒ Object
§9.7 step 1 — operation axis: requested operation's coarse type vs lane type. The registry publishes lanes only for supported operations, so a type match is :supported and a type miss is :unsupported.
123 124 125 126 127 |
# File 'lib/legion/llm/routing/filter.rb', line 123 def filter_operation(lane:, operation:, **) requested_type = OPS.lane_type_for(operation: operation) lane_type = OPS.lane_type_for(operation: lane.operation) lane_type == requested_type ? :supported : :unsupported end |
#filter_pins(lane:, provider_pin: nil, instance_pin: nil, model_pin: nil, tier_constraint: nil) ⇒ Object
§9.7 step 2 — provider/instance/model/tier pins. :match when all configured pins equal the lane's fields. :mismatch when any configured pin differs.
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/legion/llm/routing/filter.rb', line 132 def filter_pins(lane:, provider_pin: nil, instance_pin: nil, model_pin: nil, tier_constraint: nil, **) ik = lane.instance_key return :mismatch if provider_pin && provider_pin != ik.provider_family return :mismatch if instance_pin && instance_pin != ik.instance_id return :mismatch if model_pin && model_pin != lane.model return :mismatch if tier_constraint && tier_constraint != lane.tier :match end |
#filter_policy(lane:, whitelist:, blacklist:) ⇒ Object
Policy: model whitelist/blacklist (substring, case-insensitive); blacklist wins.
45 46 47 48 49 50 51 52 53 |
# File 'lib/legion/llm/routing/filter.rb', line 45 def filter_policy(lane:, whitelist:, blacklist:, **) model_lc = lane.model.downcase return :denied if Array(blacklist).any? { |e| model_lc.include?(e.to_s.downcase) } wl = Array(whitelist) return :denied if wl.any? && wl.none? { |e| model_lc.include?(e.to_s.downcase) } :allowed end |
#filter_provider(**opts) ⇒ Object
Provider constraint: the provider the request asks for, or nil when unconstrained.
27 28 29 30 |
# File 'lib/legion/llm/routing/filter.rb', line 27 def filter_provider(**opts) provider = opts[:provider] provider if provider.is_a?(Symbol) end |
#filter_tier(**opts) ⇒ Object
Tier constraint: the tier the request asks for, or nil when unconstrained.
39 40 41 42 |
# File 'lib/legion/llm/routing/filter.rb', line 39 def filter_tier(**opts) tier = opts[:tier] tier if tier.is_a?(Symbol) end |
#filter_type(**opts) ⇒ Object
Type constraint: the lane type/modality the request asks for, or nil when unconstrained.
21 22 23 24 |
# File 'lib/legion/llm/routing/filter.rb', line 21 def filter_type(**opts) type = opts[:type] type if type.is_a?(Symbol) end |
#filter_weight(lane:) ⇒ Object
Weight: the stored write-time weight; any zero component disables the lane.
102 103 104 105 106 107 108 109 |
# File 'lib/legion/llm/routing/filter.rb', line 102 def filter_weight(lane:, **) inputs = lane.weight_inputs if inputs.nil? || inputs.values.any?(&:zero?) :disabled else :enabled end end |
#model_policy_for(lane:) ⇒ Object
Returns { whitelist: Array
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/legion/llm/routing/filter.rb', line 238 def model_policy_for(lane:, **) ext_llm = Legion::Settings[:extensions][:llm] || {} ik = lane.instance_key pf = ik.provider_family iid = ik.instance_id prov = ext_llm[pf] || {} instances = prov[:instances] || {} inst = instances[iid.to_sym] || instances[iid] || {} wl = first_existing_policy(inst, prov, ext_llm, :model_whitelist) bl = first_existing_policy(inst, prov, ext_llm, :model_blacklist) { whitelist: Array(wl).freeze, blacklist: Array(bl).freeze }.freeze end |
#preferred_context_range_for(lane:) ⇒ Object
Returns { min: Integer_or_nil, max: Integer_or_nil } or nil when no preferred range is configured. Resolved through the lex-llm 3-level cascade (provider -> instance -> model, most-specific-first) keyed by the config name.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/legion/llm/routing/filter.rb', line 258 def preferred_context_range_for(lane:, **) ext_llm = Legion::Settings[:extensions][:llm] || {} pf = lane.instance_key.provider_family iid = lane.instance_key.instance_id min_v = CASCADE.resolve_from( llm_conf: ext_llm, provider_family: pf, instance: iid, key: :preferred_min_context_tokens, model: lane.model ) max_v = CASCADE.resolve_from( llm_conf: ext_llm, provider_family: pf, instance: iid, key: :preferred_max_context_tokens, model: lane.model ) return nil unless min_v || max_v { min: min_v, max: max_v }.freeze end |