lex-llm-vllm 0.3.13 → 0.3.14
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0459c715b28893afc32019e6f2f831ee08824cd3a0224c17fa25164f5f14a9fc'
|
|
4
|
+
data.tar.gz: 72629182ec192e73316f29d1b491b471a1c8cfe6d87e348f2ce00a16f9df8a1b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 60b334c314c6f29257ae7c9a073dfc034536f50f4c58e620137d49d88fb682d12f1abb5882ae792e7a99faffabd3cb01ba990f053f341d10b5478952f527e6bd
|
|
7
|
+
data.tar.gz: 3b602d7548c15a76068645d27da5a94619d7ca1954a2620c8291b2eb4af93384b2a4fe5f241b15487b3b8f7bb99a8294cd474ddac7f94ee1439d9e2d5f76ee08
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.14] - 2026-07-24
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Translator passes `finish_reason` and `usage` through on all streaming chunk types.** vLLM sends `finish_reason` on the same chunk as the last content token. Previously the translator dropped it because the early-return pattern only emitted finish_reason when the delta was empty. Now `text_delta`, `tool_call_delta`, and `thinking_delta` chunks carry `stop_reason` and `usage` when present on the SSE event, enabling the accumulator to capture the real provider signal.
|
|
7
|
+
- **Translator returns both thinking and content when vLLM sends both on the same SSE chunk.** vLLM emits `reasoning` and `content` simultaneously on the boundary between thinking and visible output. Previously the first-branch-wins pattern dropped content when reasoning was present, causing visible characters to disappear from responses (e.g. numbered list items losing their leading digit). `parse_chunk` now returns an array `[thinking_delta, text_delta]` when both fields are present, and the streaming handler iterates them.
|
|
8
|
+
|
|
3
9
|
## [0.3.13] - 2026-07-14
|
|
4
10
|
|
|
5
11
|
### Fixed
|
|
@@ -471,10 +471,14 @@ module Legion
|
|
|
471
471
|
|
|
472
472
|
# Override: delegate SSE chunk parsing to the canonical translator.
|
|
473
473
|
def build_chunk(data)
|
|
474
|
-
|
|
475
|
-
return nil if
|
|
474
|
+
result = translator.parse_chunk(data)
|
|
475
|
+
return nil if result.nil?
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
if result.is_a?(Array)
|
|
478
|
+
result.map { |c| to_legacy_chunk(c, data) }
|
|
479
|
+
else
|
|
480
|
+
to_legacy_chunk(result, data)
|
|
481
|
+
end
|
|
478
482
|
end
|
|
479
483
|
|
|
480
484
|
def parse_list_models_response(response, provider, capabilities)
|
|
@@ -163,23 +163,43 @@ module Legion
|
|
|
163
163
|
)
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
+
chunk_stop_reason = finish_reason ? map_stop_reason(finish_reason) : nil
|
|
167
|
+
chunk_usage = finish_reason && data['usage'] ? Canonical::Usage.from_hash(data['usage']) : nil
|
|
168
|
+
|
|
166
169
|
tool_calls = Array(delta['tool_calls'])
|
|
167
|
-
|
|
170
|
+
unless tool_calls.empty?
|
|
171
|
+
if delta['content'] && !delta['content'].to_s.empty?
|
|
172
|
+
log.debug '[vllm][translator] action=content_dropped_with_tool_call ' \
|
|
173
|
+
"content=#{delta['content'][0, 100].inspect} request_id=#{request_id}"
|
|
174
|
+
end
|
|
175
|
+
return build_tool_call_delta_chunk(tool_calls.first, request_id,
|
|
176
|
+
stop_reason: chunk_stop_reason, usage: chunk_usage)
|
|
177
|
+
end
|
|
168
178
|
|
|
169
179
|
# Thinking delta from reasoning_content
|
|
170
180
|
reasoning_content = delta['reasoning_content'] || delta['reasoning']
|
|
181
|
+
content = delta['content']
|
|
171
182
|
unless reasoning_content.to_s.empty?
|
|
172
|
-
|
|
183
|
+
thinking_chunk = Canonical::Chunk.thinking_delta(
|
|
173
184
|
delta: reasoning_content,
|
|
174
185
|
request_id: request_id,
|
|
175
186
|
block_index: delta.dig('content_block', 'index'),
|
|
176
|
-
item_id: delta['content_block_start']&.dig('id')
|
|
187
|
+
item_id: delta['content_block_start']&.dig('id'),
|
|
188
|
+
stop_reason: content.to_s.empty? ? chunk_stop_reason : nil,
|
|
189
|
+
usage: content.to_s.empty? ? chunk_usage : nil
|
|
177
190
|
)
|
|
191
|
+
return thinking_chunk if content.to_s.empty?
|
|
192
|
+
|
|
193
|
+
content_chunk = parse_text_delta_with_thinking(content, request_id, data,
|
|
194
|
+
stop_reason: chunk_stop_reason, usage: chunk_usage)
|
|
195
|
+
return [thinking_chunk, content_chunk]
|
|
178
196
|
end
|
|
179
197
|
|
|
180
198
|
# Text delta — check for embedded think tags
|
|
181
|
-
|
|
182
|
-
|
|
199
|
+
unless content.to_s.empty?
|
|
200
|
+
return parse_text_delta_with_thinking(content, request_id, data,
|
|
201
|
+
stop_reason: chunk_stop_reason, usage: chunk_usage)
|
|
202
|
+
end
|
|
183
203
|
|
|
184
204
|
nil
|
|
185
205
|
rescue Legion::JSON::ParseError => e
|
|
@@ -635,7 +655,7 @@ module Legion
|
|
|
635
655
|
# fragments carry id: nil and a raw partial-JSON arguments string.
|
|
636
656
|
# The StreamAccumulator keys off a nil id to append fragments to the
|
|
637
657
|
# current tool call, so the id must NOT be synthesized here.
|
|
638
|
-
def build_tool_call_delta_chunk(first_call, request_id)
|
|
658
|
+
def build_tool_call_delta_chunk(first_call, request_id, stop_reason: nil, usage: nil)
|
|
639
659
|
function = first_call.fetch('function', {})
|
|
640
660
|
|
|
641
661
|
tc = Canonical::ToolCall.new(
|
|
@@ -649,7 +669,9 @@ module Legion
|
|
|
649
669
|
Canonical::Chunk.tool_call_delta(
|
|
650
670
|
tool_call: tc,
|
|
651
671
|
request_id: request_id,
|
|
652
|
-
block_index: first_call['index']
|
|
672
|
+
block_index: first_call['index'],
|
|
673
|
+
stop_reason: stop_reason,
|
|
674
|
+
usage: usage
|
|
653
675
|
)
|
|
654
676
|
end
|
|
655
677
|
|
|
@@ -667,11 +689,13 @@ module Legion
|
|
|
667
689
|
# (Previously called ThinkingExtractor.extract_from_content, which is
|
|
668
690
|
# private_class_method in lex-llm >= 0.5.0 and raised NoMethodError on
|
|
669
691
|
# every streamed text delta, silently killing all vLLM streaming.)
|
|
670
|
-
def parse_text_delta_with_thinking(content, request_id, data)
|
|
692
|
+
def parse_text_delta_with_thinking(content, request_id, data, stop_reason: nil, usage: nil)
|
|
671
693
|
Canonical::Chunk.text_delta(
|
|
672
694
|
delta: content,
|
|
673
695
|
request_id: request_id,
|
|
674
|
-
index: data['index']
|
|
696
|
+
index: data['index'],
|
|
697
|
+
stop_reason: stop_reason,
|
|
698
|
+
usage: usage
|
|
675
699
|
)
|
|
676
700
|
end
|
|
677
701
|
|