Class: Legion::LLM::Inference::Request

Inherits:
Data
  • Object
show all
Defined in:
lib/legion/llm/inference/request.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Attribute Details

#agentObject (readonly)

Returns the value of attribute agent

Returns:

  • (Object)

    the current value of agent



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def agent
  @agent
end

#billingObject (readonly)

Returns the value of attribute billing

Returns:

  • (Object)

    the current value of billing



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def billing
  @billing
end

#body_model_hint_decisionObject (readonly)

Returns the value of attribute body_model_hint_decision

Returns:

  • (Object)

    the current value of body_model_hint_decision



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def body_model_hint_decision
  @body_model_hint_decision
end

#cacheObject (readonly)

Returns the value of attribute cache

Returns:

  • (Object)

    the current value of cache



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def cache
  @cache
end

#callerObject (readonly)

Returns the value of attribute caller

Returns:

  • (Object)

    the current value of caller



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def caller
  @caller
end

#classificationObject (readonly)

Returns the value of attribute classification

Returns:

  • (Object)

    the current value of classification



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def classification
  @classification
end

#context_strategyObject (readonly)

Returns the value of attribute context_strategy

Returns:

  • (Object)

    the current value of context_strategy



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def context_strategy
  @context_strategy
end

#conversation_idObject (readonly)

Returns the value of attribute conversation_id

Returns:

  • (Object)

    the current value of conversation_id



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def conversation_id
  @conversation_id
end

#enrichmentsObject (readonly)

Returns the value of attribute enrichments

Returns:

  • (Object)

    the current value of enrichments



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def enrichments
  @enrichments
end

#extraObject (readonly)

Returns the value of attribute extra

Returns:

  • (Object)

    the current value of extra



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def extra
  @extra
end

#forkObject (readonly)

Returns the value of attribute fork

Returns:

  • (Object)

    the current value of fork



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def fork
  @fork
end

#generationObject (readonly)

Returns the value of attribute generation

Returns:

  • (Object)

    the current value of generation



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def generation
  @generation
end

#hooksObject (readonly)

Returns the value of attribute hooks

Returns:

  • (Object)

    the current value of hooks



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def hooks
  @hooks
end

#idObject (readonly)

Returns the value of attribute id

Returns:

  • (Object)

    the current value of id



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def id
  @id
end

#idempotency_keyObject (readonly)

Returns the value of attribute idempotency_key

Returns:

  • (Object)

    the current value of idempotency_key



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def idempotency_key
  @idempotency_key
end

#messagesObject (readonly)

Returns the value of attribute messages

Returns:

  • (Object)

    the current value of messages



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def messages
  @messages
end

#metadataObject (readonly)

Returns the value of attribute metadata

Returns:

  • (Object)

    the current value of metadata



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def 
  @metadata
end

#modalityObject (readonly)

Returns the value of attribute modality

Returns:

  • (Object)

    the current value of modality



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def modality
  @modality
end

#predictionsObject (readonly)

Returns the value of attribute predictions

Returns:

  • (Object)

    the current value of predictions



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def predictions
  @predictions
end

#priorityObject (readonly)

Returns the value of attribute priority

Returns:

  • (Object)

    the current value of priority



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def priority
  @priority
end

#response_formatObject (readonly)

Returns the value of attribute response_format

Returns:

  • (Object)

    the current value of response_format



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def response_format
  @response_format
end

#routingObject (readonly)

Returns the value of attribute routing

Returns:

  • (Object)

    the current value of routing



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def routing
  @routing
end

#routing_contextObject (readonly)

Returns the value of attribute routing_context

Returns:

  • (Object)

    the current value of routing_context



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def routing_context
  @routing_context
end

#routing_settings_snapshotObject (readonly)

Returns the value of attribute routing_settings_snapshot

Returns:

  • (Object)

    the current value of routing_settings_snapshot



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def routing_settings_snapshot
  @routing_settings_snapshot
end

#schema_versionObject (readonly)

Returns the value of attribute schema_version

Returns:

  • (Object)

    the current value of schema_version



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def schema_version
  @schema_version
end

#stopObject (readonly)

Returns the value of attribute stop

Returns:

  • (Object)

    the current value of stop



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def stop
  @stop
end

#streamObject (readonly)

Returns the value of attribute stream

Returns:

  • (Object)

    the current value of stream



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def stream
  @stream
end

#systemObject (readonly)

Returns the value of attribute system

Returns:

  • (Object)

    the current value of system



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def system
  @system
end

#testObject (readonly)

Returns the value of attribute test

Returns:

  • (Object)

    the current value of test



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def test
  @test
end

#thinkingObject (readonly)

Returns the value of attribute thinking

Returns:

  • (Object)

    the current value of thinking



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def thinking
  @thinking
end

#tokensObject (readonly)

Returns the value of attribute tokens

Returns:

  • (Object)

    the current value of tokens



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def tokens
  @tokens
end

#tool_choiceObject (readonly)

Returns the value of attribute tool_choice

Returns:

  • (Object)

    the current value of tool_choice



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def tool_choice
  @tool_choice
end

#toolsObject (readonly)

Returns the value of attribute tools

Returns:

  • (Object)

    the current value of tools



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def tools
  @tools
end

#tracingObject (readonly)

Returns the value of attribute tracing

Returns:

  • (Object)

    the current value of tracing



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def tracing
  @tracing
end

#trusted_constraintsObject (readonly)

Returns the value of attribute trusted_constraints

Returns:

  • (Object)

    the current value of trusted_constraints



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def trusted_constraints
  @trusted_constraints
end

#ttlObject (readonly)

Returns the value of attribute ttl

Returns:

  • (Object)

    the current value of ttl



13
14
15
# File 'lib/legion/llm/inference/request.rb', line 13

def ttl
  @ttl
end

Class Method Details

.auto_routing_model?(model) ⇒ Boolean

Returns:

  • (Boolean)


186
187
188
189
190
191
192
193
194
195
# File 'lib/legion/llm/inference/request.rb', line 186

def self.auto_routing_model?(model)
  # SSOT v4: auto_routing_model_aliases is a router tunable — read it from
  # the canonical [:llm][:router] home (default in settings/router.rb) so
  # ingress normalization agrees with the Router's body-model-hint ladder
  # (filter.rb). No .dig / no || shadow-default: the default guarantees the key.
  configured = Legion::Settings[:llm][:router][:auto_routing_model_aliases]
  aliases = Array(configured).map { |entry| entry.to_s.strip.downcase }.reject(&:empty?)
  aliases = [AUTO_ROUTING_MODEL_KEY] if aliases.empty?
  aliases.include?(model.to_s.strip.downcase)
end

.build(routing_context: nil, **kwargs) ⇒ Object

SSOT v3 §7.2 additive build order. routing_context is injected only by build_for_test; otherwise a fresh server seed is created here. The new trusted fields are always populated (derived from existing routing kwargs when a caller has not yet migrated), so every Request carries them.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/legion/llm/inference/request.rb', line 52

def self.build(routing_context: nil, **kwargs)
  routing, extra = normalize_auto_routing(
    kwargs.fetch(:routing, { provider: nil, model: nil }),
    kwargs.fetch(:extra, {})
  )

  ctx = routing_context || Legion::LLM::RoutingContext.build
  settings_snapshot = kwargs[:routing_settings_snapshot] || Legion::LLM::Routing::SettingsState.current
  trusted = kwargs[:trusted_constraints] || trusted_from_routing(routing, settings_snapshot)

  # SSOT v4: the raw client body model is the sole input to the Router's
  # body-model-hint ladder, which reads it from metadata[:client_model].
  # Client translators pass it as the top-level client_model: kwarg, so
  # fold that into metadata here (the single bridge point) — the Request
  # no longer carries a precomputed body_model_hint_decision.
   = kwargs.fetch(:metadata, {})
   = .merge(client_model: kwargs[:client_model]) unless kwargs[:client_model].nil?

  new(
    routing_context:           ctx,
    routing_settings_snapshot: settings_snapshot,
    trusted_constraints:       trusted,
    body_model_hint_decision:  nil,
    id:                        kwargs[:id] || "req_#{SecureRandom.hex(12)}",
    conversation_id:           kwargs[:conversation_id],
    idempotency_key:           kwargs[:idempotency_key],
    schema_version:            kwargs.fetch(:schema_version, '1.0.0'),
    system:                    kwargs[:system],
    messages:                  canonicalize_messages(kwargs.fetch(:messages, [])),
    tools:                     kwargs.key?(:tools) ? kwargs[:tools] : nil,
    tool_choice:               kwargs.fetch(:tool_choice, { mode: :auto }),
    routing:                   routing,
    tokens:                    kwargs.fetch(:tokens, { max: 4096 }),
    stop:                      kwargs.fetch(:stop, { sequences: [] }),
    generation:                kwargs.fetch(:generation, {}),
    thinking:                  kwargs[:thinking],
    response_format:           kwargs.fetch(:response_format, { type: :text }),
    stream:                    kwargs.fetch(:stream, false),
    fork:                      kwargs[:fork],
    context_strategy:          kwargs.fetch(:context_strategy, :auto),
    cache:                     kwargs.fetch(:cache, { strategy: :default, cacheable: true }),
    priority:                  kwargs.fetch(:priority, :normal),
    ttl:                       kwargs[:ttl],
    extra:                     extra,
    metadata:                  ,
    enrichments:               kwargs.fetch(:enrichments, {}),
    predictions:               kwargs.fetch(:predictions, {}),
    tracing:                   kwargs[:tracing],
    classification:            kwargs[:classification],
    caller:                    kwargs[:caller],
    agent:                     kwargs[:agent],
    billing:                   kwargs[:billing],
    test:                      kwargs[:test],
    modality:                  kwargs[:modality],
    hooks:                     kwargs[:hooks]
  )
end

.build_for_test(routing_seed:, **keywords) ⇒ Object

SSOT v3 §7.2 test helper: delegates to the production normalization path, substituting only a deterministic RoutingContext. It cannot bypass body policy, constraint derivation, or request freezing.



170
171
172
# File 'lib/legion/llm/inference/request.rb', line 170

def self.build_for_test(routing_seed:, **keywords)
  build(routing_context: Legion::LLM::RoutingContext.for_test(routing_seed: routing_seed), **keywords)
end

.canonicalize_inbound_message(message) ⇒ Object

Raises:

  • (ArgumentError)


38
39
40
41
42
43
44
45
46
# File 'lib/legion/llm/inference/request.rb', line 38

def self.canonicalize_inbound_message(message)
  canonical = Legion::Extensions::Llm::Canonical::Message
  return message if message.is_a?(canonical)
  return canonical.build(role: :user, content: message) if message.is_a?(String)
  return canonical.from_hash(message) if message.is_a?(Hash)

  raise ArgumentError,
        "Inference::Request messages must be Canonical::Message, String, or Hash, got #{message.class}"
end

.canonicalize_messages(messages) ⇒ Object

N x N law: Inference::Request#messages is ArrayCanonical::Message. Every pipeline entry (client translators, native/inference APIs, daemon-internal chat) funnels through build; canonical objects pass through, plain strings and wire hashes are translated to canonical at this boundary, and anything else raises loudly. No hash-shaped messages survive past this point.



34
35
36
# File 'lib/legion/llm/inference/request.rb', line 34

def self.canonicalize_messages(messages)
  Array(messages).map { |message| canonicalize_inbound_message(message) }
end

.default_auto_routing_intentObject



197
198
199
200
201
# File 'lib/legion/llm/inference/request.rb', line 197

def self.default_auto_routing_intent
  intent = Legion::Settings[:llm][:routing][:default_intent]
  intent = intent.is_a?(Hash) ? normalize_hash(intent) : {}
  intent.merge(operation: :chat, effort: :moderate)
end

.from_chat_args(**kwargs) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/legion/llm/inference/request.rb', line 110

