lex-llm 0.6.15 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07f6e605230a187e17667abdf947db738dab8b823783cde124766278159c49f1
4
- data.tar.gz: e1858e04a213ed78175c8647c74f05a5c300db4a8a54caadaba523cb3a1e71ef
3
+ metadata.gz: 3ba576c21739b35e60594706468f7a05769871b25e5acd466ae01cbc451eec70
4
+ data.tar.gz: e0ac7077fdc03270059abe385f202b681928e62d667f0cefad10a0a7d1b79760
5
5
  SHA512:
6
- metadata.gz: 10216387247f307ee05fe4e06a199cbf6cc762f6869636b16043adb18e1fdae355127f99e433e67614580e3c85b3d18b9d6199291d2703167184ee93e8810745
7
- data.tar.gz: f010c914eddd807fc13593692a6fc20353ea1c12487838eed859493088b908a64a6459ee1866d5ffaaa9e4b3924e521b8ed3e79fce9e94fe27364283fb7be62e
6
+ metadata.gz: 997f024896ce8d0eb54d8574615312c0280efea81ecae75859b3bd702b8dd65aae0fbd5f615801e3e43b9d50cf6079ce4638d9a39488edb7e3a86887ee94a977
7
+ data.tar.gz: 3a33857e99d99a8f3358e781d46903560cb6932026d6889be2a922e23b865f3c2dacef4e38bd6a34e52af35474eab7f7e935d4c68360a8c56e2758836648d589
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.16 - 2026-08-04
4
+
5
+ ### Fixed
6
+ - **Interleaved streaming tool-call fragments are correlated by provider index.** `ToolCall` now carries the provider wire index, and `StreamAccumulator` maps continuation fragments back to the call opened at that index. Parallel calls no longer send every id-less fragment to the most recently opened call, which previously lost one call's arguments and contaminated another's.
7
+
3
8
  ## 0.6.15 - 2026-07-31
4
9
 
5
10
  ### Fixed
@@ -26,6 +26,7 @@ module Legion
26
26
  @untagged_preamble_pending = true
27
27
  @untagged_preamble_buffer = +''
28
28
  @latest_tool_call_id = nil
29
+ @index_to_id = {}
29
30
  end
30
31
 
31
32
  def add(chunk)
@@ -158,13 +159,15 @@ module Legion
158
159
  end
159
160
 
160
161
  def start_tool_call(tool_call)
161
- @tool_calls[tool_call.id] = ToolCall.new(
162
- id: tool_call.id.empty? ? SecureRandom.uuid : tool_call.id,
162
+ resolved_id = tool_call.id.empty? ? SecureRandom.uuid : tool_call.id
163
+ @tool_calls[resolved_id] = ToolCall.new(
164
+ id: resolved_id,
163
165
  name: tool_call.name,
164
166
  arguments: mutable_tool_arguments(tool_call.arguments),
165
167
  thought_signature: tool_call.thought_signature
166
168
  )
167
- @latest_tool_call_id = tool_call.id
169
+ @latest_tool_call_id = resolved_id
170
+ @index_to_id[tool_call.index] = resolved_id unless tool_call.index.nil?
168
171
  end
169
172
 
170
173
  def mutable_tool_arguments(arguments)
@@ -186,10 +189,20 @@ module Legion
186
189
  thought_signature: tool_call.thought_signature
187
190
  )
188
191
  @latest_tool_call_id = generated_id
192
+ @index_to_id[tool_call.index] = generated_id unless tool_call.index.nil?
189
193
  end
190
194
 
195
+ # Correlate a continuation fragment (id=nil, name=nil) to its call by the
196
+ # provider's authoritative wire index. Interleaved parallel calls
197
+ # (opener0, opener1, frag0, frag1, ...) only reassemble correctly when
198
+ # each fragment lands on the call at ITS index; recency (@latest_tool_call_id)
199
+ # sends every fragment to the last-opened call, losing one call's arguments
200
+ # and contaminating the other. Recency is retained only as the fallback for
201
+ # providers that emit no index.
191
202
  def append_tool_call_fragment(tool_call)
192
- existing = @tool_calls[@latest_tool_call_id]
203
+ target_id = @index_to_id[tool_call.index] if tool_call.index
204
+ target_id ||= @latest_tool_call_id
205
+ existing = @tool_calls[target_id]
193
206
  return unless existing
194
207
 
195
208
  existing.arguments << tool_call.arguments.to_s
@@ -5,14 +5,19 @@ module Legion
5
5
  module Llm
6
6
  # Represents a function call from an AI model to a Tool.
7
7
  class ToolCall
8
- attr_reader :id, :name, :arguments
8
+ attr_reader :id, :name, :arguments, :index
9
9
  attr_accessor :thought_signature
10
10
 
11
- def initialize(id:, name:, arguments: {}, thought_signature: nil)
11
+ # index carries the provider's authoritative wire position
12
+ # (delta.tool_calls[N].index) so the StreamAccumulator can correlate
13
+ # interleaved argument fragments to the right parallel call by index
14
+ # rather than by recency. nil for non-streaming/single-call paths.
15
+ def initialize(id:, name:, arguments: {}, thought_signature: nil, index: nil)
12
16
  @id = id
13
17
  @name = name
14
18
  @arguments = arguments
15
19
  @thought_signature = thought_signature
20
+ @index = index
16
21
  end
17
22
 
18
23
  def to_h
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Llm
6
- VERSION = '0.6.15'
6
+ VERSION = '0.6.16'
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.15
4
+ version: 0.6.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO