lex-llm-vertex 0.1.5 → 0.2.0

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: 2a7dc0a783d3cb0961f7881a4ef0412d1842730852518f1f7ac859ad2cbb944d
4
- data.tar.gz: fabd6084238f3473c1b3a75e53e1f19de820f6ce12231316c38b39a33a2a506f
3
+ metadata.gz: 35a2ffd46ca20c21c1c0d280688794b6702285b996d0f1386c37de565a409b57
4
+ data.tar.gz: 5ae360df40fe27bd2c43d37cd15ee36449303422df3a65550e596fc51f22ae3e
5
5
  SHA512:
6
- metadata.gz: 403c6201955ec9d77611f0ade5a338b58d4164f9567bab09b927c73427a06a2214e6828dff3f3fe82ac435ed33932195454a426f1c78bbac2f74c0743db26c27
7
- data.tar.gz: 91592103291579ba34d8bd61dd1e066111b7a68449585bd96d8d5ca7f38b141dfc0c9d37db532c73721eb3de88d9bdca302510366088464678156c1e978132e8
6
+ metadata.gz: ac9e04f6a5ffdf57bd83392cbb039220871eac5d006256bf90c0a095bae241722a2aa8a05216ab663751bc0d5f144eec95ffcf6f6b5921f2135309938bd3de77
7
+ data.tar.gz: 928812748673dd57855e0ad4e718d2de1fa0f331d2d60df190d96b072de49cd4b317f4eb905c59a9eccaaf38a85c5ea2b87879bcaab00d5a7c4446db6295258d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 - 2026-05-01
4
+
5
+ - Add auto-discovery via CredentialSources and AutoRegistration from lex-llm 0.3.0
6
+ - Self-register discovered instances into Call::Registry at require-time
7
+ - Require lex-llm >= 0.3.0
8
+
9
+
3
10
  ## [0.1.5] - 2026-04-30
4
11
 
5
12
  - Add `Legion::Logging::Helper` to all modules and classes for structured logging
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'legion-json', '>= 1.2.1'
27
27
  spec.add_dependency 'legion-logging', '>= 1.3.2'
28
28
  spec.add_dependency 'legion-settings', '>= 1.3.14'
29
- spec.add_dependency 'lex-llm', '>= 0.1.5'
29
+ spec.add_dependency 'lex-llm', '>= 0.3.0'
30
30
  end
@@ -56,7 +56,7 @@ module Legion
56
56
  def capabilities = Capabilities
57
57
 
58
58
  def registry_publisher
59
- @registry_publisher ||= RegistryPublisher.new
59
+ @registry_publisher ||= Legion::Extensions::Llm::RegistryPublisher.new(provider_family: :vertex)
60
60
  end
61
61
 
62
62
  def resolve_model_id(model_id, config: nil)
@@ -112,16 +112,25 @@ module Legion
112
112
  "#{publisher_model_path(model)}:#{suffix}"
113
113
  end
114
114
 
115
+ def list_models
116
+ log.info { 'listing available Vertex models from static catalog' }
117
+ STATIC_MODELS.map { |entry| model_info_from_static(entry) }.tap do |models|
118
+ log.info { "discovered #{models.size} Vertex model(s); publishing to registry" }
119
+ self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
120
+ end
121
+ end
122
+
115
123
  def discover_offerings(live: false, **filters)
116
124
  log.info { "discovering offerings live=#{live} project=#{project} location=#{location}" }
117
125
  return static_offerings(**filters) unless live
118
126
 
119
127
  response = connection.get(models_url)
120
128
  models = response.body['publisherModels'] || response.body['models'] || []
121
- models.map { |model| offering_from_live_model(model) }.tap do |offerings|
122
- log.info { "discovered #{offerings.size} live offering(s) from Vertex" }
123
- self.class.registry_publisher.publish_offerings_async(offerings, readiness: readiness(live: false))
124
- end
129
+ offerings = models.map { |model| offering_from_live_model(model) }
130
+ log.info { "discovered #{offerings.size} live offering(s) from Vertex" }
131
+ model_infos = offerings.map { |o| model_info_from_offering(o) }
132
+ self.class.registry_publisher.publish_models_async(model_infos, readiness: readiness(live: false))
133
+ offerings
125
134
  end
126
135
 
127
136
  def offering_for(model:, model_family: nil, instance_id: :default, **metadata)
