Module: Legion::LLM::Routing::Outcome

Includes:
Legion::Logging::Helper
Included in:
Legion::LLM::Router
Defined in:
lib/legion/llm/routing/outcome.rb

Overview

Outcome classification mixin — consumes a Phase 1 normalized ProviderOutcome plus the AttemptContext and classifies into success/retry/terminal. Shaped like Rank (mixin + records in one file).

Included into the Router class; all methods are INSTANCE methods.

Defined Under Namespace

Classes: Action, GlobalTransition

Constant Summary collapse

RETRYABLE =

Outcomes that retry against a different eligible target when attempts remain.

%i[
  instance_unavailable overloaded model_not_ready timeout connection_failure
  provider_error malformed_output tool_failure rate_limited
  authentication authorization billing model_missing context_rejected
].freeze
TERMINAL_REJECTION_KIND =

Terminal provider outcomes -> best-fit Rejection kind. cancelled/client_disconnect preserve the outcome so the owner can skip HTTP rendering for a gone client.

{
  policy:            :policy_denied,
  invalid_request:   :invalid_request,
  safety_refusal:    :invalid_request,
  cancelled:         :invalid_request,
  client_disconnect: :invalid_request
}.freeze

Instance Method Summary collapse

Instance Method Details

#classify_outcome(outcome:, attempt_context:, attempts_remaining:) ⇒ Action

Classify a dispatch outcome into success/retry/terminal. Pure classification — returns an Action; does NOT apply exclusions or transitions (the Router's stateful #classify does that).

Parameters:

  • outcome (ProviderOutcome)

    the normalized outcome from dispatch

  • attempt_context (AttemptContext)

    the attempt's routing context (selection, etc.)

  • attempts_remaining (Integer)

    how many attempts remain after this one

Returns:

Raises:

  • (ArgumentError)


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/legion/llm/routing/outcome.rb', line 102

def classify_outcome(outcome:, attempt_context:, attempts_remaining:, **)
  kind = outcome.kind
  return Action.success(outcome: outcome) if kind == :success

  if kind == :instance_unavailable
    return Action.retry(
      exclusions: [], outcome: outcome,
      global_transition: GlobalTransition.new(
        instance_key:       attempt_context.selection.instance_key,
        publisher_token_id: attempt_context.selection.publisher_token_id,
        reason:             outcome.reason
      )
    )
  end

  if RETRYABLE.include?(kind)
    return attempts_exhausted_rejection(outcome) unless attempts_remaining.positive?

    return Action.retry(exclusions: quota_exclusions(outcome), outcome: outcome)
  end

  rejection_kind = TERMINAL_REJECTION_KIND[kind]
  raise ArgumentError, "unclassifiable provider outcome kind: #{kind.inspect}" if rejection_kind.nil?

  Action.terminal(outcome: outcome, rejection: terminal_rejection(rejection_kind, outcome))
end