Class: Legion::Extensions::Llm::Canonical::Chunk

Inherits:
Data
  • Object
show all
Defined in:
lib/legion/extensions/llm/canonical/chunk.rb

Overview

Canonical streaming chunk with full lifecycle support. Per R4: block_index/item_id/signature lifecycle, multi-tool-call deltas. Per G20d (04 §11, stated law): strict on produce — the named factories and the generic build validate type against CHUNK_TYPES; lenient on consume — from_hash accepts any type symbol and passes it through. # -- required for Data.define block scope

Constant Summary collapse

CHUNK_TYPES =
%i[text_delta thinking_delta tool_call_delta usage done error].freeze
BUILD_SITE =
'Canonical::Chunk.build'
FROM_HASH_SITE =
'Canonical::Chunk.from_hash'
NEW_SITE =
'Canonical::Chunk.new'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#block_indexObject (readonly)

Returns the value of attribute block_index

Returns:

  • (Object)

    the current value of block_index



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def block_index
  @block_index
end

#conversation_idObject (readonly)

Returns the value of attribute conversation_id

Returns:

  • (Object)

    the current value of conversation_id



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def conversation_id
  @conversation_id
end

#deltaObject (readonly)

Returns the value of attribute delta

Returns:

  • (Object)

    the current value of delta



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def delta
  @delta
end

#exchange_idObject (readonly)

Returns the value of attribute exchange_id

Returns:

  • (Object)

    the current value of exchange_id



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def exchange_id
  @exchange_id
end

#indexObject (readonly)

Returns the value of attribute index

Returns:

  • (Object)

    the current value of index



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def index
  @index
end

#item_idObject (readonly)

Returns the value of attribute item_id

Returns:

  • (Object)

    the current value of item_id



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def item_id
  @item_id
end

#metadataObject (readonly)

Returns the value of attribute metadata

Returns:

  • (Object)

    the current value of metadata



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def 
  @metadata
end

#request_idObject (readonly)

Returns the value of attribute request_id

Returns:

  • (Object)

    the current value of request_id



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def request_id
  @request_id
end

#signatureObject (readonly)

Returns the value of attribute signature

Returns:

  • (Object)

    the current value of signature



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def signature
  @signature
end

#stop_reasonObject (readonly)

Returns the value of attribute stop_reason

Returns:

  • (Object)

    the current value of stop_reason



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def stop_reason
  @stop_reason
end

#timestampObject (readonly)

Returns the value of attribute timestamp

Returns:

  • (Object)

    the current value of timestamp



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def timestamp
  @timestamp
end

#tool_callObject (readonly)

Returns the value of attribute tool_call

Returns:

  • (Object)

    the current value of tool_call



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def tool_call
  @tool_call
end

#typeObject (readonly)

Returns the value of attribute type

Returns:

  • (Object)

    the current value of type



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def type
  @type
end

#usageObject (readonly)

Returns the value of attribute usage

Returns:

  • (Object)

    the current value of usage



13
14
15
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 13

def usage
  @usage
end

Class Method Details

.build(type:, request_id: nil, conversation_id: nil, exchange_id: nil, index: nil, block_index: nil, item_id: nil, delta: nil, tool_call: nil, signature: nil, usage: nil, stop_reason: nil, metadata: {}, timestamp: nil) ⇒ Object

Generic produce path — the only way to construct an arbitrary-type chunk; type is validated against CHUNK_TYPES (G20d).



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 21

def self.build(
  type:, request_id: nil, conversation_id: nil, exchange_id: nil,
  index: nil, block_index: nil, item_id: nil,
  delta: nil, tool_call: nil, signature: nil,
  usage: nil, stop_reason: nil, metadata: {}, timestamp: nil
)
  type_sym = type.is_a?(::String) ? type.to_sym : type
  Strict.enum!(type_sym, self::CHUNK_TYPES, self::BUILD_SITE, :type)
  new(
    request_id:, conversation_id:, exchange_id:,
    index:, type: type_sym, block_index:,
    item_id:, delta:,
    tool_call: normalize_tool_call!(tool_call, self::BUILD_SITE),
    signature:,
    usage: normalize_usage!(usage, self::BUILD_SITE),
    stop_reason: stop_reason&.to_sym,
    metadata: Strict.metadata!(, self::BUILD_SITE),
    timestamp: timestamp || ::Time.now
  )