@@ -235,6 +244,34 @@ module Legion
235
244
 
236
245
  private
237
246
 
247
+ def model_info_from_static(entry)
248
+ caps = default_capabilities(entry[:model], api: entry.fetch(:api, :generate_content))
249
+ Legion::Extensions::Llm::Model::Info.new(
250
+ id: entry[:model],
251
+ name: entry[:alias] || entry[:model],
252
+ provider: :vertex,
253
+ family: entry[:model_family].to_s,
254
+ capabilities: caps.map(&:to_s),
255
+ metadata: {
256
+ publisher: entry[:publisher],
257
+ project: project,
258
+ location: location,
259
+ api: entry.fetch(:api, :generate_content)
260
+ }.compact
261
+ )
262
+ end
263
+
264
+ def model_info_from_offering(offering)
265
+ Legion::Extensions::Llm::Model::Info.new(
266
+ id: offering.model,
267
+ name: offering.metadata[:alias] || offering.model,
268
+ provider: :vertex,
269
+ family: offering.metadata[:model_family].to_s,
270
+ capabilities: offering.capabilities.map(&:to_s),
271
+ metadata: offering.metadata
272
+ )
273
+ end
274
+
238
275
  def static_offerings(**filters)
239
276
  STATIC_MODELS.filter_map do |entry|
240
277
  next if filters[:model_family] && entry.fetch(:model_family) != filters[:model_family].to_sym
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Vertex
7
- VERSION = '0.1.5'
7
+ VERSION = '0.2.0'
8
8
  end
9
9
  end
10
10
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require 'legion/extensions/llm'
4
4
  require 'legion/extensions/llm/vertex/provider'
5
- require 'legion/extensions/llm/vertex/registry_event_builder'
6
- require 'legion/extensions/llm/vertex/registry_publisher'
7
5
  require 'legion/extensions/llm/vertex/version'
8
6
 
9
7
  module Legion
@@ -13,37 +11,73 @@ module Legion
13
11
  module Vertex
14
12
  extend Legion::Logging::Helper
15
13
  extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
14
+ extend Legion::Extensions::Llm::AutoRegistration
16
15
 
17
16
  PROVIDER_FAMILY = :vertex
18
17
 
19
18
  def self.default_settings
20
- ::Legion::Extensions::Llm.provider_settings(
21
- family: PROVIDER_FAMILY,
22
- discovery: { enabled: true, live: false, locations: %w[us-central1 us-east5 europe-west4] },
23
- instance: {
24
- endpoint: 'https://us-central1-aiplatform.googleapis.com/v1',
25
- project: 'env://GOOGLE_CLOUD_PROJECT',
26
- location: 'us-central1',
27
- tier: :frontier,
28
- transport: :http,
29
- credentials: {
30
- provider: 'google-application-default-credentials',
31
- access_token: 'env://VERTEX_ACCESS_TOKEN',
32
- credentials_file: 'env://GOOGLE_APPLICATION_CREDENTIALS'
33
- },
34
- usage: { inference: true, embedding: true, token_counting: true },
35
- limits: { concurrency: 4 }
36
- }
37
- )
19
+ {
20
+ enabled: false,
21
+ default_model: nil,
22
+ project: nil,
23
+ location: 'us-central1',
24
+ model_whitelist: [],
25
+ model_blacklist: [],
26
+ model_cache_ttl: 3600,
27
+ tls: { enabled: false, verify: :peer },
28
+ instances: {}
29
+ }
38
30
  end
39
31
 
40
32
  def self.provider_class
41
33
  Provider
42
34
  end
35
+
36
+ def self.discover_instances
37
+ instances = {}
38
+ discover_default_instance(instances)
39
+ discover_named_instances(instances)
40
+ instances
41
+ end
42
+
43
+ def self.discover_default_instance(instances)
44
+ cfg = CredentialSources.setting(:extensions, :llm, :vertex)
45
+ return unless cfg.is_a?(Hash) && vertex_credentials_present?(cfg)
46
+
47
+ instances[:settings] = cfg.except(:instances, 'instances').merge(tier: :cloud)
48
+ end
49
+
50
+ def self.discover_named_instances(instances)
51
+ cfg = CredentialSources.setting(:extensions, :llm, :vertex)
52
+ return unless cfg.is_a?(Hash)
53
+
54
+ named = cfg[:instances] || cfg['instances']
55
+ return unless named.is_a?(Hash)
56
+
57
+ named.each do |name, config|
58
+ next unless config.is_a?(Hash) && vertex_credentials_present?(config)
59
+
60
+ instances[name.to_sym] = config.merge(tier: :cloud)
61
+ end
62
+ end
63
+
64
+ def self.vertex_credentials_present?(cfg)
65
+ project = cfg[:project] || cfg['project']
66
+ return false if project.nil? || project.to_s.strip.empty?
67
+
68
+ token = cfg[:access_token] || cfg['access_token']
69
+ creds = cfg[:credentials] || cfg['credentials']
70
+ !(token.nil? && creds.nil?)
71
+ end
72
+
73
+ private_class_method :discover_default_instance, :discover_named_instances, :vertex_credentials_present?
43
74
  end
