Class: Legion::Extensions::Llm::Openai::Translator
- Inherits:
-
Object
- Object
- Legion::Extensions::Llm::Openai::Translator
- Includes:
- Logging::Helper
- Defined in:
- lib/legion/extensions/llm/openai/translator.rb
Overview
Canonical <-> OpenAI wire-format translator.
Implements the provider-boundary contract (Amendment A) so that canonical requests/responses/chunks cross exactly one translation layer per provider. Extracted from OpenAICompatible mixin methods; semantics preserved, not rewritten.
Capabilities (declarative, per the design doc):
- reasoning_effort: true (gpt-5.x / o-series)
- responses_api: true (chat/completions wire format)
- thinking_metadata_keys: [...] (metadata keys for thinking)
- stop_reason_map: { openai -> canonical }
– translator complexity is inherent to multi-field mapping (B1a pattern) rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
Constant Summary collapse
- STOP_REASON_MAP =
OpenAI finish_reason -> canonical stop_reason (G18 stop-reason matrix)
{ 'stop' => :end_turn, 'tool_calls' => :tool_use, 'function_call' => :tool_use, 'length' => :max_tokens, 'content_filter' => :content_filter }.freeze
- THINKING_METADATA_KEYS =
Metadata keys carrying thinking/reasoning in OpenAI responses
%i[ reasoning_content reasoning thinking thinking_text thinking_signature reasoning_signature ].freeze
Instance Method Summary collapse
-
#capabilities ⇒ Hash
Declarative capabilities consumed by routing/dispatch.
-
#initialize(api_base: nil, headers: nil) ⇒ Translator
constructor
A new instance of Translator.
- #parse_chunk(raw) ⇒ Canonical::Chunk?
- #parse_response(wire) ⇒ Canonical::Response
-
#render_request(canonical_request) ⇒ Hash
OpenAI wire-format payload for /v1/chat/completions.
Constructor Details
#initialize(api_base: nil, headers: nil) ⇒ Translator
Returns a new instance of Translator.
39 40 41 42 |
# File 'lib/legion/extensions/llm/openai/translator.rb', line 39 def initialize(api_base: nil, headers: nil) @api_base = api_base @headers = headers || {} end |
Instance Method Details
#capabilities ⇒ Hash
Returns declarative capabilities consumed by routing/dispatch.
45 46 47 48 49 50 51 52 53 |
# File 'lib/legion/extensions/llm/openai/translator.rb', line 45 def capabilities { provider: 'openai', reasoning_effort: true, responses_api: true, thinking_metadata_keys: THINKING_METADATA_KEYS, stop_reason_map: STOP_REASON_MAP } end |
#parse_chunk(raw) ⇒ Canonical::Chunk?
109 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 |
# File 'lib/legion/extensions/llm/openai/translator.rb', line 109 def parse_chunk(raw) return nil if raw.nil? || raw.to_h.empty? return Canonical::Chunk.from_hash(raw) if canonical_chunk_form?(raw) data = raw.to_h choice = Array(data['choices']).first || {} delta = choice['delta'] || {} finish_reason = choice['finish_reason'] usage_raw = data['usage'] return build_done_chunk(data, finish_reason, usage_raw) if finish_reason return parse_error_chunk(data) if data['error'] reasoning = delta['reasoning_content'] || delta['reasoning'] content = delta['content'] return build_thinking_chunk(reasoning, data['id']) if reasoning return build_text_chunk(content, data['id']) if content parse_tool_call_delta(delta, data) rescue StandardError => e handle_exception(e, level: :error, handled: true, operation: 'openai.translator.parse_chunk') Canonical::Chunk.error_chunk( error: e., request_id: raw.to_h['id'] ) end |
#parse_response(wire) ⇒ Canonical::Response
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 |
# File 'lib/legion/extensions/llm/openai/translator.rb', line 75 def parse_response(wire) return Canonical::Response.from_hash(wire) if canonical_form?(wire) body = wire.to_h choice = Array(body['choices']).first || {} = choice['message'] || {} usage_raw = body['usage'] || {} text, thinking = () tool_calls = parse_tool_calls(['tool_calls']) usage = parse_usage(usage_raw) stop_reason = map_stop_reason(choice['finish_reason']) = () Canonical::Response.build( text: text, thinking: thinking, tool_calls: tool_calls, usage: usage, stop_reason: stop_reason, model: body['model'], metadata: ) rescue StandardError => e handle_exception(e, level: :error, handled: true, operation: 'openai.translator.parse_response') Canonical::Response.build( text: '', stop_reason: :error, metadata: { error: e. } ) end |
#render_request(canonical_request) ⇒ Hash
Returns OpenAI wire-format payload for /v1/chat/completions.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/legion/extensions/llm/openai/translator.rb', line 57 def render_request(canonical_request) wire = { model: resolve_model(canonical_request), messages: (canonical_request), stream: canonical_request.stream }.compact apply_params(wire, canonical_request.params) if canonical_request.params apply_tools(wire, canonical_request) if canonical_request.tools&.any? apply_tool_choice(wire, canonical_request.tool_choice) if canonical_request.tool_choice apply_thinking(wire, canonical_request) use_stream_usage(wire) if canonical_request.stream wire end |