end

.done(request_id:, usage: nil, stop_reason: nil, conversation_id: nil, exchange_id: nil) ⇒ Object

Build a done chunk.



80
81
82
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 80

def self.done(request_id:, usage: nil, stop_reason: nil, conversation_id: nil, exchange_id: nil)
  build(type: :done, request_id:, usage:, stop_reason:, conversation_id:, exchange_id:)
end

.error_chunk(error:, request_id:, conversation_id: nil, exchange_id: nil, metadata: {}) ⇒ Object

Build an error chunk.



85
86
87
88
89
90
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 85

def self.error_chunk(error:, request_id:, conversation_id: nil, exchange_id: nil, metadata: {})
  build(
    type: :error, request_id:, conversation_id:, exchange_id:,
    stop_reason: :error, metadata: .merge(error:)
  )
end

.from_hash(source) ⇒ Object

Build from a Hash (raw provider response or deserialized wire payload). Per G20d: ignore-unknown on consume — unknown chunk types pass through.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 101

def self.from_hash(source)
  Strict.require_hash!(source, self::FROM_HASH_SITE)
  hash = Strict.symbolize_keys(source)
   = Strict.fold_unknowns!(self, self::FROM_HASH_SITE, hash)
  type_raw = hash.delete(:type)
  tool_call = normalize_tool_call!(hash.delete(:tool_call), self::FROM_HASH_SITE)
  usage = normalize_usage!(hash.delete(:usage), self::FROM_HASH_SITE)
  stop_reason_raw = hash.delete(:stop_reason)
  timestamp = hash.delete(:timestamp)
  # Remaining keys are all members; pass through with consume defaults.
  new(
    request_id: hash[:request_id],
    conversation_id: hash[:conversation_id],
    exchange_id: hash[:exchange_id],
    index: hash[:index],
    type: type_raw&.to_sym,
    block_index: hash[:block_index],
    item_id: hash[:item_id],
    delta: hash[:delta],
    tool_call:,
    signature: hash[:signature],
    usage:,
    stop_reason: stop_reason_raw&.to_sym,
    metadata:,
    timestamp: timestamp || ::Time.now
  )
end

.normalize_tool_call!(tool_call, site) ⇒ Object

tool_call member: the delta fragment (Hash) or a full ToolCall; nil allowed.



130
131
132
133
134
135
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 130

def self.normalize_tool_call!(tool_call, site)
  return nil if tool_call.nil?
  return tool_call if tool_call.is_a?(::Hash) || tool_call.is_a?(ToolCall)

  Strict.expect_type!(tool_call, [::Hash, ToolCall], site, :tool_call)
end

.normalize_usage!(usage, site) ⇒ Object



137
138
139
140
141
142
143
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 137

def self.normalize_usage!(usage, site)
  return nil if usage.nil?
  return usage if usage.is_a?(Usage)

  Strict.expect_type!(usage, [::Hash], site, :usage)
  Usage.from_hash(usage)
end

.shape_symbol!(value, site, member) ⇒ Object

Raises:

  • (ArgumentError)


92
93
94
95
96
97
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 92

def self.shape_symbol!(value, site, member)
  return nil if value.nil?
  return value.to_sym if value.is_a?(::String) || value.is_a?(::Symbol)

  raise ArgumentError, "#{site}: #{member} expected String or Symbol, got #{value.class}"
end

.text_delta(delta:, request_id:, conversation_id: nil, exchange_id: nil, index: 0, block_index: nil, item_id: nil, stop_reason: nil, usage: nil) ⇒ Object

Build a text delta chunk.



43
44
45
46
47
48
49
50
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 43

