lex-llm-gateway 0.2.9 → 0.2.12

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: bce7fb746d14ff63db46cb4fc87613a5a0834c61e5e067dc4e5bf6122dbd251a
4
- data.tar.gz: 2d7c0d14bef999fece93d633dd74c700d2594e76d1aae216a2afd3b51f13a534
3
+ metadata.gz: 23b44bc07cb28fc0e364b119984909f1d8becba720ea547b1255a8512188dec7
4
+ data.tar.gz: a4aae0ecf4e1b053405f74628af518156ae2ffc3d0678b4f920771a9e6a68ee6
5
5
  SHA512:
6
- metadata.gz: 488014e8667b9782d18a56dee95250ce8869aff2f9d9e0a5254d77f3d6a5ffd9a1cea802c66766fa50da1e2fa0b5801fb6b1b17b460dfb39305ddd3eaa2aeb81
7
- data.tar.gz: a65be2e6996f18b98cbdafb3f1e6145671a6eb4f37f63e7b6e500e272d8d16ba26e5414f32489cd5569c16c6881c17dab601833f1eae5edf2516c177077e54f3
6
+ metadata.gz: 1cf5d4fe5d3f0728dee2dfa04019f62c7e2fd799527df6a79b069f9aabf4ef7cff44674fa0405406e200705faaa03eff282c60ab9b0ca3db25a5f93fa7bb6c1f
7
+ data.tar.gz: c01a0edb556ccf0346b111b6187f05e4083dde3c52ecbc43707fbb720c40a73501a76a98c85e37389512fb2db0184d631ba98edbecab77b456cd1af1401b882d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.12] - 2026-03-24
4
+
5
+ ### Fixed
6
+ - fix `uninitialized constant Legion::Extensions::Llm::Gateway` when `ensure_namespace` pre-creates `Llm` as empty module before gem loads — unconditionally `remove_const(:Llm)` then reassign alias to `LLM`
7
+
8
+ ## [0.2.11] - 2026-03-23
9
+
10
+ ### Changed
11
+ - Add `caller:` identity to all `Legion::LLM` call sites: 3 pipeline-path calls in `Runners::Inference` (`chat`, `embed`, `structured`) and 4 fleet worker calls in `Runners::FleetHandler` (`call_chat` ×2, `call_structured`, `call_embed`) with `extension: 'lex-llm-gateway'` and `operation: 'inference'`/`'fleet'`
12
+
13
+ ## [0.2.10] - 2026-03-23
14
+
15
+ ### Added
16
+ - ProviderStats runner: health_report, provider_detail, circuit_summary for LLM provider observability
17
+ - Reports circuit breaker state, routing adjustment, and health status per provider
18
+ - 5 specs covering report generation, detail, circuit summary, and fallback behavior
19
+
3
20
  ## [0.2.9] - 2026-03-23
4
21
 
5
22
  ### Added
@@ -48,9 +48,11 @@ module Legion
48
48
  def call_chat(payload)
49
49
  messages = payload[:messages]
50
50
  if messages.is_a?(Array) && messages.size > 1
51
- Legion::LLM.chat(model: payload[:model], messages: messages)
51
+ Legion::LLM.chat(model: payload[:model], messages: messages,
52
+ caller: { extension: 'lex-llm-gateway', operation: 'fleet' })
52
53
  else
53
- Legion::LLM.chat(model: payload[:model], message: messages&.dig(0, :content))
54
+ Legion::LLM.chat(model: payload[:model], message: messages&.dig(0, :content),
55
+ caller: { extension: 'lex-llm-gateway', operation: 'fleet' })
54
56
  end
55
57
  end
56
58
 
@@ -58,13 +60,15 @@ module Legion
58
60
  Legion::LLM.structured(
59
61
  model: payload[:model],
60
62
  messages: payload[:messages],
61
- schema: payload[:schema]
63
+ schema: payload[:schema],
64
+ caller: { extension: 'lex-llm-gateway', operation: 'fleet' }
62
65
  )
63
66
  end
64
67
 
65
68
  def call_embed(payload)
66
69
  text = payload[:text] || payload.dig(:messages, 0, :content)
67
- Legion::LLM.embed(model: payload[:model], text: text)
70
+ Legion::LLM.embed(model: payload[:model], text: text,
71
+ caller: { extension: 'lex-llm-gateway', operation: 'fleet' })
68
72
  end
69
73
 
70
74
  def build_response(correlation_id, response)
@@ -5,10 +5,16 @@ module Legion
5
5
  module LLM
6
6
  module Gateway
7
7
  module Runners
8
- module Inference
8
+ module Inference # rubocop:disable Metrics/ModuleLength
9
9
  module_function
10
10
 
11
- def chat(model: nil, provider: nil, **opts)
11
+ def chat(model: nil, provider: nil, **opts) # rubocop:disable Metrics/MethodLength
12
+ if pipeline_available?
13
+ log_deprecation(:chat)
14
+ return Legion::LLM.chat(model: model, provider: provider,
15
+ caller: { extension: 'lex-llm-gateway', operation: 'inference' }, **opts)
16
+ end
17
+
12
18
  start_ms = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
13
19
  response = dispatch_chat(model: model, provider: provider, **opts)
14
20
  elapsed_ms = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - start_ms
@@ -17,7 +23,13 @@ module Legion
17
23
  response
18
24
  end
19
25
 
20
- def embed(text: nil, model: nil, provider: nil, **)
26
+ def embed(text: nil, model: nil, provider: nil, **) # rubocop:disable Metrics/MethodLength
27
+ if pipeline_available?
28
+ log_deprecation(:embed)
29
+ return Legion::LLM.embed(text, model: model, provider: provider,
30
+ caller: { extension: 'lex-llm-gateway', operation: 'inference' }, **)
31
+ end
32
+
21
33
  start_ms = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
22
34
  response = dispatch_embed(text: text, model: model, provider: provider, **)
23
35
  elapsed_ms = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) - start_ms
@@ -26,7 +38,14 @@ module Legion
26
38
  response
27
39
  end
28
40
 
29
- def structured(messages: nil, schema: nil, model: nil, provider: nil, **)
41
+ def structured(messages: nil, schema: nil, model: nil, provider: nil, **) # rubocop:disable Metrics/MethodLength
42
+ if pipeline_available?
43
+ log_deprecation(:structured)
44
+ return Legion::LLM.structured(messages: messages, schema: schema, model: model,
45
+ provider: provider,
46
+ caller: { extension: 'lex-llm-gateway', operation: 'inference' }, **)
47
+ end
48
+
30
49
  start_ms = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond)
31
50
  response = dispatch_structured(messages: messages, schema: schema, model: model,
32
51
  provider: provider, **)
@@ -36,6 +55,21 @@ module Legion
36
55
  response
37
56
  end
38
57
 
58
+ def pipeline_available?
59
+ defined?(Legion::LLM::Pipeline::Executor) &&
60
+ defined?(Legion::LLM) &&
61
+ Legion::LLM.respond_to?(:pipeline_enabled?) &&
62
+ Legion::LLM.pipeline_enabled?
63
+ end
64
+
65
+ def log_deprecation(method)
66
+ return unless defined?(Legion::Logging)
67
+
68
+ Legion::Logging.warn(
69
+ "lex-llm-gateway is deprecated for #{method}, use Legion::LLM.#{method} directly"
70
+ )
71
+ end
72
+
39
73
  def dispatch_chat(message: nil, messages: nil, model: nil, provider: nil, **opts)
40
74
  tier = opts[:tier]
41
75
  Legion::Logging.debug "[Gateway::Inference] dispatch_chat tier=#{tier}" if defined?(Legion::Logging)
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module LLM
6
+ module Gateway
7
+ module Runners
8
+ module ProviderStats
9
+ module_function
10
+
11
+ def health_report
12
+ return unavailable_result unless router_available?
13
+
14
+ tracker = health_tracker
15
+ providers = known_providers
16
+ providers.map { |name| provider_entry(name, tracker) }
17
+ end
18
+
19
+ def provider_detail(provider:)
20
+ return unavailable_result unless router_available?
21
+
22
+ provider_entry(provider, health_tracker)
23
+ end
24
+
25
+ def circuit_summary
26
+ return unavailable_result unless router_available?
27
+
28
+ tracker = health_tracker
29
+ providers = known_providers
30
+ {
31
+ total: providers.size,
32
+ closed: providers.count { |p| tracker.circuit_state(p) == :closed },
33
+ open: providers.count { |p| tracker.circuit_state(p) == :open },
34
+ half_open: providers.count { |p| tracker.circuit_state(p) == :half_open }
35
+ }
36
+ end
37
+
38
+ def router_available?
39
+ defined?(Legion::LLM::Router) &&
40
+ Legion::LLM::Router.respond_to?(:routing_enabled?) &&
41
+ Legion::LLM::Router.routing_enabled?
42
+ end
43
+
44
+ def health_tracker
45
+ Legion::LLM::Router.health_tracker
46
+ end
47
+
48
+ def known_providers
49
+ return [] unless defined?(Legion::Settings)
50
+
51
+ providers = Legion::Settings.dig(:llm, :providers)
52
+ return [] unless providers.is_a?(Hash)
53
+
54
+ providers.select { |_, cfg| cfg.is_a?(Hash) && cfg[:enabled] }
55
+ .keys
56
+ .map(&:to_sym)
57
+ rescue StandardError
58
+ []
59
+ end
60
+
61
+ def provider_entry(name, tracker)
62
+ {
63
+ provider: name.to_s,
64
+ circuit: tracker.circuit_state(name).to_s,
65
+ adjustment: tracker.adjustment(name),
66
+ healthy: tracker.circuit_state(name) != :open
67
+ }
68
+ end
69
+
70
+ def unavailable_result
71
+ { success: false, error: 'router_not_available' }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module LLM
6
6
  module Gateway
7
- VERSION = '0.2.9'
7
+ VERSION = '0.2.12'
8
8
  end
9
9
  end
10
10
  end
@@ -9,6 +9,7 @@ module Legion
9
9
  extend Legion::Extensions::Core if Legion::Extensions.const_defined?(:Core)
10
10
  end
11
11
  end
12
- Llm = LLM unless const_defined?(:Llm, false)
12
+ remove_const(:Llm) if const_defined?(:Llm, false)
13
+ Llm = LLM
13
14
  end
14
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-gateway
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -191,6 +191,7 @@ files:
191
191
  - lib/legion/extensions/llm/gateway/runners/inference.rb
192
192
  - lib/legion/extensions/llm/gateway/runners/metering.rb
193
193
  - lib/legion/extensions/llm/gateway/runners/metering_writer.rb
194
+ - lib/legion/extensions/llm/gateway/runners/provider_stats.rb
194
195
  - lib/legion/extensions/llm/gateway/runners/usage_reporter.rb
195
196
  - lib/legion/extensions/llm/gateway/transport/exchanges/inference.rb
196
197
  - lib/legion/extensions/llm/gateway/transport/exchanges/metering.rb