Class: Legion::LLM::API::ClientTranslators::OpenAIChat::Events

Inherits:
Object
  • Object
show all
Includes:
Legion::Logging::Helper
Defined in:
lib/legion/llm/api/client_translators/openai_chat.rb

Overview

Events emitter for /v1/chat/completions streaming. Emits data: <chunk>\n\n lines with a final data: [DONE]\n\n.

Instance Method Summary collapse

Constructor Details

#initialize(out:, request_id:, model:, conv_id: nil, include_reasoning: true) ⇒ Events

Returns a new instance of Events.



228
229
230
231
232
233
234
235
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 228

def initialize(out:, request_id:, model:, conv_id: nil, include_reasoning: true)
  @out = out
  @request_id = request_id
  @model = model
  @conv_id = conv_id
  @include_reasoning = include_reasoning
  @done_emitted = false
end

Instance Method Details

#on_done(stop_reason:, usage:, model:) ⇒ Object



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 334

def on_done(stop_reason:, usage:, model:)
  return if @done_emitted

  finish_reason = FINISH_REASON_MAP[stop_reason] || 'stop'
  done_chunk = {
    id:      "chatcmpl-#{@request_id.to_s.delete('-')}",
    object:  'chat.completion.chunk',
    created: Time.now.to_i,
    model:   model.to_s,
    choices: [{ index: 0, delta: {}, finish_reason: finish_reason }],
    usage:   {
      prompt_tokens:     usage[:input_tokens].to_i,
      completion_tokens: usage[:output_tokens].to_i,
      total_tokens:      usage[:input_tokens].to_i + usage[:output_tokens].to_i
    }
  }
  emit_chunk(done_chunk)
  @out << "data: [DONE]\n\n"
  @done_emitted = true
end

#on_error(message:, type:, status_code:) ⇒ Object



355
356
357
358
359
360
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 355

def on_error(message:, type:, status_code:)
  _ = status_code
  emit_chunk({ error: { message: message, type: type } })
  @out << "data: [DONE]\n\n" unless @done_emitted
  @done_emitted = true
end

#on_keep_aliveObject



324
325
326
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 324

def on_keep_alive
  @out << ": keep-alive\n\n"
end

#on_message_delta(stop_reason:, output_tokens:) ⇒ Object



328
329
330
331
332
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 328

def on_message_delta(stop_reason:, output_tokens:)
  _ = stop_reason
  _ = output_tokens
  # Trailing fields fold into on_done.
end

#on_server_tool_result(block_index:, tool_call_id:, result_text:) ⇒ Object



316
317
318
319
320
321
322
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 316

def on_server_tool_result(block_index:, tool_call_id:, result_text:)
  _ = block_index
  _ = tool_call_id
  _ = result_text
  # Chat completions has no inline server tool result event. The
  # follow-up turn carries the tool message.
end

#on_start(model:, request_id:, input_tokens:) ⇒ Object



237
238
239
240
241
242
243
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 237

def on_start(model:, request_id:, input_tokens:)
  _ = model
  _ = request_id
  _ = input_tokens
  # Chat completions stream has no equivalent of "message_start";
  # initial role marker piggybacks on the first text delta.
end

#on_text_close(block_index:) ⇒ Object



255
256
257
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 255

def on_text_close(block_index:)
  _ = block_index
end

#on_text_delta(block_index:, text:) ⇒ Object



250
251
252
253
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 250

def on_text_delta(block_index:, text:)
  _ = block_index
  emit_chunk(delta_envelope({ content: text }))
end

#on_text_open(block_index:) ⇒ Object



245
246
247
248
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 245

def on_text_open(block_index:)
  _ = block_index
  # No explicit open in chat.completion.chunk format.
end

#on_thinking_close(block_index:, signature:) ⇒ Object



272
273
274
275
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 272

def on_thinking_close(block_index:, signature:)
  _ = block_index
  _ = signature
end

#on_thinking_delta(block_index:, text:, signature:) ⇒ Object



263
264
265
266
267
268
269
270
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 263

def on_thinking_delta(block_index:, text:, signature:)
  _ = block_index
  _ = signature
  return unless @include_reasoning
  return if text.to_s.empty?

  emit_chunk(delta_envelope({ reasoning_content: text }))
end

#on_thinking_open(block_index:) ⇒ Object



259
260
261
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 259

def on_thinking_open(block_index:)
  _ = block_index
end

#on_tool_call_abortObject



312
313
314
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 312

def on_tool_call_abort(**)
  nil
end

#on_tool_call_close(block_index:) ⇒ Object



306
307
308
309
310
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 306

def on_tool_call_close(block_index:)
  _ = block_index
  # No close event in chat.completion.chunk; finish_reason
  # 'tool_calls' on the final done covers it.
end

#on_tool_call_delta(block_index:, partial_arguments_json:) ⇒ Object



295
296
297
298
299
300
301
302
303
304
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 295

def on_tool_call_delta(block_index:, partial_arguments_json:)
  return if partial_arguments_json.to_s.empty?

  emit_chunk(delta_envelope({
                              tool_calls: [{
                                index:    block_index,
                                function: { arguments: partial_arguments_json }
                              }]
                            }))
end

#on_tool_call_open(block_index:, tool_call:, server_tool:) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/legion/llm/api/client_translators/openai_chat.rb', line 277

def on_tool_call_open(block_index:, tool_call:, server_tool:)
  _ = server_tool
  # Send the initial tool_call with name. Index is 0-based across
  # this streaming response; chat.completion.chunk references it
  # by index in the delta.
  emit_chunk(delta_envelope({
                              tool_calls: [{
                                index:    block_index,
                                id:       tool_call[:id],
                                type:     'function',
                                function: {
                                  name:      tool_call[:name].to_s,
                                  arguments: ''
                                }
                              }]
                            }))
end