lex-llm-vertex 0.1.3 → 0.1.5

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: fa2b76730bd06bc3d37af2ea20ad016bdb4015a0776601e2080d1ab8d1c09168
4
- data.tar.gz: ed7bfbc8083c3ad758f240cbb14e8f9d95cf9810dcbf7f17ed8e0a2077fdc4d7
3
+ metadata.gz: 2a7dc0a783d3cb0961f7881a4ef0412d1842730852518f1f7ac859ad2cbb944d
4
+ data.tar.gz: fabd6084238f3473c1b3a75e53e1f19de820f6ce12231316c38b39a33a2a506f
5
5
  SHA512:
6
- metadata.gz: 68a7cc952a88e6b155173006fe9728a30ae71f32dca491ec8726bc88f56cf70a54ed7c50929f9b9e28c6bd14d91d5401c429329af678b747ad42c7b615ef368d
7
- data.tar.gz: 5d511da4251f5e5b06a21dd29b5ab2fc773af9434af7b0c211da4c584940b538c6c90938f7226281aea82778a88e8b1b2fd5e5a3470917b5fa3b70496a9167d2
6
+ metadata.gz: 403c6201955ec9d77611f0ade5a338b58d4164f9567bab09b927c73427a06a2214e6828dff3f3fe82ac435ed33932195454a426f1c78bbac2f74c0743db26c27
7
+ data.tar.gz: 91592103291579ba34d8bd61dd1e066111b7a68449585bd96d8d5ca7f38b141dfc0c9d37db532c73721eb3de88d9bdca302510366088464678156c1e978132e8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.5] - 2026-04-30
4
+
5
+ - Add `Legion::Logging::Helper` to all modules and classes for structured logging
6
+ - Replace ad-hoc `log_publish_failure` with `handle_exception` in RegistryPublisher
7
+ - Add `handle_exception` to every rescue block with correct level, handled, and operation
8
+ - Add info-level logging for key provider actions: chat, stream, embed, count_tokens, discover_offerings, health
9
+ - Update README to reflect current architecture, file map, and observability conventions
10
+
11
+ ## [0.1.4] - 2026-04-30
12
+
13
+ - Add headers: parameter to complete method for base provider contract compliance
14
+
3
15
  ## 0.1.3 - 2026-04-28
4
16
 
5
17
  - Remove the unused runtime `legion/settings` require while preserving the gemspec dependency.
data/README.md CHANGED
@@ -48,6 +48,18 @@ provider.count_tokens(messages, model: model)
48
48
 
49
49
  `discover_offerings(live: false)` returns a conservative static catalog for routing defaults and unit tests. `discover_offerings(live: true)` calls the Vertex publisher models listing endpoint and maps returned model data into `Legion::Extensions::Llm::Routing::ModelOffering` records.
50
50
 
51
+ ## Static Model Catalog
52
+
53
+ | Model | Alias | Publisher | Family | API Mode |
54
+ |-------|-------|-----------|--------|----------|
55
+ | gemini-2.5-flash | gemini-flash | google | gemini | generateContent |
56
+ | gemini-2.5-pro | gemini-pro | google | gemini | generateContent |
57
+ | gemini-embedding-001 | gemini-embedding | google | gemini | predict (embedding) |
58
+ | text-embedding-005 | text-embedding | google | gemini | predict (embedding) |
59
+ | claude-sonnet-4-5 | claude-sonnet | anthropic | anthropic | rawPredict |
60
+ | mistral-medium-3 | mistral-medium | mistralai | mistral | rawPredict |
61
+ | llama-4-maverick | llama-4-maverick | meta | meta | rawPredict |
62
+
51
63
  ## Model Offerings
52
64
 
53
65
  Every offering uses:
@@ -60,6 +72,30 @@ Every offering uses:
60
72
 