def self.from_chat_args(**kwargs)
  request_id = kwargs[:request_id] || kwargs[:id]
  # Plain strings, wire hashes, and canonical objects are all translated
  # to Array<Canonical::Message> by build's canonicalize_messages.
  messages = kwargs[:messages] || kwargs[:message] || []

  routing = {
    provider: kwargs[:provider],
    model:    kwargs[:model]
  }

  extra = kwargs.except(
    :message, :messages, :model, :provider, :system,
    :tools, :tool_choice, :stream, :caller, :classification, :billing,
    :agent, :test, :tracing, :priority, :conversation_id,
    :request_id, :id, :generation, :thinking, :response_format,
    :context_strategy, :cache, :fork, :tokens, :stop,
    :modality, :hooks, :idempotency_key, :ttl, :metadata,
    :enrichments, :predictions
  )

  build_args = {
    messages:         messages,
    system:           kwargs[:system],
    routing:          routing,
    tools:            kwargs.key?(:tools) ? kwargs[:tools] : nil,
    tool_choice:      kwargs[:tool_choice] || { mode: :auto },
    stream:           kwargs.fetch(:stream, false),
    generation:       kwargs[:generation] || {},
    thinking:         kwargs[:thinking],
    response_format:  kwargs[:response_format] || { type: :text },
    context_strategy: kwargs.fetch(:context_strategy, :auto),
    cache:            kwargs[:cache] || { strategy: :default, cacheable: true },
    fork:             kwargs[:fork],
    tokens:           kwargs[:tokens] || { max: 4096 },
    stop:             kwargs[:stop] || { sequences: [] },
    modality:         kwargs[:modality],
    hooks:            kwargs[:hooks],
    caller:           kwargs[:caller],
    classification:   kwargs[:classification],
    billing:          kwargs[:billing],
    agent:            kwargs[:agent],
    test:             kwargs[:test],
    tracing:          kwargs[:tracing],
    priority:         kwargs.fetch(:priority, :normal),
    conversation_id:  kwargs[:conversation_id],
    idempotency_key:  kwargs[:idempotency_key],
    ttl:              kwargs[:ttl],
    metadata:         kwargs[:metadata] || {},
    enrichments:      kwargs[:enrichments] || {},
    predictions:      kwargs[:predictions] || {},
    extra:            extra
  }
  build_args[:id] = request_id if request_id
  build(**build_args)
end

.normalize_auto_routing(routing, extra) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/legion/llm/inference/request.rb', line 203

def self.normalize_auto_routing(routing, extra)
  normalized_routing = normalize_hash(routing)
  normalized_extra = normalize_hash(extra)
  return [normalized_routing, normalized_extra] unless auto_routing_model?(normalized_routing[:model])

  normalized_routing = normalized_routing.dup
  normalized_routing[:model] = nil
  normalized_extra = normalized_extra.dup
  normalized_extra[:requested_model_alias] = Legion::LLM::Inference::AUTO_ROUTING_MODEL_KEY
  if normalized_routing.values_at(:provider, :instance, :instance_id, :provider_instance).compact.any? ||
     normalized_extra[:tier]
    return [normalized_routing, normalized_extra]
  end

  normalized_extra[:intent] ||= default_auto_routing_intent
  normalized_extra[:auto_route] = true
  [normalized_routing, normalized_extra]
end

.normalize_hash(value) ⇒ Object



222
223
224
225
226
227
228
# File 'lib/legion/llm/inference/request.rb', line 222

def self.normalize_hash(value)
  return {} unless value.is_a?(Hash)

  value.each_with_object({}) do |(key, hash_value), normalized|
    normalized[key.respond_to?(:to_sym) ? key.to_sym : key] = hash_value
  end
end

.trusted_from_routing(routing, settings_snapshot) ⇒ Object

Derive trusted constraints from a legacy routing hash for callers not yet migrated to pass an explicit trusted_constraints value. The routing hash is trusted internal input (not an untrusted client body field).



177
178
179
180
181
182
183
184
# File 'lib/legion/llm/inference/request.rb', line 177

def self.trusted_from_routing(routing, settings_snapshot)
  routing ||= {}
  instance = routing[:instance] || routing[:instance_id] || routing[:provider_instance]
  Legion::LLM::Routing::HeaderConstraints.from_internal(
    provider: routing[:provider], instance_id: instance, model: routing[:model],
    tier: routing[:tier], maximum_attempts: nil, settings_snapshot: settings_snapshot
  )
end