44
75
  end
45
76
  end
46
77
  end
47
78
 
48
- Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::Vertex::PROVIDER_FAMILY,
49
- Legion::Extensions::Llm::Vertex::Provider)
79
+ Legion::Extensions::Llm::Configuration.register_provider_options(
80
+ Legion::Extensions::Llm::Vertex::Provider.configuration_options
81
+ )
82
+
83
+ Legion::Extensions::Llm::Vertex.register_discovered_instances
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.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 0.1.5
60
+ version: 0.3.0
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: 0.1.5
67
+ version: 0.3.0
68
68
  description: Google Cloud Vertex AI provider integration for the LegionIO LLM routing
69
69
  framework.
70
70
  email:
@@ -85,10 +85,6 @@ files:
85
85
  - lex-llm-vertex.gemspec
86
86
  - lib/legion/extensions/llm/vertex.rb
87
87
  - lib/legion/extensions/llm/vertex/provider.rb
88
- - lib/legion/extensions/llm/vertex/registry_event_builder.rb
89
- - lib/legion/extensions/llm/vertex/registry_publisher.rb
90
- - lib/legion/extensions/llm/vertex/transport/exchanges/llm_registry.rb
91
- - lib/legion/extensions/llm/vertex/transport/messages/registry_event.rb
92
88
  - lib/legion/extensions/llm/vertex/version.rb
93
89
  homepage: https://github.com/LegionIO/lex-llm-vertex