def self.text_delta(delta:, request_id:, conversation_id: nil, exchange_id: nil,
                    index: 0, block_index: nil, item_id: nil,
                    stop_reason: nil, usage: nil)
  build(
    type: :text_delta, delta:, request_id:, conversation_id:, exchange_id:,
    index:, block_index:, item_id:, stop_reason:, usage:
  )
end

.thinking_delta(delta:, request_id:, conversation_id: nil, exchange_id: nil, index: 0, block_index: nil, item_id: nil, signature: nil, stop_reason: nil, usage: nil) ⇒ Object

Build a thinking delta chunk.



53
54
55
56
57
58
59
60
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 53

def self.thinking_delta(delta:, request_id:, conversation_id: nil, exchange_id: nil,
                        index: 0, block_index: nil, item_id: nil, signature: nil,
                        stop_reason: nil, usage: nil)
  build(
    type: :thinking_delta, delta:, request_id:, conversation_id:, exchange_id:,
    index:, block_index:, item_id:, signature:, stop_reason:, usage:
  )
end

.tool_call_delta(tool_call:, request_id:, conversation_id: nil, exchange_id: nil, index: 0, block_index: nil, item_id: nil, stop_reason: nil, usage: nil) ⇒ Object

Build a tool_call_delta chunk (supports multiple in-flight tool calls via the fragment's id/index). tool_call is the delta fragment: { id:, name:, arguments: , index:, signature: }.



65
66
67
68
69
70
71
72
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 65

def self.tool_call_delta(tool_call:, request_id:, conversation_id: nil, exchange_id: nil,
                         index: 0, block_index: nil, item_id: nil,
                         stop_reason: nil, usage: nil)
  build(
    type: :tool_call_delta, tool_call:, request_id:, conversation_id:, exchange_id:,
    index:, block_index:, item_id:, stop_reason:, usage:
  )
end

.usage_chunk(usage:, request_id:, conversation_id: nil, exchange_id: nil) ⇒ Object

Build a usage chunk.



75
76
77
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 75

def self.usage_chunk(usage:, request_id:, conversation_id: nil, exchange_id: nil)
  build(type: :usage, request_id:, conversation_id:, exchange_id:, usage:)
end

Instance Method Details

#as_jsonObject

MultiJson/Oj/::JSON callback — prevents Data.define #inspect leak into JSON.



166
167
168
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 166

def as_json(*)
  to_h
end

#content?Boolean

Whether this chunk carries content (text or thinking).

Returns:

  • (Boolean)


183
184
185
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 183

def content?
  %i[text_delta thinking_delta].include?(type)
end

#done?Boolean

Returns:

  • (Boolean)


179
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 179

def done? = type == :done

#error?Boolean

Returns:

  • (Boolean)


180
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 180

def error? = type == :error

#text_delta?Boolean

Type predicate helpers.

Returns:

  • (Boolean)


175
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 175

def text_delta? = type == :text_delta

#thinking_delta?Boolean

Returns:

  • (Boolean)


176
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 176

def thinking_delta? = type == :thinking_delta

#to_hObject

Serialize to a Hash for AMQP/fleet/wire transport.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 146

def to_h
  {
    request_id: request_id,
    conversation_id: conversation_id,
    exchange_id: exchange_id,
    index: index,
    type: type,
    block_index: block_index,
    item_id: item_id,
    delta: delta,
    tool_call: tool_call&.to_h,
    signature: signature,
    usage: usage&.to_h,
    stop_reason: stop_reason,
    metadata: ,
    timestamp: timestamp
  }.compact
end

#to_jsonObject



170
171
172
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 170

def to_json(*)
  to_h.to_json(*)
end

#tool_call_delta?Boolean

Returns:

  • (Boolean)


177
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 177

def tool_call_delta? = type == :tool_call_delta

#usage?Boolean

Returns:

  • (Boolean)


178
# File 'lib/legion/extensions/llm/canonical/chunk.rb', line 178

def usage? = type == :usage