lex-llm-gemini 0.1.5 → 0.1.7

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: b4a35ee0fc2182185d444789b3bd23581e9e2b89cd9998299b5492e2e241236b
4
- data.tar.gz: f0c9b84a6fe7bc3889646aeb856ff0484b4cf0216ea8f949cf01247e6d3b0ff0
3
+ metadata.gz: 3b4d2bbf521fe7cd5757553aff5a6d61c1a9e9ffbe18c683058710ab19a8a6ff
4
+ data.tar.gz: 98d0db7b428b68d0543682c43a27d77fde00aee8bb02377195c37224e9429fef
5
5
  SHA512:
6
- metadata.gz: fe8ffa42e7427aaffa21453ae6edc5abfd755d483a9f63a061ef3a839f8dc47374478a4339c212b0891938e4574cd9ac68a53f2c2ff072ff5f59d5f3a1a4a49a
7
- data.tar.gz: d56491f783d2d71029aa8cce971d825f1ed5388317f18b8d77883082ad239727c8c1976ccf93d22acb5b5eda5768e15ccd4e0867fecd378f00896e9c227bae87
6
+ metadata.gz: c40e3408f31507e4de8622e65676a2eb67849a05964dee02585545647563a4655fe808a3673782b1819bc7828a1f9fd3af900aa57ce09f6119ba28caefe01813
7
+ data.tar.gz: bacceead0f9edceed7da8efb605fe516f0ea7110c57f47cf8b16bad77805e74d8a1a99d37ffec056fa4affdd2c4e6a30f45c7a5812c03221182038373a45c784
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.7 - 2026-04-30
4
+
5
+ - Audit logging, rescue blocks, and README for full observability.
6
+ - Add `Legion::Logging::Helper` to every module and class in lib/.
7
+ - Replace all bare rescue blocks and custom `log_publish_failure` with `handle_exception(e, level:, handled:, operation:)`.
8
+ - Add info-level action logging for model listing and registry publishing.
9
+ - Update README to reflect registry event publishing and observability patterns.
10
+
11
+ ## 0.1.6 - 2026-04-28
12
+
13
+ - Publish best-effort `llm.registry` discovered-model availability events when transport is already loaded.
14
+
3
15
  ## 0.1.5 - 2026-04-28
4
16
 
5
17
  - Require current shared Legion JSON, logging, settings, and `lex-llm >= 0.1.5` runtime dependencies.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # lex-llm-gemini
2
2
 
3
- LegionIO LLM provider extension for Gemini.
3
+ LegionIO LLM provider extension for Google Gemini.
4
4
 
5
5
  This gem lives under `Legion::Extensions::Llm::Gemini` and depends on `lex-llm` for shared provider-neutral routing, fleet, and schema primitives.
6
6
 
@@ -10,11 +10,24 @@ Load it with `require 'legion/extensions/llm/gemini'`.
10
10
 
11
11
  - `Legion::Extensions::Llm::Provider` registration as `:gemini`
12
12
  - Gemini-native chat requests through `POST /v1beta/{model=models/*}:generateContent`
13
- - streaming chat support through `POST /v1beta/{model=models/*}:streamGenerateContent?alt=sse`
14
- - model discovery through `GET /v1beta/models`
15
- - embeddings through `POST /v1beta/{model=models/*}:embedContent`
16
- - normalized chat, streaming, vision, function calling, and embedding capability mapping from `supportedGenerationMethods`
17
- - shared fleet/default settings via `Legion::Extensions::Llm.provider_settings`
13
+ - Streaming chat support through `POST /v1beta/{model=models/*}:streamGenerateContent?alt=sse`
14
+ - Model discovery through `GET /v1beta/models`
15
+ - Embeddings through `POST /v1beta/{model=models/*}:embedContent`
16
+ - Normalized chat, streaming, vision, function calling, and embedding capability mapping from `supportedGenerationMethods`
17
+ - Best-effort `llm.registry` availability events published to AMQP when transport is loaded
18
+ - Shared fleet/default settings via `Legion::Extensions::Llm.provider_settings`
19
+
20
+ ## File Map
21
+
22
+ | Path | Purpose |
23
+ |------|---------|
24
+ | `lib/legion/extensions/llm/gemini.rb` | Entry point; provider registration |
25
+ | `lib/legion/extensions/llm/gemini/provider.rb` | Gemini provider (chat, streaming, models, embeddings) |
26
+ | `lib/legion/extensions/llm/gemini/registry_event_builder.rb` | Builds sanitized lex-llm registry event envelopes |
27
+ | `lib/legion/extensions/llm/gemini/registry_publisher.rb` | Best-effort async publisher for model availability events |
28
+ | `lib/legion/extensions/llm/gemini/transport/exchanges/llm_registry.rb` | `llm.registry` AMQP topic exchange |
29
+ | `lib/legion/extensions/llm/gemini/transport/messages/registry_event.rb` | AMQP message wrapper for registry events |
30
+ | `lib/legion/extensions/llm/gemini/version.rb` | `VERSION` constant |
18
31
 