94
90
  licenses:
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vertex
7
- # Builds sanitized lex-llm registry envelopes for Vertex provider state.
8
- class RegistryEventBuilder
9
- include Legion::Logging::Helper
10
-
11
- def readiness(readiness)
12
- registry_event_class.public_send(
13
- readiness[:ready] ? :available : :unavailable,
14
- provider_offering(readiness),
15
- runtime: runtime_metadata,
16
- health: readiness_health(readiness),
17
- metadata: readiness_metadata(readiness)
18
- )
19
- end
20
-
21
- def offering_available(offering, readiness:)
22
- registry_event_class.available(
23
- offering,
24
- runtime: runtime_metadata,
25
- health: offering_health(readiness),
26
- metadata: offering_metadata
27
- )
28
- end
29
-
30
- private
31
-
32
- def provider_offering(readiness)
33
- {
34
- provider_family: :vertex,
35
- provider_instance: provider_instance,
36
- transport: :http,
37
- model: 'provider-readiness',
38
- usage_type: :inference,
39
- capabilities: [],
40
- health: readiness_health(readiness),
41
- metadata: { lex: :llm_vertex, provider_readiness: true }
42
- }
43
- end
44
-
45
- def readiness_health(readiness)
46
- health = {
47
- ready: readiness[:ready] == true,
48
- status: readiness[:ready] ? :available : :unavailable,
49
- checked: readiness[:checked] != false
50
- }
51
- add_readiness_error(health, readiness)
52
- end
53
-
54
- def add_readiness_error(health, source)
55
- error_class = source[:error] || source['error']
56
- error_message = source[:message] || source['message']
57
- health[:error_class] = error_class if error_class
58
- health[:error] = error_message if error_message
59
- health
60
- end
61
-
62
- def offering_health(readiness)
63
- ready = readiness.fetch(:ready, true) == true
64
- { ready:, status: ready ? :available : :degraded, checked: readiness[:checked] != false }
65
- end
66
-
67
- def readiness_metadata(readiness)
68
- {
69
- extension: :lex_llm_vertex,
70
- provider: :vertex,
71
- configured: readiness[:configured] == true,
72
- live: readiness[:live] == true
73
- }
74
- end
75
-
76
- def offering_metadata
77
- { extension: :lex_llm_vertex, provider: :vertex }
78
- end
79
-
80
- def runtime_metadata
81
- { node: provider_instance }
82
- end
83
-
84
- def provider_instance
85
- :vertex
86
- end
87
-
88
- def registry_event_class
89
- ::Legion::Extensions::Llm::Routing::RegistryEvent
90
- end
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vertex
7
- # Best-effort publisher for Vertex provider availability events.
8
- class RegistryPublisher
9
- include Legion::Logging::Helper
10
-
11
- APP_ID = 'lex-llm-vertex'
12
-
13
- def initialize(builder: RegistryEventBuilder.new)
14
- @builder = builder
15
- end
16
-
17
- def publish_readiness_async(readiness)
18
- log.info { 'publishing readiness event to llm.registry' }
19
- schedule { publish_event(@builder.readiness(readiness)) }
20
- end
21
-
22
- def publish_offerings_async(offerings, readiness:)
23
- log.info { "publishing #{Array(offerings).size} offering event(s) to llm.registry" }
24
- schedule do
25
- Array(offerings).each do |offering|
26
- publish_event(@builder.offering_available(offering, readiness:))
27
- end
28
- end
29
- end
30
-
31
- private
32
-
33
- def schedule(&)
34
- return false unless publishing_available?
35
-
36
- Thread.new do
37
- Thread.current.abort_on_exception = false
38
- yield
39
- rescue StandardError => e
40
- handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.schedule_thread')
41
- end
42
- rescue StandardError => e
43
- handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.schedule')
44
- false
45
- end
46
-
47
- def publish_event(event)
48
- return false unless publishing_available?
49
-
50
- message_class.new(event:, app_id: APP_ID).publish(spool: false)
51
- rescue StandardError => e
52
- handle_exception(e, level: :warn, handled: true, operation: 'vertex.registry.publish_event')
53
- false
54
- end
55
-
56
- def publishing_available?
57
- return false unless registry_event_available?
58
- return false unless transport_message_available?
59
- return true unless defined?(::Legion::Transport::Connection)
60
- return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
61
-
62
- ::Legion::Transport::Connection.session_open?
63
- rescue StandardError => e
64
- handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.publishing_available?')
65
- false
66
- end
67
-
68
- def registry_event_available?
69
- defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
70
- end
71
-
72
- def transport_message_available?
73
- return true if message_class_defined?
74
- return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
75
-
76
- require 'legion/extensions/llm/vertex/transport/messages/registry_event'
77
- message_class_defined?
78
- rescue LoadError => e
79
- handle_exception(e, level: :debug, handled: true, operation: 'vertex.registry.transport_load')
80
- false
81
- end
82
-
83
- def message_class_defined?
84
- defined?(::Legion::Extensions::Llm::Vertex::Transport::Messages::RegistryEvent)
85
- end
86
-
87
- def message_class
88
- ::Legion::Extensions::Llm::Vertex::Transport::Messages::RegistryEvent
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vertex
7
- module Transport
8
- module Exchanges
9
- # Topic exchange for Vertex provider availability events.
10
- class LlmRegistry < ::Legion::Transport::Exchange
11
- include Legion::Logging::Helper
12
-
13
- def exchange_name
14
- 'llm.registry'
15
- end
16
-
17
- def default_type
18
- 'topic'
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'legion/extensions/llm/vertex/transport/exchanges/llm_registry'
4
-
5
- module Legion
6
- module Extensions
7
- module Llm
8
- module Vertex
9
- module Transport
10
- module Messages
11
- # Publishes lex-llm RegistryEvent envelopes to the llm.registry exchange.
12
- class RegistryEvent < ::Legion::Transport::Message
13
- include Legion::Logging::Helper
14
-
15
- def initialize(event:, **options)
16
- super(**event.to_h.merge(options))
17
- end
18
-
19
- def exchange
20
- Transport::Exchanges::LlmRegistry
21
- end
22
-
23
- def routing_key
24
- @options[:routing_key] || "llm.registry.#{@options.fetch(:event_type)}"
25
- end
26
-
27
- def type
28
- 'llm.registry.event'
29
- end
30
-
31
- def app_id
32
- @options[:app_id] || RegistryPublisher::APP_ID
33
- end
34
-
35
- def persistent # rubocop:disable Naming/PredicateMethod
36
- false
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end