Module: Legion::Extensions::Llm::Bedrock::ThinkingModes

Defined in:
lib/legion/extensions/llm/bedrock/thinking_modes.rb

Overview

Single source of truth for how each Bedrock model expresses extended thinking on the wire. Shared by both the Provider (invoke_model / converse paths) and the Translator (canonical render path) so the two never diverge.

Bedrock supports exactly one thinking wire shape for Anthropic Claude:

{ type: 'enabled', budget_tokens: N }   (native Anthropic Messages API)

There is NO Bedrock Claude model that accepts { type: 'adaptive' } — sending adaptive raises ValidationException: adaptive thinking is not supported on this model (observed live on opus-4-5) and surfaces as an HTTP 500. So a model either supports budgeted thinking (emit enabled) or it does not (OMIT the thinking field entirely — never adaptive).

Budgeted extended thinking arrived with Claude 3.7 Sonnet and is supported across the entire Claude 4 family (sonnet-4, opus-4.x, haiku-4.5). The match is a substring so it tolerates the many Bedrock model-id decorations (geo prefixes us./eu./ap., anthropic. provider prefix, -vN:0 version suffixes, :200k context suffixes).

Constant Summary collapse

BUDGETED_THINKING_FRAGMENTS =

Model-id fragments for Claude families that support explicit budgeted extended thinking via { type: 'enabled', budget_tokens: N }.

%w[
  claude-3-7-sonnet
  claude-sonnet-4
  claude-opus-4
  claude-haiku-4
].freeze

Class Method Summary collapse

Class Method Details

.budgeted_thinking?(model_id) ⇒ Boolean

Returns true when the model supports { type: 'enabled', budget_tokens: N }.

Returns:

  • (Boolean)

    true when the model supports { type: 'enabled', budget_tokens: N }



39
40
41
42
43
44
45
46
# File 'lib/legion/extensions/llm/bedrock/thinking_modes.rb', line 39

def budgeted_thinking?(model_id)
  return false if model_id.nil? || model_id.to_s.strip.empty?

  mid = model_id.to_s
  # Substring scan (String#include?), NOT array intersection: intersect?
  # raises TypeError on a String argument.
  BUDGETED_THINKING_FRAGMENTS.any? { |fragment| mid.include?(fragment) }
end

.known_non_thinking?(model_id) ⇒ Boolean

Distinct from !budgeted_thinking?: a nil/blank/unknown model id is NOT known-unsupported — we then honor an explicit thinking request and emit the (safe) enabled shape rather than dropping it. The router's capability filter (fed by the shared catalog) is the real guard that keeps thinking requests off non-thinking models; this method only strips thinking for a positively-identified non-thinking Claude model so we never emit an unsupported shape and 500.

Returns:

  • (Boolean)

    true when the model is KNOWN not to support thinking.



56
57
58
59
60
# File 'lib/legion/extensions/llm/bedrock/thinking_modes.rb', line 56

def known_non_thinking?(model_id)
  return false if model_id.nil? || model_id.to_s.strip.empty?

  !budgeted_thinking?(model_id)
end