61
73
  Known aliases are intentionally small and configurable. For example, `gemini-flash` resolves to `gemini-2.5-flash`, while the offering preserves `projects/{project}/locations/{location}/publishers/google/models/gemini-2.5-flash`.
62
74
 
75
+ ## Registry Events
76
+
77
+ When transport is available, the `RegistryPublisher` publishes best-effort readiness and offering availability events to the `llm.registry` topic exchange using `lex-llm` registry envelopes. Events are published asynchronously in background threads and never block the caller.
78
+
79
+ ## File Map
80
+
81
+ | Path | Purpose |
82
+ |------|---------|
83
+ | `lib/legion/extensions/llm/vertex.rb` | Namespace module, default settings, provider registration |
84
+ | `lib/legion/extensions/llm/vertex/provider.rb` | Vertex AI provider: chat, stream, embed, count_tokens, health, discovery |
85
+ | `lib/legion/extensions/llm/vertex/registry_publisher.rb` | Async best-effort llm.registry event publisher |
86
+ | `lib/legion/extensions/llm/vertex/registry_event_builder.rb` | Builds sanitized registry event envelopes |
87
+ | `lib/legion/extensions/llm/vertex/version.rb` | `VERSION` constant |
88
+ | `lib/legion/extensions/llm/vertex/transport/exchanges/llm_registry.rb` | `llm.registry` topic exchange definition |
89
+ | `lib/legion/extensions/llm/vertex/transport/messages/registry_event.rb` | Transport message for registry events |
90
+
91
+ ## Observability
92
+
93
+ All modules and classes use `Legion::Logging::Helper` for structured logging:
94
+
95
+ - **Info-level logging** on key provider actions: `chat`, `stream`, `embed`, `count_tokens`, `discover_offerings`, `health`, and registry publish operations
96
+ - **Every rescue block** calls `handle_exception(e, level:, handled:, operation:)` with dot-separated operation names (e.g. `vertex.provider.health`, `vertex.registry.publish_event`)
97
+ - **Level conventions**: `:warn` for recoverable failures, `:error` for unexpected errors, `:debug` for expected/best-effort failures (transport unavailable, etc.)
98
+
63
99
  ## API Contract
64
100
 
65
101
  The implementation is intentionally limited to Vertex AI REST surfaces documented by Google Cloud:
@@ -71,7 +107,20 @@ The implementation is intentionally limited to Vertex AI REST surfaces documente
71
107
 
72
108
  Provider-specific request bodies are not guessed. Partner raw-predict chat requests use the message shape documented for those partner model endpoints; embeddings are only implemented for documented Vertex text embedding models.
73
109
 
74
- Google Cloud references:
110
+ ## Development
111
+
112
+ ```bash
113
+ bundle install
114
+ bundle exec rspec # 0 failures
115
+ bundle exec rubocop -A # auto-fix
116
+ bundle exec rubocop # lint check
117
+ ```
118
+
119
+ ## License
120
+
121
+ Apache-2.0
122
+
123
+ ## References
75
124
 
76
125
  - [Vertex AI GenAI REST API](https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest)
77
126
  - [Generate content with the Gemini API in Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference)
@@ -113,11 +113,13 @@ module Legion
113
113
  end
114
114
 
115
115
  def discover_offerings(live: false, **filters)
116
+ log.info { "discovering offerings live=#{live} project=#{project} location=#{location}" }
116
117
  return static_offerings(**filters) unless live
117
118
 
118
119
  response = connection.get(models_url)
119
120
  models = response.body['publisherModels'] || response.body['models'] || []
120
121
  models.map { |model| offering_from_live_model(model) }.tap do |offerings|
122
+ log.info { "discovered #{offerings.size} live offering(s) from Vertex" }
121
123
  self.class.registry_publisher.publish_offerings_async(offerings, readiness: readiness(live: false))
122
124
  end
123
125
  end
@@ -140,6 +142,7 @@ module Legion
140
142
  end
141
143
 
