lex-llm-vertex 0.2.13 → 0.2.15
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1b2e665277fea3299658b992eac2b441a363e3c81dbf87d69e86c7328c6c99c
|
|
4
|
+
data.tar.gz: d81b38ce8e5e797a074d30a670a5e6fa9bcb92762c5dcd066ed86ad8196d5340
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 97009f1dd75f9f053ec06dfba726627f7c6202f92bb56a0185e9250d7579ff73de1c7c410b3ae6a82b4ad308032384362a0dd5c12ed2eeddc30e3c690d381753
|
|
7
|
+
data.tar.gz: 2f36d16c24610e1355ee50cdeb26e99818e8a1b7cc6441b9ef88f7e0d17130bd8691bba88e8db3c71bc3becd33a1abe5962ee65802a957abdcc6dec7bc4f839d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.15] - 2026-06-20
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Align Vertex offerings to the current `lex-llm` contract: shared `discover_offerings` now rebuilds
|
|
7
|
+
resource-name offerings from discovered models, preserves provider health on offerings, and keeps the
|
|
8
|
+
shared capability-override path intact.
|
|
9
|
+
- Fix the provider tail introduced during the contract refactor so the provider file closes cleanly again.
|
|
10
|
+
|
|
11
|
+
## [0.2.14] - 2026-06-19
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Adopt `Legion::Extensions::Llm::Inventory::ScopedRefresher` mixin (lex-llm 0.6.0). Discovery
|
|
15
|
+
refresh actors now write directly to the live `Inventory` catalog via `Inventory.write_lane`.
|
|
16
|
+
- Pin `lex-llm >= 0.6.0` and `legion-llm >= 0.14.0` in gemspec.
|
|
17
|
+
- Standard `weight: 100` default added to provider instance settings schema.
|
|
18
|
+
|
|
3
19
|
## 0.2.13 - 2026-06-16
|
|
4
20
|
|
|
5
21
|
- Dependency updates and code quality improvements.
|
data/lex-llm-vertex.gemspec
CHANGED
|
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
28
28
|
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
29
29
|
spec.add_dependency 'legion-transport', '>= 1.4.14'
|
|
30
|
-
spec.add_dependency 'lex-llm', '>= 0.
|
|
30
|
+
spec.add_dependency 'lex-llm', '>= 0.6.0'
|
|
31
31
|
end
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'digest'
|
|
4
|
+
|
|
3
5
|
begin
|
|
4
6
|
require 'legion/extensions/actors/every'
|
|
5
7
|
rescue LoadError => e
|
|
6
8
|
warn(e.message) if $VERBOSE
|
|
7
9
|
end
|
|
8
10
|
|
|
11
|
+
begin
|
|
12
|
+
require 'legion/extensions/llm/inventory/scoped_refresher'
|
|
13
|
+
rescue LoadError => e
|
|
14
|
+
warn(e.message) if $VERBOSE
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
return unless defined?(Legion::Extensions::Actors::Every)
|
|
10
18
|
|
|
11
19
|
module Legion
|
|
@@ -16,7 +24,11 @@ module Legion
|
|
|
16
24
|
class DiscoveryRefresh < Legion::Extensions::Actors::Every # rubocop:disable Style/Documentation
|
|
17
25
|
include Legion::Logging::Helper
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
if defined?(Legion::Extensions::Llm::Inventory::ScopedRefresher)
|
|
28
|
+
include Legion::Extensions::Llm::Inventory::ScopedRefresher
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.every_seconds = 3600
|
|
20
32
|
|
|
21
33
|
def runner_class = self.class
|
|
22
34
|
def runner_function = 'manual'
|
|
@@ -26,23 +38,106 @@ module Legion
|
|
|
26
38
|
def generate_task? = false
|
|
27
39
|
|
|
28
40
|
def time
|
|
29
|
-
return
|
|
41
|
+
return self.class.every_seconds unless defined?(Legion::Settings)
|
|
30
42
|
|
|
31
|
-
Legion::Settings.dig(:extensions, :llm, :vertex, :discovery_interval) ||
|
|
43
|
+
Legion::Settings.dig(:extensions, :llm, :vertex, :discovery_interval) || self.class.every_seconds
|
|
32
44
|
end
|
|
33
45
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
def scope_key
|
|
47
|
+
{ provider: :vertex }
|
|
48
|
+
end
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
def compute_lanes_for_scope(**)
|
|
51
|
+
return [] unless defined?(Legion::LLM::Call::Registry)
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
settings = Legion::Settings.dig(:extensions, :llm, :vertex) || {}
|
|
54
|
+
fleet_enabled = settings.dig(:fleet, :dispatch, :enabled)
|
|
55
|
+
instances = Legion::LLM::Call::Registry.all_instances.select do |e|
|
|
56
|
+
(e[:provider] || '').to_sym == :vertex
|
|
42
57
|
end
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
|
|
59
|
+
instances.flat_map do |entry|
|
|
60
|
+
lanes_for_instance(entry, fleet_enabled: fleet_enabled)
|
|
45
61
|
end
|
|
62
|
+
rescue StandardError => e
|
|
63
|
+
handle_exception(e, level: :warn, handled: true, operation: 'vertex.actor.compute_lanes_for_scope')
|
|
64
|
+
[]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def lanes_for_instance(entry, fleet_enabled: false)
|
|
70
|
+
adapter = entry[:adapter]
|
|
71
|
+
instance_id = entry[:instance] || entry[:instance_id] || entry[:id]
|
|
72
|
+
lanes = []
|
|
73
|
+
Array(adapter.discover_offerings(live: false)).each do |raw_offering|
|
|
74
|
+
offering = offering_to_hash(raw_offering)
|
|
75
|
+
next unless offering
|
|
76
|
+
|
|
77
|
+
lane = build_lane(offering, instance_id)
|
|
78
|
+
lanes << lane
|
|
79
|
+
lanes << fleet_lane(lane, instance_id, offering) if fleet_enabled && lane[:type] == :inference
|
|
80
|
+
end
|
|
81
|
+
lanes
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def offering_to_hash(offering)
|
|
85
|
+
return nil if offering.nil?
|
|
86
|
+
return offering if offering.is_a?(Hash)
|
|
87
|
+
|
|
88
|
+
hash = offering.to_h
|
|
89
|
+
hash[:type] ||= hash[:usage_type]
|
|
90
|
+
hash[:enabled] = offering.respond_to?(:enabled?) ? offering.enabled? : true
|
|
91
|
+
hash
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def build_lane(offering, instance_id)
|
|
95
|
+
type = offering_type(offering)
|
|
96
|
+
tier = offering[:tier]&.to_sym || :cloud
|
|
97
|
+
caps = normalize_capabilities(offering[:capabilities])
|
|
98
|
+
flds = { tier: tier, provider_family: :vertex, instance_id: instance_id,
|
|
99
|
+
type: type, model: offering[:model] }
|
|
100
|
+
{
|
|
101
|
+
id: Legion::Extensions::Llm::Inventory::ScopedRefresher.compose_id(flds),
|
|
102
|
+
tier: tier,
|
|
103
|
+
provider_family: :vertex,
|
|
104
|
+
instance_id: instance_id,
|
|
105
|
+
model: offering[:model],
|
|
106
|
+
canonical_model_alias: offering[:canonical_model_alias],
|
|
107
|
+
type: type,
|
|
108
|
+
capabilities: caps,
|
|
109
|
+
limits: offering[:limits] || {},
|
|
110
|
+
enabled: offering.fetch(:enabled, true),
|
|
111
|
+
cost: offering[:cost] || {}
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def fleet_lane(lane, instance_id, offering)
|
|
116
|
+
flds = { tier: :fleet, provider_family: :vertex, instance_id: instance_id,
|
|
117
|
+
type: lane[:type], model: offering[:model] }
|
|
118
|
+
lane.merge(id: Legion::Extensions::Llm::Inventory::ScopedRefresher.compose_id(flds), tier: :fleet)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def offering_type(offering)
|
|
122
|
+
%i[embed embedding].include?(offering[:type]&.to_sym) ? :embedding : :inference
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def normalize_capabilities(caps)
|
|
126
|
+
return [] unless defined?(Legion::Extensions::Llm::Inventory::Capabilities) &&
|
|
127
|
+
Legion::Extensions::Llm::Inventory::Capabilities.respond_to?(:normalize)
|
|
128
|
+
|
|
129
|
+
Legion::Extensions::Llm::Inventory::Capabilities.normalize(caps)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
public
|
|
133
|
+
|
|
134
|
+
def credential_hash(**)
|
|
135
|
+
settings = Legion::Settings.dig(:extensions, :llm, :vertex) || {}
|
|
136
|
+
::Digest::SHA256.hexdigest(settings[:api_key].to_s + settings[:instances].to_s)[0, 16]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def manual
|
|
140
|
+
tick if respond_to?(:tick)
|
|
46
141
|
rescue StandardError => e
|
|
47
142
|
handle_exception(e, level: :warn, handled: true, operation: 'vertex.actor.discovery_refresh')
|
|
48
143
|
end
|
|
@@ -116,7 +116,7 @@ module Legion
|
|
|
116
116
|
"#{publisher_model_path(model)}:#{suffix}"
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
def list_models(**)
|
|
119
|
+
def list_models(**_filters)
|
|
120
120
|
log.info { 'listing available Vertex models from static catalog' }
|
|
121
121
|
STATIC_MODELS.map { |entry| model_info_from_static(entry) }.tap do |models|
|
|
122
122
|
log.info { "discovered #{models.size} Vertex model(s); publishing to registry" }
|
|
@@ -125,22 +125,13 @@ module Legion
|
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def discover_offerings(live: false, **filters)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
models = response.body['publisherModels'] || response.body['models'] || []
|
|
133
|
-
offerings = models.filter_map do |model|
|
|
134
|
-
offering = offering_from_live_model(model)
|
|
135
|
-
model_id = offering.respond_to?(:model) ? offering.model : (offering[:model] || offering[:id])
|
|
136
|
-
next unless model_allowed?(model_id.to_s)
|
|
137
|
-
|
|
138
|
-
offering
|
|
128
|
+
unless live
|
|
129
|
+
return static_offerings(**filters).select do |offering|
|
|
130
|
+
model_allowed?(short_model_id(offering.model))
|
|
131
|
+
end
|
|
139
132
|
end
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
self.class.registry_publisher.publish_models_async(model_infos, readiness: readiness(live: false))
|
|
143
|
-
offerings
|
|
133
|
+
|
|
134
|
+
super
|
|
144
135
|
end
|
|
145
136
|
|
|
146
137
|
def offering_for(model:, model_family: nil, instance_id: :default, **metadata)
|
|
@@ -325,8 +316,48 @@ module Legion
|
|
|
325
316
|
offering_for(model: id, publisher:, metadata: model)
|
|
326
317
|
end
|
|
327
318
|
|
|
319
|
+
def offering_from_model(model_info, health: {})
|
|
320
|
+
metadata = model_info.respond_to?(:metadata) ? model_info.metadata.to_h : {}
|
|
321
|
+
raw_model = model_info.respond_to?(:id) ? model_info.id : model_info
|
|
322
|
+
publisher = metadata[:publisher] || metadata['publisher'] || publisher_for(raw_model)
|
|
323
|
+
api = metadata[:api] || metadata['api'] || api_for(raw_model)
|
|
324
|
+
alias_name = model_info.respond_to?(:name) ? model_info.name : nil
|
|
325
|
+
alias_name = nil if alias_name.to_s.empty? || alias_name.to_s == raw_model.to_s
|
|
326
|
+
|
|
327
|
+
build_offering(
|
|
328
|
+
model: resource_name(raw_model, publisher: publisher),
|
|
329
|
+
alias_name: alias_name,
|
|
330
|
+
model_family: if model_info.respond_to?(:family) && model_info.family
|
|
331
|
+
model_info.family.to_sym
|
|
332
|
+
else
|
|
333
|
+
model_family_for(
|
|
334
|
+
raw_model, publisher
|
|
335
|
+
)
|
|
336
|
+
end,
|
|
337
|
+
instance_id: if model_info.respond_to?(:instance)
|
|
338
|
+
model_info.instance || provider_instance_id
|
|
339
|
+
else
|
|
340
|
+
provider_instance_id
|
|
341
|
+
end,
|
|
342
|
+
publisher: publisher,
|
|
343
|
+
usage_type: if model_info.respond_to?(:embedding?) && model_info.embedding?
|
|
344
|
+
:embedding
|
|
345
|
+
else
|
|
346
|
+
usage_type_for(raw_model)
|
|
347
|
+
end,
|
|
348
|
+
api: api,
|
|
349
|
+
health: health,
|
|
350
|
+
metadata: metadata.merge(
|
|
351
|
+
limits: {
|
|
352
|
+
context_window: model_info.respond_to?(:context_length) ? model_info.context_length : nil,
|
|
353
|
+
max_output_tokens: model_info.respond_to?(:max_output_tokens) ? model_info.max_output_tokens : nil
|
|
354
|
+
}.compact
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
end
|
|
358
|
+
|
|
328
359
|
def build_offering(model:, model_family:, usage_type:, publisher:, api:, instance_id: :default,
|
|
329
|
-
alias_name: nil, metadata: {})
|
|
360
|
+
alias_name: nil, health: {}, metadata: {})
|
|
330
361
|
policy = resolve_capability_policy(model, api:, metadata:, instance_id:)
|
|
331
362
|
|
|
332
363
|
Legion::Extensions::Llm::Routing::ModelOffering.new(
|
|
@@ -339,6 +370,7 @@ module Legion
|
|
|
339
370
|
capabilities: base_capabilities(model, api:) + policy[:capabilities],
|
|
340
371
|
capability_sources: policy[:sources],
|
|
341
372
|
limits: metadata.delete(:limits) || {},
|
|
373
|
+
health: health,
|
|
342
374
|
metadata: metadata.merge(
|
|
343
375
|
model_family: model_family,
|
|
344
376
|
alias: alias_name,
|
|
@@ -697,7 +729,7 @@ module Legion
|
|
|
697
729
|
|
|
698
730
|
cfg.except(:instances, 'instances')
|
|
699
731
|
rescue StandardError => e
|
|
700
|
-
handle_exception(e, level: :
|
|
732
|
+
handle_exception(e, level: :warn, handled: true, operation: 'vertex.provider.capability_policy_config')
|
|
701
733
|
{}
|
|
702
734
|
end
|
|
703
735
|
|
|
@@ -710,7 +742,7 @@ module Legion
|
|
|
710
742
|
|
|
711
743
|
(instances[instance_id] || instances[instance_id.to_s] || {}).to_h
|
|
712
744
|
rescue StandardError => e
|
|
713
|
-
handle_exception(e, level: :
|
|
745
|
+
handle_exception(e, level: :warn, handled: true, operation: 'vertex.provider.instance_config')
|
|
714
746
|
{}
|
|
715
747
|
end
|
|
716
748
|
|
|
@@ -724,7 +756,7 @@ module Legion
|
|
|
724
756
|
id = short_model_id(model)
|
|
725
757
|
(models[id.to_sym] || models[id.to_s] || {}).to_h
|
|
726
758
|
rescue StandardError => e
|
|
727
|
-
handle_exception(e, level: :
|
|
759
|
+
handle_exception(e, level: :warn, handled: true, operation: 'vertex.provider.model_config')
|
|
728
760
|
{}
|
|
729
761
|
end
|
|
730
762
|
|
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.2.
|
|
4
|
+
version: 0.2.15
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -71,14 +71,14 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.
|
|
74
|
+
version: 0.6.0
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.
|
|
81
|
+
version: 0.6.0
|
|
82
82
|
description: Google Cloud Vertex AI provider integration for the LegionIO LLM routing
|
|
83
83
|
framework.
|
|
84
84
|
email:
|