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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f260c2a456cc265c9ae615a84021ad73c8a82dd8bc7969e0c0d6afdab6c18e6
4
- data.tar.gz: 38c4fac7b3fdb5ca97857317f17beda3fc202542aac0aed954d2959bb12c4beb
3
+ metadata.gz: 93611da95712602a9f99e00c4b34523c23838a99d34c3c441ea6bef642231e3f
4
+ data.tar.gz: 6ced6ad0b6091c5a3d53702b867eea5f04d35199892338023aebb6bb452ed867
5
5
  SHA512:
6
- metadata.gz: 37ccea0f649857ee3492c4df9fb9c6c58667d5899bcba2fac5eb2daef119c2bf361c1ae58fe37815a952625ecb8d8722d760ca5860ec99644f04d423da47c6f0
7
- data.tar.gz: 48c4845b82db666058aea36a4d98e20108c6754ba52cd15598c9e18f7b5a7921a9c37f34b81d8b3ece8f9e733f42dc0bf965c4c8120ecf40212e4b75692baa1a
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
- return unless chunk.respond_to?(:input_tokens)
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] = usage_hash(chunk)
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.input_tokens.to_i,
396
- output_tokens: response.output_tokens.to_i,
397
- cache_read_tokens: response.cached_tokens.to_i,
398
- cache_write_tokens: response.cache_creation_tokens.to_i
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?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module LLM
5
- VERSION = '0.9.35'
5
+ VERSION = '0.9.36'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.35
4
+ version: 0.9.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity