Class: Legion::LLM::Call::NativeResponseAdapter
- Inherits:
-
Object
- Object
- Legion::LLM::Call::NativeResponseAdapter
- Defined in:
- lib/legion/llm/call/dispatch.rb
Overview
Wraps a native dispatch result hash so Pipeline::Executor and ConversationStore can consume a stable provider response object.
Constant Summary collapse
- HASH_KEY_MAP =
{ result: :content, content: :content, input_tokens: :input_tokens, output_tokens: :output_tokens, cache_read_tokens: :cache_read_tokens, cache_write_tokens: :cache_write_tokens, usage: :usage, metadata: :metadata, tool_calls: :tool_calls, stop_reason: :stop_reason, thinking: :thinking, data: :content, model: :model }.freeze
Instance Attribute Summary collapse
-
#cache_read_tokens ⇒ Object
readonly
Returns the value of attribute cache_read_tokens.
-
#cache_write_tokens ⇒ Object
readonly
Returns the value of attribute cache_write_tokens.
-
#content ⇒ Object
readonly
Returns the value of attribute content.
-
#input_tokens ⇒ Object
readonly
Returns the value of attribute input_tokens.
-
#metadata ⇒ Object
readonly
Returns the value of attribute metadata.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#output_tokens ⇒ Object
readonly
Returns the value of attribute output_tokens.
-
#stop_reason ⇒ Object
readonly
Returns the value of attribute stop_reason.
-
#thinking ⇒ Object
readonly
Returns the value of attribute thinking.
-
#tool_calls ⇒ Object
readonly
Returns the value of attribute tool_calls.
-
#usage ⇒ Object
readonly
Returns the value of attribute usage.
Class Method Summary collapse
- .coerce_result(raw) ⇒ Object
- .coerce_single_tool_call(entry) ⇒ Object
- .coerce_tool_calls(raw) ⇒ Object
- .coerce_usage(raw_usage) ⇒ Object
- .extract_result(result, metadata: {}, thinking: nil) ⇒ Object
- .merge_thinking_payloads(existing, extracted) ⇒ Object
- .normalize_thinking_payload(value = nil, content: nil, signature: nil) ⇒ Object
- .single_tool_call_hash?(hash) ⇒ Boolean
Instance Method Summary collapse
- #[](key) ⇒ Object
- #dig(*keys) ⇒ Object
-
#initialize(result_hash) ⇒ NativeResponseAdapter
constructor
A new instance of NativeResponseAdapter.
Constructor Details
#initialize(result_hash) ⇒ NativeResponseAdapter
Returns a new instance of NativeResponseAdapter.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/legion/llm/call/dispatch.rb', line 26 def initialize(result_hash) result_hash = self.class.coerce_result(result_hash) extracted = self.class.extract_result( result_hash[:result], metadata: result_hash[:metadata] || {}, thinking: result_hash[:thinking] ) @content = extracted[:result].to_s @model = result_hash[:model] @metadata = extracted[:metadata] || {} @tool_calls = self.class.coerce_tool_calls(result_hash[:tool_calls]) @stop_reason = result_hash[:stop_reason] @thinking = extracted[:thinking] usage = self.class.coerce_usage(result_hash[:usage]) @usage = usage @input_tokens = usage.input_tokens @output_tokens = usage.output_tokens @cache_read_tokens = usage.cache_read_tokens @cache_write_tokens = usage.cache_write_tokens end |
Instance Attribute Details
#cache_read_tokens ⇒ Object (readonly)
Returns the value of attribute cache_read_tokens.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def cache_read_tokens @cache_read_tokens end |
#cache_write_tokens ⇒ Object (readonly)
Returns the value of attribute cache_write_tokens.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def cache_write_tokens @cache_write_tokens end |
#content ⇒ Object (readonly)
Returns the value of attribute content.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def content @content end |
#input_tokens ⇒ Object (readonly)
Returns the value of attribute input_tokens.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def input_tokens @input_tokens end |
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def @metadata end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def model @model end |
#output_tokens ⇒ Object (readonly)
Returns the value of attribute output_tokens.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def output_tokens @output_tokens end |
#stop_reason ⇒ Object (readonly)
Returns the value of attribute stop_reason.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def stop_reason @stop_reason end |
#thinking ⇒ Object (readonly)
Returns the value of attribute thinking.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def thinking @thinking end |
#tool_calls ⇒ Object (readonly)
Returns the value of attribute tool_calls.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def tool_calls @tool_calls end |
#usage ⇒ Object (readonly)
Returns the value of attribute usage.
13 14 15 |
# File 'lib/legion/llm/call/dispatch.rb', line 13 def usage @usage end |
Class Method Details
.coerce_result(raw) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/legion/llm/call/dispatch.rb', line 60 def self.coerce_result(raw) return raw if raw.is_a?(Hash) { result: raw.respond_to?(:content) ? raw.content : raw, usage: Usage.new( input_tokens: raw.respond_to?(:input_tokens) ? raw.input_tokens.to_i : 0, output_tokens: raw.respond_to?(:output_tokens) ? raw.output_tokens.to_i : 0, cache_read_tokens: raw.respond_to?(:cached_tokens) ? raw.cached_tokens.to_i : 0, cache_write_tokens: raw.respond_to?(:cache_creation_tokens) ? raw.cache_creation_tokens.to_i : 0 ), metadata: raw.respond_to?(:metadata) && raw..is_a?(Hash) ? raw. : {}, tool_calls: raw.respond_to?(:tool_calls) ? coerce_tool_calls(raw.tool_calls) : [], stop_reason: raw.respond_to?(:stop_reason) ? raw.stop_reason : nil, thinking: raw.respond_to?(:thinking) ? raw.thinking : nil }.compact end |
.coerce_single_tool_call(entry) ⇒ Object
119 120 121 122 123 124 125 126 127 |
# File 'lib/legion/llm/call/dispatch.rb', line 119 def self.coerce_single_tool_call(entry) if entry.respond_to?(:id) && entry.respond_to?(:name) return { id: entry.id, name: entry.name, arguments: entry.respond_to?(:arguments) ? entry.arguments : {} } end return entry if entry.is_a?(Hash) nil end |
.coerce_tool_calls(raw) ⇒ Object
106 107 108 109 110 111 112 113 |
# File 'lib/legion/llm/call/dispatch.rb', line 106 def self.coerce_tool_calls(raw) return [] if raw.nil? return raw if raw.is_a?(Array) return raw.values.filter_map { |entry| coerce_single_tool_call(entry) } if raw.is_a?(Hash) && !single_tool_call_hash?(raw) [coerce_single_tool_call(raw)].compact end |
.coerce_usage(raw_usage) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/legion/llm/call/dispatch.rb', line 94 def self.coerce_usage(raw_usage) return raw_usage if raw_usage.is_a?(Usage) return Usage.new unless raw_usage.is_a?(Hash) Usage.new( input_tokens: (raw_usage[:input_tokens] || raw_usage['input_tokens']).to_i, output_tokens: (raw_usage[:output_tokens] || raw_usage['output_tokens']).to_i, cache_read_tokens: (raw_usage[:cache_read_tokens] || raw_usage['cache_read_tokens']).to_i, cache_write_tokens: (raw_usage[:cache_write_tokens] || raw_usage['cache_write_tokens']).to_i ) end |
.extract_result(result, metadata: {}, thinking: nil) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/legion/llm/call/dispatch.rb', line 78 def self.extract_result(result, metadata: {}, thinking: nil) extractor = defined?(::Legion::Extensions::Llm::Responses::ThinkingExtractor) && ::Legion::Extensions::Llm::Responses::ThinkingExtractor return { result: result, metadata: || {}, thinking: normalize_thinking_payload(thinking) } unless extractor extraction = extractor.extract(result, metadata: || {}) { result: extraction.content, metadata: extraction., thinking: merge_thinking_payloads( normalize_thinking_payload(thinking), normalize_thinking_payload(content: extraction.thinking, signature: extraction.signature) ) } end |
.merge_thinking_payloads(existing, extracted) ⇒ Object
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/legion/llm/call/dispatch.rb', line 129 def self.merge_thinking_payloads(existing, extracted) return existing || extracted unless existing && extracted content = [existing[:content], extracted[:content]].compact.map(&:to_s).reject(&:empty?).join existing.merge( content: content.empty? ? nil : content, signature: existing[:signature] || extracted[:signature], enabled: true ).compact end |
.normalize_thinking_payload(value = nil, content: nil, signature: nil) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/legion/llm/call/dispatch.rb', line 140 def self.normalize_thinking_payload(value = nil, content: nil, signature: nil) value = { content: content, signature: signature } if value.nil? && (content || signature) return nil if value.nil? if value.is_a?(Hash) normalized = value.transform_keys { |key| key.respond_to?(:to_sym) ? key.to_sym : key } content = normalized[:content] || normalized[:text] signature = normalized[:signature] elsif value.respond_to?(:text) content = value.text signature = value.respond_to?(:signature) ? value.signature : nil else content = value signature = nil end content = content.to_s.strip unless content.nil? return nil if content.to_s.empty? && signature.to_s.empty? { content: content, signature: signature, enabled: true }.compact end |
.single_tool_call_hash?(hash) ⇒ Boolean
115 116 117 |
# File 'lib/legion/llm/call/dispatch.rb', line 115 def self.single_tool_call_hash?(hash) hash.key?(:name) || hash.key?('name') || hash.key?(:function) || hash.key?('function') end |
Instance Method Details
#[](key) ⇒ Object
47 48 49 50 |
# File 'lib/legion/llm/call/dispatch.rb', line 47 def [](key) attr = HASH_KEY_MAP[key.to_sym] attr ? public_send(attr) : nil end |
#dig(*keys) ⇒ Object
52 53 54 55 56 57 58 |
# File 'lib/legion/llm/call/dispatch.rb', line 52 def dig(*keys) value = self[keys.first] return value if keys.length == 1 return nil unless value.respond_to?(:dig) value.dig(*keys[1..]) end |