19
32
  ## Defaults
20
33
 
@@ -45,3 +58,24 @@ Legion::Extensions::Llm.configure do |config|
45
58
  config.default_embedding_model = "gemini-embedding-001"
46
59
  end
47
60
  ```
61
+
62
+ ## Observability
63
+
64
+ Every module and class includes or extends `Legion::Logging::Helper`:
65
+
66
+ - **Info-level logging** on `list_models` and registry event publishing.
67
+ - **All rescue blocks** call `handle_exception(e, level:, handled:, operation:)` for structured exception telemetry.
68
+ - Registry publishing is best-effort; failures are handled at `:debug` or `:warn` level and never block the caller.
69
+
70
+ ## Development
71
+
72
+ ```bash
73
+ bundle install
74
+ bundle exec rspec # 0 failures
75
+ bundle exec rubocop -A # auto-fix
76
+ bundle exec rubocop # lint check
77
+ ```
78
+
79
+ ## License
80
+
81
+ Apache-2.0
@@ -8,11 +8,19 @@ module Legion
8
8
  module Gemini
9
9
  # Gemini provider implementation for the Legion::Extensions::Llm base provider contract.
10
10
  class Provider < Legion::Extensions::Llm::Provider # rubocop:disable Metrics/ClassLength
11
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
12
+
11
13
  class << self
14
+ attr_writer :registry_publisher
15
+
12
16
  def slug = 'gemini'
13
17
  def configuration_options = %i[gemini_api_key gemini_api_base]
14
18
  def configuration_requirements = %i[gemini_api_key]
15
19
  def capabilities = Capabilities
20
+
21
+ def registry_publisher
22
+ @registry_publisher ||= RegistryPublisher.new
23
+ end
16
24
  end
17
25
 
18
26
  # Capability predicates for Gemini API models.
@@ -87,6 +95,14 @@ module Legion
87
95
  "#{model_path(model)}:embedContent"
88
96
  end
89
97
 
98
+ def list_models
99
+ log.info { 'listing available Gemini models' }
100
+ super.tap do |models|
101
+ log.info { "discovered #{models.size} Gemini model(s); publishing to registry" }
102
+ self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
103
+ end
104
+ end
105
+
90
106
  private
91
107
 
92
108
  def model_path(model)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Gemini
7
+ # Builds sanitized lex-llm registry envelopes for Gemini provider state.
8
+ class RegistryEventBuilder
9
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
10
+
11
+ def model_available(model, readiness:)
12
+ registry_event_class.available(
13
+ model_offering(model),
14
+ runtime: runtime_metadata,
15
+ health: model_health(readiness),
16
+ metadata: model_metadata(model)
17
+ )
18
+ end
19
+
20
+ private
21
+
22
+ def model_offering(model)
23
+ {
24
+ provider_family: :gemini,
25
+ provider_instance: provider_instance,
26
+ transport: :http,
27
+ model: model.id,
28
+ usage_type: usage_type_for(model),
29
+ capabilities: Array(model.capabilities).map(&:to_sym),
30
+ limits: model_limits(model),
31
+ metadata: { lex: :llm_gemini, model_name: model.name }.compact
32
+ }
33
+ end
34
+
35
+ def model_health(readiness)
36
+ ready = readiness.fetch(:ready, true) == true
37
+ { ready:, status: ready ? :available : :degraded }
38
+ end
39
+
40
+ def model_metadata(model)
41
+ { extension: :lex_llm_gemini, provider: :gemini, model_type: model.type }
42
+ end
43
+
44
+ def runtime_metadata
45
+ { node: provider_instance }
46
+ end
47
+
48
+ def model_limits(model)
49
+ {
50
+ context_window: model.context_window,
51
+ max_output_tokens: model.max_output_tokens
52
+ }.compact
53
+ end
54
+
55
+ def usage_type_for(model)
56
+ model.type == 'embedding' ? :embedding : :inference
57
+ end
58
+
59
+ def provider_instance
60
+ configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
61
+ value = configured_node.to_s.strip
62
+ value.empty? ? :gemini : value.to_sym
63
+ rescue StandardError => e
64
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.registry.provider_instance')
65
+ :gemini
66
+ end
67
+
68
+ def registry_event_class
69
+ ::Legion::Extensions::Llm::Routing::RegistryEvent
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Gemini
7
+ # Best-effort publisher for Gemini provider availability events.
8
+ class RegistryPublisher
9
+ include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
10
+
11
+ APP_ID = 'lex-llm-gemini'
12
+
13
+ def initialize(builder: RegistryEventBuilder.new)
14
+ @builder = builder
15
+ end
16
+
17
+ def publish_models_async(models, readiness:)
18
+ log.info { "publishing #{Array(models).size} model event(s) to llm.registry" }
19
+ schedule do
20
+ Array(models).each do |model|
21
+ publish_event(@builder.model_available(model, readiness:))
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def schedule(&)
29
+ return false unless publishing_available?
30
+
31
+ Thread.new do
32
+ Thread.current.abort_on_exception = false
33
+ yield
34
+ rescue StandardError => e
35
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.registry.schedule_thread')
36
+ end
37
+ rescue StandardError => e
38
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.registry.schedule')
39
+ false
40
+ end
41
+
42
+ def publish_event(event)
43
+ return false unless publishing_available?
44
+
45
+ message_class.new(event:, app_id: APP_ID).publish(spool: false)
46
+ rescue StandardError => e
47
+ handle_exception(e, level: :warn, handled: true, operation: 'gemini.registry.publish_event')
48
+ false
49
+ end
50
+
51
+ def publishing_available?
52
+ return false unless registry_event_available?
53
+ return false unless transport_message_available?
54
+ return true unless defined?(::Legion::Transport::Connection)
55
+ return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
56
+
57
+ ::Legion::Transport::Connection.session_open?
58
+ rescue StandardError => e
59
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.registry.publishing_available?')
60
+ false
61
+ end
62
+
63
+ def registry_event_available?
64
+ defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
65
+ end
66
+
67
+ def transport_message_available?
68
+ return true if message_class_defined?
69
+ return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
70
+
71
+ require 'legion/extensions/llm/gemini/transport/messages/registry_event'
72
+ message_class_defined?
73
+ rescue LoadError => e
74
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.registry.transport_load')
75
+ false
76
+ end
77
+
78
+ def message_class_defined?
79
+ defined?(::Legion::Extensions::Llm::Gemini::Transport::Messages::RegistryEvent)
80
+ end
81
+
82
+ def message_class
83
+ ::Legion::Extensions::Llm::Gemini::Transport::Messages::RegistryEvent
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Gemini
7
+ module Transport
8
+ module Exchanges
9
+ # Topic exchange for Gemini provider availability events.
10
+ class LlmRegistry < ::Legion::Transport::Exchange
11
+ def exchange_name
12
+ 'llm.registry'
13
+ end
14
+
15
+ def default_type
16
+ 'topic'
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/llm/gemini/transport/exchanges/llm_registry'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Llm
8
+ module Gemini
9
+ module Transport
10
+ module Messages
11
+ # Publishes lex-llm RegistryEvent envelopes to the llm.registry exchange.
12
+ class RegistryEvent < ::Legion::Transport::Message
13
+ def initialize(event:, **options)
14
+ super(**event.to_h.merge(options))
15
+ end
16
+
17
+ def exchange
18
+ Transport::Exchanges::LlmRegistry
19
+ end
20
+
21
+ def routing_key
22
+ @options[:routing_key] || "llm.registry.#{@options.fetch(:event_type)}"
23
+ end
24
+
25
+ def type
26
+ 'llm.registry.event'
27
+ end
28
+
29
+ def app_id
30
+ @options[:app_id] || RegistryPublisher::APP_ID
31
+ end
32
+
33
+ def persistent # rubocop:disable Naming/PredicateMethod
34
+ false
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Gemini
7
- VERSION = '0.1.5'
7
+ VERSION = '0.1.7'
8
8
  end
9
9
  end
10
10
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/llm'
4
+ require 'legion/extensions/llm/gemini/registry_event_builder'
5
+ require 'legion/extensions/llm/gemini/registry_publisher'
4
6
  require 'legion/extensions/llm/gemini/provider'
5
7
  require 'legion/extensions/llm/gemini/version'
6
8
 
@@ -10,6 +12,7 @@ module Legion
10
12
  # Gemini provider extension namespace.
11
13
  module Gemini
12
14
  extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
15
+ extend Legion::Logging::Helper if defined?(Legion::Logging::Helper)
13
16
 
14
17
  PROVIDER_FAMILY = :gemini
15
18
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-gemini
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO
@@ -84,6 +84,10 @@ files:
84
84
  - lex-llm-gemini.gemspec
85
85
  - lib/legion/extensions/llm/gemini.rb
86
86
  - lib/legion/extensions/llm/gemini/provider.rb
87
+ - lib/legion/extensions/llm/gemini/registry_event_builder.rb
88
+ - lib/legion/extensions/llm/gemini/registry_publisher.rb
89
+ - lib/legion/extensions/llm/gemini/transport/exchanges/llm_registry.rb
90
+ - lib/legion/extensions/llm/gemini/transport/messages/registry_event.rb
87
91
  - lib/legion/extensions/llm/gemini/version.rb
88
92
  homepage: https://github.com/LegionIO/lex-llm-gemini
89
93
  licenses: