Module: Legion::Extensions::Llm::Responses::ThinkingExtractor

Defined in:
lib/legion/extensions/llm/responses/thinking_extractor.rb

Overview

Separates provider thinking markup from caller-visible text.

Defined Under Namespace

Classes: Extraction

Constant Summary collapse

THINK_TAG_PAIRS =
[
  ['<thinking>', '</thinking>'],
  ['<think>',    '</think>']
].freeze
UNTAGGED_PREAMBLE_MAX_LENGTH =
4_000
UNTAGGED_PREAMBLE_STARTS =
[
  'the user',
  'the request',
  'the prompt',
  'the question',
  'i need',
  'i should',
  'i will',
  "i'll",
  'i can',
  'we need',
  'we should',
  'we will',
  "we'll",
  'we can',
  'let me'
].freeze
UNTAGGED_PREAMBLE_PATTERNS =
[
  /
    \AThe\s+(?:user|request|prompt|question)\b.*\b
    (?:let\s+me|i'll|i\s+will|i\s+should|i\s+need|i\s+can|respond|answer|reply)\b
  /imx,
  /
    \A(?:I|We)\s+(?:need|should|will|can)\s+(?:to\s+)?
    (?:answer|respond|reply|confirm|provide|explain|help)\b
  /imx,
  /\ALet me\s+(?:answer|respond|reply|confirm|provide|explain|help)\b/im
].freeze
THINKING_METADATA_KEYS =
%i[
  reasoning_content reasoning thinking thinking_text thinking_signature reasoning_signature thought_signature
].freeze
RAW_METADATA_KEYS =
%i[
  raw raw_response response_body provider_body provider_response
].freeze

Class Method Summary collapse

Class Method Details

.extract(content, metadata: {}) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/legion/extensions/llm/responses/thinking_extractor.rb', line 53

def extract(content, metadata: {})
   = ()
  content, extracted_thinking = extract_from_content(content)
   = ()
   = ()

  Extraction.new(
    content: content,
    thinking: compact_thinking([, extracted_thinking]),
    signature: ,
    metadata: ()
  )
end

.extract_untagged_preamble(content) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/legion/extensions/llm/responses/thinking_extractor.rb', line 82

def extract_untagged_preamble(content)
  return [content, nil] unless content.is_a?(String)

  match = content.match(/\A(?<preamble>.+?)\n{2,}(?<visible>.+)\z/m)
  return [content, nil] unless match

  preamble = match[:preamble].strip
  return [content, nil] unless untagged_reasoning_preamble?(preamble)

  [match[:visible].sub(/\A[[:space:]]+/, '').strip, preamble]
end

.untagged_reasoning_preamble_candidate?(content) ⇒ Boolean

Returns:

  • (Boolean)


94
95
96
97
98
99
100
101
102
103
# File 'lib/legion/extensions/llm/responses/thinking_extractor.rb', line 94

def untagged_reasoning_preamble_candidate?(content)
  return false unless content.is_a?(String)

  text = content.lstrip.downcase
  return false if text.empty?

  UNTAGGED_PREAMBLE_STARTS.any? do |start|
    start.start_with?(text) || text.start_with?(start)
  end
end