142
144
  def health(live: false)
145
+ log.info { "checking health live=#{live} project=#{project} location=#{location}" }
143
146
  baseline = {
144
147
  provider: :vertex,
145
148
  project: project,
@@ -154,6 +157,7 @@ module Legion
154
157
  connection.get(models_url)
155
158
  baseline.merge(checked: true)
156
159
  rescue StandardError => e
160
+ handle_exception(e, level: :warn, handled: true, operation: 'vertex.provider.health')
157
161
  baseline.merge(checked: true, ready: false, error: e.class.name, message: e.message)
158
162
  end
159
163
 
@@ -166,6 +170,7 @@ module Legion
166
170
 
167
171
  def chat(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {})
168
172
  model_id = model_id(model)
173
+ log.info { "chat model=#{model_id} messages=#{messages.size}" }
169
174
  @model = model_id
170
175
  payload = Utils.deep_merge(chat_payload(messages, model: model_id, temperature:, max_tokens:, tools:,
171
176
  tool_prefs:, stream: false), params)
@@ -175,6 +180,7 @@ module Legion
175
180
 
176
181
  def stream(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {})
177
182
  model_id = model_id(model)
183
+ log.info { "stream model=#{model_id} messages=#{messages.size}" }
178
184
  @model = model_id
179
185
  payload = Utils.deep_merge(chat_payload(messages, model: model_id, temperature:, max_tokens:, tools:,
180
186
  tool_prefs:, stream: true), params)
@@ -186,6 +192,7 @@ module Legion
186
192
 
187
193
  def count_tokens(messages, model:, params: {})
188
194
  model_id = model_id(model)
195
+ log.info { "count_tokens model=#{model_id}" }
189
196
  unless generate_content_model?(model_id)
190
197
  return {
191
198
  supported: false,
@@ -202,6 +209,7 @@ module Legion
202
209
 
203
210
  def embed(text, model:, dimensions: nil, task_type: nil, title: nil, params: {})
204
211
  model_id = model_id(model)
212
+ log.info { "embed model=#{model_id} inputs=#{Array(text).size}" }
205
213
  unless Capabilities.embeddings?(model_id)
206
214
  raise NotImplementedError, "Vertex embedding payload for #{model_id} is not standardized"
207
215
  end
@@ -213,8 +221,8 @@ module Legion
213
221
  parse_embedding_response(response, model: model_id)
214
222
  end
215
223
 
216
- def complete(messages, tools:, temperature:, model:, params: {}, schema: nil, thinking: nil, tool_prefs: nil,
217
- &)
224
+ def complete(messages, tools:, temperature:, model:, params: {}, headers: {}, schema: nil, thinking: nil, # rubocop:disable Lint/UnusedMethodArgument
225
+ tool_prefs: nil, &)
218
226
  payload = params.dup
219
227
  payload[:generationConfig] = Utils.deep_merge(payload[:generationConfig] || {},
220
228
  generation_config(temperature, schema, thinking))
@@ -6,6 +6,8 @@ module Legion
6
6
  module Vertex
7
7
  # Builds sanitized lex-llm registry envelopes for Vertex provider state.
8
8
  class RegistryEventBuilder
9
+ include Legion::Logging::Helper
10
+
9
11
  def readiness(readiness)
10
12
  registry_event_class.public_send(
11
13
  readiness[:ready] ? :available : :unavailable,
@@ -6,6 +6,8 @@ module Legion
6
6
  module Vertex
7
7
  # Best-effort publisher for Vertex provider availability events.
8
8
  class RegistryPublisher
9
+ include Legion::Logging::Helper
10
+
9
11
  APP_ID = 'lex-llm-vertex'
10
12
 
11
13
  def initialize(builder: RegistryEventBuilder.new)
@@ -13,10 +15,12 @@ module Legion
13
15
  end
14
16
 
15
17
  def publish_readiness_async(readiness)
18
+ log.info { 'publishing readiness event to llm.registry' }
16
19
  schedule { publish_event(@builder.readiness(readiness)) }
17
20
  end
18
21
 
19
22
  def publish_offerings_async(offerings, readiness:)
23
+ log.info { "publishing #{Array(offerings).size} offering event(s) to llm.registry" }
20
24
  schedule do
21
25
  Array(offerings).each do |offering|
22
26
  publish_event(@builder.offering_available(offering, readiness:))
@@ -33,10 +37,10 @@ module Legion
33
37
  Thread.current.abort_on_exception = false
34
38
  yield
35
39
  rescue StandardError => e
36
- log_publish_failure(e, level: :debug)
40
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.schedule_thread')
37
41
  end
38
42
  rescue StandardError => e
39
- log_publish_failure(e, level: :debug)
43
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.schedule')
40
44
  false
41
45
  end
42
46
 
@@ -45,7 +49,7 @@ module Legion
45
49
 
46
50
  message_class.new(event:, app_id: APP_ID).publish(spool: false)
47
51
  rescue StandardError => e
48
- log_publish_failure(e)
52
+ handle_exception(e, level: :warn, handled: true, operation: 'vertex.registry.publish_event')
49
53
  false
50
54
  end
51
55
 
@@ -56,7 +60,8 @@ module Legion
56
60
  return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
57
61
 
58
62
  ::Legion::Transport::Connection.session_open?
59
- rescue StandardError
63
+ rescue StandardError => e
64
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.publishing_available?')
60
65
  false
61
66
  end
62
67
 
@@ -70,7 +75,8 @@ module Legion
70
75
 
71
76
  require 'legion/extensions/llm/vertex/transport/messages/registry_event'
72
77
  message_class_defined?
73
- rescue LoadError
78
+ rescue LoadError => e
79
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.transport_load')
74
80
  false
75
81
  end
76
82
 
@@ -81,18 +87,6 @@ module Legion
81
87
  def message_class
82
88
  ::Legion::Extensions::Llm::Vertex::Transport::Messages::RegistryEvent
83
89
  end
84
-
85
- def log_publish_failure(error, level: :warn)
86
- message = "[lex-llm-vertex] llm.registry publish failed: #{error.class}: #{error.message}"
87
- logger = ::Legion::Extensions::Llm.logger if defined?(::Legion::Extensions::Llm)
88
- if logger.respond_to?(level)
89
- logger.public_send(level, message)
90
- elsif logger.respond_to?(:debug)
91
- logger.debug(message)
92
- end
93
- rescue StandardError
94
- nil
95
- end
96
90
  end
97
91
  end
98
92
  end
@@ -8,6 +8,8 @@ module Legion
8
8
  module Exchanges
9
9
  # Topic exchange for Vertex provider availability events.
10
10
  class LlmRegistry < ::Legion::Transport::Exchange
11
+ include Legion::Logging::Helper
12
+
11
13
  def exchange_name
12
14
  'llm.registry'
13
15
  end
@@ -10,6 +10,8 @@ module Legion
10
10
  module Messages
11
11
  # Publishes lex-llm RegistryEvent envelopes to the llm.registry exchange.
12
12
  class RegistryEvent < ::Legion::Transport::Message
13
+ include Legion::Logging::Helper
14
+
13
15
  def initialize(event:, **options)
14
16
  super(**event.to_h.merge(options))
15
17
  end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Vertex
7
- VERSION = '0.1.3'
7
+ VERSION = '0.1.5'
8
8
  end
9
9
  end
10
10
  end
@@ -11,6 +11,7 @@ module Legion
11
11
  module Llm
12
12
  # Google Cloud Vertex AI provider extension namespace.
13
13
  module Vertex
14
+ extend Legion::Logging::Helper
14
15
  extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
15
16
 
16
17
  PROVIDER_FAMILY = :vertex
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-vertex
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO