legion-llm 0.9.35 → 0.9.36
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 +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/legion/llm/call/lex_llm_adapter.rb +83 -6
- data/lib/legion/llm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93611da95712602a9f99e00c4b34523c23838a99d34c3c441ea6bef642231e3f
|
|
4
|
+
data.tar.gz: 6ced6ad0b6091c5a3d53702b867eea5f04d35199892338023aebb6bb452ed867
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa99ed858c6bef1fc214a45d4d59e51f1e9f0262f75dcdbd0f60645d59296edf6fa57e47dfa706dd0b06ec7c7f6dbf572f3832235d0d7125cd9992ec65aa6eee
|
|
7
|
+
data.tar.gz: dfe7e2db5cf883de39a5ac47438408a858372a52dd82230baa4a624e33e17b0558eb50359237345afa5b8a1df432b164149c3fce540304ac56ffbad888110c33
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Legion LLM Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.36] - 2026-05-22
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Providers: `LexLLMAdapter` now preserves streamed token usage from the upstream `llm-gateway.uhg.com` Responses API payload added in LegionIO/legion-llm#130, including gateway-shaped `usage`, `raw[:data]`, and `raw[:response][:usage]` token fields, so LegionIO `response.completed.usage.input_tokens` no longer collapses to `0`
|
|
7
|
+
|
|
3
8
|
## [0.9.35] - 2026-05-22
|
|
4
9
|
|
|
5
10
|
### Fixed
|
|
@@ -336,10 +336,11 @@ module Legion
|
|
|
336
336
|
end
|
|
337
337
|
|
|
338
338
|
def accumulate_stream_usage(accumulator, chunk)
|
|
339
|
-
|
|
339
|
+
usage = usage_hash(chunk)
|
|
340
|
+
return unless token_usage_signal?(chunk, usage)
|
|
340
341
|
|
|
341
342
|
accumulator[:model] = chunk.model_id if chunk.respond_to?(:model_id)
|
|
342
|
-
accumulator[:usage] =
|
|
343
|
+
accumulator[:usage] = merge_usage_hash(accumulator[:usage], usage)
|
|
343
344
|
accumulator[:raw] = chunk.raw if chunk.respond_to?(:raw)
|
|
344
345
|
end
|
|
345
346
|
|
|
@@ -392,13 +393,89 @@ module Legion
|
|
|
392
393
|
|
|
393
394
|
def usage_hash(response)
|
|
394
395
|
{
|
|
395
|
-
input_tokens: response
|
|
396
|
-
output_tokens: response
|
|
397
|
-
cache_read_tokens: response
|
|
398
|
-
cache_write_tokens: response
|
|
396
|
+
input_tokens: extract_token_metric(response, :input_tokens, :prompt_tokens),
|
|
397
|
+
output_tokens: extract_token_metric(response, :output_tokens, :completion_tokens),
|
|
398
|
+
cache_read_tokens: extract_token_metric(response, :cache_read_tokens, :cached_tokens),
|
|
399
|
+
cache_write_tokens: extract_token_metric(response, :cache_write_tokens, :cache_creation_tokens)
|
|
399
400
|
}
|
|
400
401
|
end
|
|
401
402
|
|
|
403
|
+
def token_usage_signal?(response, usage)
|
|
404
|
+
usage.values.any?(&:positive?) ||
|
|
405
|
+
response.respond_to?(:usage) ||
|
|
406
|
+
response.respond_to?(:raw) ||
|
|
407
|
+
response.respond_to?(:input_tokens) ||
|
|
408
|
+
response.respond_to?(:output_tokens)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def merge_usage_hash(existing, incoming)
|
|
412
|
+
current = existing.is_a?(Hash) ? existing : {}
|
|
413
|
+
latest = incoming.is_a?(Hash) ? incoming : {}
|
|
414
|
+
|
|
415
|
+
{
|
|
416
|
+
input_tokens: [current[:input_tokens].to_i, latest[:input_tokens].to_i].max,
|
|
417
|
+
output_tokens: [current[:output_tokens].to_i, latest[:output_tokens].to_i].max,
|
|
418
|
+
cache_read_tokens: [current[:cache_read_tokens].to_i, latest[:cache_read_tokens].to_i].max,
|
|
419
|
+
cache_write_tokens: [current[:cache_write_tokens].to_i, latest[:cache_write_tokens].to_i].max
|
|
420
|
+
}
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def extract_token_metric(response, canonical_key, legacy_key = nil)
|
|
424
|
+
values = token_metric_candidates(response, canonical_key, legacy_key)
|
|
425
|
+
positive = values.find(&:positive?)
|
|
426
|
+
positive || values.first || 0
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def token_metric_candidates(response, canonical_key, legacy_key = nil)
|
|
430
|
+
keys = [canonical_key, legacy_key].compact
|
|
431
|
+
token_metric_sources(response).flat_map do |source|
|
|
432
|
+
keys.filter_map { |key| extract_metric_value(source, key) }
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def token_metric_sources(response)
|
|
437
|
+
sources = [response]
|
|
438
|
+
sources << response.usage if response.respond_to?(:usage)
|
|
439
|
+
sources << response.raw if response.respond_to?(:raw)
|
|
440
|
+
|
|
441
|
+
sources.compact.flat_map { |source| expand_token_metric_source(source) }.compact.uniq
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def expand_token_metric_source(source, depth = 0)
|
|
445
|
+
return [] if source.nil?
|
|
446
|
+
return [source] unless source.respond_to?(:key?) && depth < 3
|
|
447
|
+
|
|
448
|
+
nested = [source]
|
|
449
|
+
nested << hash_value(source, :usage)
|
|
450
|
+
nested << hash_value(source, :data)
|
|
451
|
+
nested << hash_value(source, :response)
|
|
452
|
+
nested.compact.flat_map { |entry| [entry, *expand_token_metric_source(entry, depth + 1)] }
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def extract_metric_value(source, key)
|
|
456
|
+
if source.respond_to?(key)
|
|
457
|
+
value = source.public_send(key)
|
|
458
|
+
return value.to_i unless value.nil?
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
return nil unless source.respond_to?(:key?)
|
|
462
|
+
|
|
463
|
+
value = hash_value(source, key)
|
|
464
|
+
value&.to_i
|
|
465
|
+
rescue StandardError => e
|
|
466
|
+
log.debug "[llm][adapter] action=extract_metric_value key=#{key} class=#{source.class} error=#{e.class}: #{e.message}"
|
|
467
|
+
nil
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def hash_value(hash, key)
|
|
471
|
+
return hash[key] if hash.key?(key)
|
|
472
|
+
|
|
473
|
+
string_key = key.to_s
|
|
474
|
+
return hash[string_key] if hash.key?(string_key)
|
|
475
|
+
|
|
476
|
+
nil
|
|
477
|
+
end
|
|
478
|
+
|
|
402
479
|
def stream_thinking_hash(accumulator)
|
|
403
480
|
thinking_text = accumulator[:thinking_text]
|
|
404
481
|
return nil if thinking_text.empty?
|
data/lib/legion/llm/version.rb
CHANGED