lex-llm-vertex 0.3.3 → 0.3.4
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: '00809f0bdd3bed557d5b9118a9fc70d312c51fd76186b8094a75e8cd466aa0cb'
|
|
4
|
+
data.tar.gz: 3aef7dbcaa603e84ddc908d70a4e5725f05a7e6976aa713cddc3bbdb8ffaf375
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 655b4165c7cc60df1203070fe7d0018b7286deefb622d8fcbdd785cedbbb016a7bb2e8c35dd78acccfd49dfe3d9c16e46c3a4191c25457ad580de48c93dae8a6
|
|
7
|
+
data.tar.gz: f3cc8a26e1bfebd588dc4c13107c6b99922468e6f11b66fed68c005305c85dabccece0e90c5e774aa633df4465f555f8f1456492c941cd9c79a8a5c36b3f56ec
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.4] - 2026-08-19
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Publish the validated four-axis lane-weight pair from every Vertex offering draft and reconcile weight-only changes atomically on the existing discovery cadence.
|
|
7
|
+
- Track initializing publications through readiness, serialize replacement/removal state, and report dormant configured weights without adding a Settings callback or operator workflow.
|
|
8
|
+
- Add callable-to-HTTP conformance coverage proving folded system messages render in Vertex's native `systemInstruction` field.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Validate startup offering weights before creating a callable or claiming its inventory scope, so malformed settings leave no untracked initializing publication and the next valid cadence pass recovers without operator action.
|
|
12
|
+
- Pin the raw-`Data` comparison exception with real-cadence regressions: frozen static catalog order and the per-instance evidence timestamp keep unchanged passes stable, while every authoritative draft member and duplicate-count change remains significant.
|
|
13
|
+
|
|
3
14
|
## [0.3.3] - 2026-08-18
|
|
4
15
|
|
|
5
16
|
### Fixed
|
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.4.2'
|
|
29
29
|
spec.add_dependency 'legion-transport', '>= 1.4.14'
|
|
30
|
-
spec.add_dependency 'lex-llm', '>= 0.7.
|
|
30
|
+
spec.add_dependency 'lex-llm', '>= 0.7.6'
|
|
31
31
|
end
|
|
@@ -17,6 +17,7 @@ require 'legion/extensions/llm/inventory/publisher'
|
|
|
17
17
|
require 'legion/extensions/llm/inventory/scoped_refresher'
|
|
18
18
|
require 'legion/extensions/llm/inventory/identity'
|
|
19
19
|
require 'legion/extensions/llm/inventory/records'
|
|
20
|
+
require 'legion/extensions/llm/inventory/weight_reconciler'
|
|
20
21
|
require 'legion/extensions/llm/inventory/evidence'
|
|
21
22
|
require 'legion/extensions/llm/inventory/probe_coordinator'
|
|
22
23
|
require 'legion/extensions/llm/routing/provider_outcome'
|
|
@@ -205,10 +206,80 @@ module Legion
|
|
|
205
206
|
handle_exception(e, level: :warn, operation: 'vertex.actor.cadence_probe', instance_id: instance_id)
|
|
206
207
|
end
|
|
207
208
|
|
|
209
|
+
def report_probe_result(instance_id:, probe_token:, readiness:, state:)
|
|
210
|
+
if readiness.ready?
|
|
211
|
+
published = @state_mutex.synchronize do
|
|
212
|
+
@instance_states[instance_id].equal?(state) && state[:published]
|
|
213
|
+
end
|
|
214
|
+
settled = if published
|
|
215
|
+
@state_mutex.synchronize do
|
|
216
|
+
next false unless @instance_states[instance_id].equal?(state) && state[:published]
|
|
217
|
+
|
|
218
|
+
publisher.readiness_succeeded(
|
|
219
|
+
instance_id: instance_id,
|
|
220
|
+
probe_token: probe_token,
|
|
221
|
+
physical_id: state[:physical_id]
|
|
222
|
+
)
|
|
223
|
+
true
|
|
224
|
+
end
|
|
225
|
+
else
|
|
226
|
+
activate_tracked_instance(
|
|
227
|
+
instance_id: instance_id,
|
|
228
|
+
state: state,
|
|
229
|
+
probe_token: probe_token
|
|
230
|
+
)
|
|
231
|
+
end
|
|
232
|
+
else
|
|
233
|
+
settled = @state_mutex.synchronize do
|
|
234
|
+
next false unless @instance_states[instance_id].equal?(state)
|
|
235
|
+
|
|
236
|
+
publisher.readiness_failed(
|
|
237
|
+
instance_id: instance_id,
|
|
238
|
+
probe_token: probe_token,
|
|
239
|
+
reason: readiness.reason,
|
|
240
|
+
physical_id: state[:physical_id]
|
|
241
|
+
)
|
|
242
|
+
true
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
if settled
|
|
246
|
+
sync_instance_health(
|
|
247
|
+
name: state[:name], instance_key: state[:instance_key], offerings: state[:offerings]
|
|
248
|
+
)
|
|
249
|
+
end
|
|
250
|
+
settled
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def check_health(instance_cfg:)
|
|
254
|
+
project = instance_cfg[:vertex_project] || instance_cfg[:project]
|
|
255
|
+
location = instance_cfg[:vertex_location] || instance_cfg[:location] || 'us-central1'
|
|
256
|
+
base_url = vertex_api_base(instance_cfg: instance_cfg, location: location)
|
|
257
|
+
path = "/v1/projects/#{project}/locations/#{location}/publishers/google/models"
|
|
258
|
+
conn = build_health_connection(base_url: base_url, instance_cfg: instance_cfg)
|
|
259
|
+
response = conn.get(path)
|
|
260
|
+
Legion::Extensions::Llm::Inventory::ReadinessResult.new(
|
|
261
|
+
ready: response.status == 200,
|
|
262
|
+
reason: "Vertex models-list returned #{response.status}",
|
|
263
|
+
metadata: { status: response.status, base_url: base_url }
|
|
264
|
+
)
|
|
265
|
+
rescue StandardError => e
|
|
266
|
+
handle_exception(e, level: :warn, operation: 'vertex.actor.check_health')
|
|
267
|
+
Legion::Extensions::Llm::Inventory::ReadinessResult.new(
|
|
268
|
+
ready: false, reason: "Vertex models-list error: #{e.message}",
|
|
269
|
+
metadata: { error_class: e.class.name }
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Request-triggered readiness probes use the same tracked state and
|
|
275
|
+
# publication result path as the periodic cadence probe.
|
|
276
|
+
module DiscoveryRefreshReactiveProbeHelpers
|
|
277
|
+
private
|
|
278
|
+
|
|
208
279
|
def handle_reactive_probe(instance_id:, request:)
|
|
209
280
|
return false if @instance_states.nil?
|
|
210
281
|
|
|
211
|
-
state = @instance_states[instance_id]
|
|
282
|
+
state = @state_mutex.synchronize { @instance_states[instance_id] }
|
|
212
283
|
return false unless state
|
|
213
284
|
|
|
214
285
|
coordinator = state[:probe_coordinator]
|
|
@@ -232,33 +303,6 @@ module Legion
|
|
|
232
303
|
false
|
|
233
304
|
end
|
|
234
305
|
|
|
235
|
-
def report_probe_result(instance_id:, probe_token:, readiness:, state:)
|
|
236
|
-
if readiness.ready?
|
|
237
|
-
if publication_state(instance_key: state[:instance_key]) == :initializing
|
|
238
|
-
# Initial-failure recovery: while the claim is still
|
|
239
|
-
# :initializing a passing probe re-activates the snapshot —
|
|
240
|
-
# activate_instance_snapshot is the only legal transition out
|
|
241
|
-
# of :initializing (readiness_succeeded raises there).
|
|
242
|
-
publisher.activate_instance_snapshot(instance_id: instance_id,
|
|
243
|
-
publisher_token: state[:publisher_token],
|
|
244
|
-
offerings: state[:offerings],
|
|
245
|
-
sequence: state[:sequence], probe_token: probe_token,
|
|
246
|
-
physical_id: state[:physical_id])
|
|
247
|
-
else
|
|
248
|
-
publisher.readiness_succeeded(instance_id: instance_id, probe_token: probe_token,
|
|
249
|
-
physical_id: state[:physical_id])
|
|
250
|
-
end
|
|
251
|
-
else
|
|
252
|
-
publisher.readiness_failed(instance_id: instance_id, probe_token: probe_token,
|
|
253
|
-
reason: readiness.reason, physical_id: state[:physical_id])
|
|
254
|
-
end
|
|
255
|
-
sync_instance_health(name: state[:name], instance_key: state[:instance_key], offerings: state[:offerings])
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
def publication_state(instance_key:)
|
|
259
|
-
publisher.snapshot.publication_status(instance_key: instance_key).state
|
|
260
|
-
end
|
|
261
|
-
|
|
262
306
|
def build_probe_enqueue(instance_id:)
|
|
263
307
|
proc do |request:|
|
|
264
308
|
handle_reactive_probe(instance_id: instance_id, request: request)
|
|
@@ -267,26 +311,6 @@ module Legion
|
|
|
267
311
|
false
|
|
268
312
|
end
|
|
269
313
|
end
|
|
270
|
-
|
|
271
|
-
def check_health(instance_cfg:)
|
|
272
|
-
project = instance_cfg[:vertex_project] || instance_cfg[:project]
|
|
273
|
-
location = instance_cfg[:vertex_location] || instance_cfg[:location] || 'us-central1'
|
|
274
|
-
base_url = vertex_api_base(instance_cfg: instance_cfg, location: location)
|
|
275
|
-
path = "/v1/projects/#{project}/locations/#{location}/publishers/google/models"
|
|
276
|
-
conn = build_health_connection(base_url: base_url, instance_cfg: instance_cfg)
|
|
277
|
-
response = conn.get(path)
|
|
278
|
-
Legion::Extensions::Llm::Inventory::ReadinessResult.new(
|
|
279
|
-
ready: response.status == 200,
|
|
280
|
-
reason: "Vertex models-list returned #{response.status}",
|
|
281
|
-
metadata: { status: response.status, base_url: base_url }
|
|
282
|
-
)
|
|
283
|
-
rescue StandardError => e
|
|
284
|
-
handle_exception(e, level: :warn, operation: 'vertex.actor.check_health')
|
|
285
|
-
Legion::Extensions::Llm::Inventory::ReadinessResult.new(
|
|
286
|
-
ready: false, reason: "Vertex models-list error: #{e.message}",
|
|
287
|
-
metadata: { error_class: e.class.name }
|
|
288
|
-
)
|
|
289
|
-
end
|
|
290
314
|
end
|
|
291
315
|
|
|
292
316
|
# Offering snapshot construction helpers for DiscoveryRefresh.
|
|
@@ -305,6 +329,8 @@ module Legion
|
|
|
305
329
|
build_offering_draft(model_entry: entry, tier: tier, instance_cfg: instance_cfg,
|
|
306
330
|
instance_key: instance_key, now: now)
|
|
307
331
|
end
|
|
332
|
+
rescue ArgumentError
|
|
333
|
+
raise
|
|
308
334
|
rescue StandardError => e
|
|
309
335
|
handle_exception(e, level: :warn, operation: 'vertex.actor.discover_offerings')
|
|
310
336
|
[]
|
|
@@ -314,8 +340,17 @@ module Legion
|
|
|
314
340
|
model_id = model_entry[:model]
|
|
315
341
|
is_embedding = model_entry[:usage_type] == :embedding
|
|
316
342
|
is_gen = model_entry.fetch(:api, :generate_content) == :generate_content
|
|
343
|
+
weight_inputs = Legion::Extensions::Llm::Inventory::WeightSchema.weight_inputs(
|
|
344
|
+
settings: Legion::Settings,
|
|
345
|
+
instance_key: instance_key,
|
|
346
|
+
provider_native_key: model_id,
|
|
347
|
+
model: model_id,
|
|
348
|
+
tier: tier
|
|
349
|
+
)
|
|
317
350
|
Legion::Extensions::Llm::Inventory::OfferingDraft.new(
|
|
318
351
|
provider_native_key: model_id, model: model_id, tier: tier,
|
|
352
|
+
weight_inputs: weight_inputs,
|
|
353
|
+
base_weight: Legion::Extensions::Llm::Inventory::WeightSchema.base_weight(weight_inputs),
|
|
319
354
|
operation_evidence: build_operation_evidence(now: now,
|
|
320
355
|
is_embedding: is_embedding,
|
|
321
356
|
is_generate_content: is_gen),
|
|
@@ -333,6 +368,89 @@ module Legion
|
|
|
333
368
|
end
|
|
334
369
|
end
|
|
335
370
|
|
|
371
|
+
# Atomic weight publication adapters and dormant observation used by
|
|
372
|
+
# the existing Vertex discovery cadence. The shared reconciler owns
|
|
373
|
+
# sequence and cache mutation.
|
|
374
|
+
module DiscoveryRefreshWeightPublication
|
|
375
|
+
private
|
|
376
|
+
|
|
377
|
+
def replace_offerings_if_changed(instance_id:, state:)
|
|
378
|
+
discovered = discover_offerings_for_instance(
|
|
379
|
+
instance_cfg: state[:instance_cfg],
|
|
380
|
+
instance_key: state[:instance_key],
|
|
381
|
+
now: state[:evidence_now]
|
|
382
|
+
)
|
|
383
|
+
changed = Legion::Extensions::Llm::Inventory::WeightReconciler.commit_if_changed!(
|
|
384
|
+
settings: Legion::Settings,
|
|
385
|
+
instance_id: instance_id,
|
|
386
|
+
state: state,
|
|
387
|
+
discovered_offerings: discovered,
|
|
388
|
+
mutex: @state_mutex,
|
|
389
|
+
equivalent: ->(previous, current) { previous == current },
|
|
390
|
+
replace: method(:replace_weight_snapshot)
|
|
391
|
+
)
|
|
392
|
+
published = @state_mutex.synchronize do
|
|
393
|
+
@instance_states[instance_id].equal?(state) && state[:published]
|
|
394
|
+
end
|
|
395
|
+
if changed && published
|
|
396
|
+
sync_instance_health(
|
|
397
|
+
name: state[:name], instance_key: state[:instance_key], offerings: state[:offerings]
|
|
398
|
+
)
|
|
399
|
+
end
|
|
400
|
+
changed
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def replace_weight_snapshot(instance_id:, state:, offerings:, sequence:)
|
|
404
|
+
publisher.replace_instance_snapshot(
|
|
405
|
+
instance_id: instance_id,
|
|
406
|
+
publisher_token: state.fetch(:publisher_token),
|
|
407
|
+
offerings: offerings,
|
|
408
|
+
sequence: sequence,
|
|
409
|
+
physical_id: state.fetch(:physical_id)
|
|
410
|
+
)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def activate_tracked_instance(instance_id:, state:, probe_token:)
|
|
414
|
+
Legion::Extensions::Llm::Inventory::WeightReconciler.activate_tracked!(
|
|
415
|
+
settings: Legion::Settings,
|
|
416
|
+
instance_id: instance_id,
|
|
417
|
+
state_key: instance_id,
|
|
418
|
+
state: state,
|
|
419
|
+
states: @instance_states,
|
|
420
|
+
mutex: @state_mutex,
|
|
421
|
+
probe_token: probe_token,
|
|
422
|
+
activate: method(:activate_weight_snapshot),
|
|
423
|
+
activation_sequence: ->(tracked) { tracked.fetch(:sequence) }
|
|
424
|
+
)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def activate_weight_snapshot(instance_id:, state:, offerings:, sequence:, probe_token:)
|
|
428
|
+
publisher.activate_instance_snapshot(
|
|
429
|
+
instance_id: instance_id,
|
|
430
|
+
publisher_token: state.fetch(:publisher_token),
|
|
431
|
+
offerings: offerings,
|
|
432
|
+
sequence: sequence,
|
|
433
|
+
probe_token: probe_token,
|
|
434
|
+
physical_id: state.fetch(:physical_id)
|
|
435
|
+
)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def observe_dormant_weights
|
|
439
|
+
Legion::Extensions::Llm::Inventory::WeightReconciler.observe_dormant!(
|
|
440
|
+
settings: Legion::Settings,
|
|
441
|
+
provider_family: :vertex,
|
|
442
|
+
states: @instance_states,
|
|
443
|
+
mutex: @state_mutex,
|
|
444
|
+
tracker: @dormant_weight_tracker,
|
|
445
|
+
dormant_logger: lambda do |key|
|
|
446
|
+
log.info do
|
|
447
|
+
"[llm][vertex] action=dormant_weight weight_key=#{key.inspect} no_lane_published=true"
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
)
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
|
|
336
454
|
# Publisher, tick-refresh, and instance removal orchestration for
|
|
337
455
|
# DiscoveryRefresh.
|
|
338
456
|
module DiscoveryRefreshLifecycleHelpers
|
|
@@ -349,8 +467,11 @@ module Legion
|
|
|
349
467
|
|
|
350
468
|
def initial_discovery
|
|
351
469
|
@instance_states = Concurrent::Map.new
|
|
470
|
+
@state_mutex = Mutex.new
|
|
471
|
+
@dormant_weight_tracker = Legion::Extensions::Llm::Inventory::DormantWeightTracker.new
|
|
352
472
|
@initialized = true
|
|
353
473
|
reconcile_instances
|
|
474
|
+
observe_dormant_weights
|
|
354
475
|
end
|
|
355
476
|
|
|
356
477
|
# Re-scans configured instances each tick so late-configured
|
|
@@ -378,7 +499,8 @@ module Legion
|
|
|
378
499
|
end
|
|
379
500
|
|
|
380
501
|
desired.each do |instance_id, entry|
|
|
381
|
-
|
|
502
|
+
tracked = @state_mutex.synchronize { @instance_states.key?(instance_id) }
|
|
503
|
+
next if tracked
|
|
382
504
|
|
|
383
505
|
claim_and_activate_instance(name: entry[:name], instance_cfg: entry[:instance_cfg])
|
|
384
506
|
rescue StandardError => e
|
|
@@ -386,53 +508,46 @@ module Legion
|
|
|
386
508
|
instance_name: entry[:name].to_s)
|
|
387
509
|
end
|
|
388
510
|
|
|
389
|
-
|
|
511
|
+
tracked_ids = @state_mutex.synchronize { @instance_states.keys }
|
|
512
|
+
(tracked_ids - desired.keys).each do |instance_id|
|
|
390
513
|
remove_instance_state(instance_id)
|
|
391
514
|
end
|
|
392
515
|
end
|
|
393
516
|
|
|
394
517
|
def tick_refresh
|
|
395
518
|
reconcile_instances
|
|
396
|
-
@
|
|
519
|
+
states = @state_mutex.synchronize { @instance_states.each_pair.to_a }
|
|
520
|
+
states.each do |instance_id, state|
|
|
397
521
|
refresh_instance(instance_id: instance_id, state: state)
|
|
398
522
|
rescue StandardError => e
|
|
399
523
|
handle_exception(e, level: :warn, operation: 'vertex.actor.refresh_instance',
|
|
400
524
|
instance_id: instance_id)
|
|
401
525
|
end
|
|
526
|
+
observe_dormant_weights
|
|
402
527
|
end
|
|
403
528
|
|
|
404
529
|
def refresh_instance(instance_id:, state:)
|
|
405
|
-
|
|
406
|
-
# the cadence probe is the recovery path (re-activation on a
|
|
407
|
-
# passing probe).
|
|
408
|
-
return run_cadence_probe(instance_id: instance_id, state: state) if initializing?(state)
|
|
409
|
-
|
|
410
|
-
new_offerings = discover_offerings_for_instance(instance_cfg: state[:instance_cfg],
|
|
411
|
-
instance_key: state[:instance_key],
|
|
412
|
-
now: state[:evidence_now])
|
|
413
|
-
if new_offerings != state[:offerings]
|
|
414
|
-
state[:sequence] += 1
|
|
415
|
-
publisher.replace_instance_snapshot(instance_id: instance_id,
|
|
416
|
-
publisher_token: state[:publisher_token],
|
|
417
|
-
offerings: new_offerings, sequence: state[:sequence],
|
|
418
|
-
physical_id: state[:physical_id])
|
|
419
|
-
state[:offerings] = new_offerings
|
|
420
|
-
sync_instance_health(name: state[:name], instance_key: state[:instance_key], offerings: new_offerings)
|
|
421
|
-
end
|
|
530
|
+
replace_offerings_if_changed(instance_id: instance_id, state: state)
|
|
422
531
|
run_cadence_probe(instance_id: instance_id, state: state)
|
|
423
532
|
end
|
|
424
533
|
|
|
425
|
-
def initializing?(state)
|
|
426
|
-
publication_state(instance_key: state[:instance_key]) == :initializing
|
|
427
|
-
end
|
|
428
|
-
|
|
429
534
|
def remove_instance_state(instance_id)
|
|
430
|
-
state = @
|
|
535
|
+
state = @state_mutex.synchronize do
|
|
536
|
+
tracked = @instance_states[instance_id]
|
|
537
|
+
next unless tracked
|
|
538
|
+
|
|
539
|
+
publisher.remove_instance(
|
|
540
|
+
instance_id: instance_id,
|
|
541
|
+
publisher_token: tracked[:publisher_token],
|
|
542
|
+
physical_id: tracked[:physical_id]
|
|
543
|
+
)
|
|
544
|
+
tracked[:published] = false
|
|
545
|
+
@instance_states.delete(instance_id)
|
|
546
|
+
tracked
|
|
547
|
+
end
|
|
431
548
|
return unless state
|
|
432
549
|
|
|
433
550
|
state[:callable].disconnect
|
|
434
|
-
publisher.remove_instance(instance_id: instance_id, publisher_token: state[:publisher_token],
|
|
435
|
-
physical_id: state[:physical_id])
|
|
436
551
|
clear_instance_health(name: state[:name])
|
|
437
552
|
rescue StandardError => e
|
|
438
553
|
handle_exception(e, level: :warn, operation: 'vertex.actor.remove_instance',
|
|
@@ -442,7 +557,12 @@ module Legion
|
|
|
442
557
|
def remove_all_instances
|
|
443
558
|
return unless @instance_states
|
|
444
559
|
|
|
445
|
-
@
|
|
560
|
+
instance_ids = @state_mutex.synchronize { @instance_states.keys }
|
|
561
|
+
instance_ids.each { |instance_id| remove_instance_state(instance_id) }
|
|
562
|
+
@state_mutex.synchronize do
|
|
563
|
+
@instance_states.clear
|
|
564
|
+
@dormant_weight_tracker.clear!
|
|
565
|
+
end
|
|
446
566
|
end
|
|
447
567
|
end
|
|
448
568
|
|
|
@@ -463,39 +583,63 @@ module Legion
|
|
|
463
583
|
instance_key = Legion::Extensions::Llm::Inventory::Identity::InstanceKey.new(
|
|
464
584
|
provider_family: :vertex, instance_id: instance_id, physical_id: physical_id
|
|
465
585
|
)
|
|
586
|
+
now = Time.now.freeze
|
|
587
|
+
offerings = discover_offerings_for_instance(instance_cfg: instance_cfg,
|
|
588
|
+
instance_key: instance_key, now: now)
|
|
466
589
|
callable = VertexCallable.new(instance_cfg: instance_cfg, logger: log)
|
|
467
590
|
probe_coordinator = Legion::Extensions::Llm::Inventory::ProbeCoordinator.new(
|
|
468
591
|
instance_key: instance_key, enqueue: build_probe_enqueue(instance_id: instance_id)
|
|
469
592
|
)
|
|
470
593
|
pub_token = publisher.claim_instance(instance_id: instance_id, callable: callable,
|
|
471
594
|
probe_request_handle: probe_coordinator, physical_id: physical_id)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
callable: callable, probe_coordinator: probe_coordinator,
|
|
483
|
-
publisher_token: pub_token, sequence: 0, offerings: offerings, evidence_now: now,
|
|
595
|
+
state = {
|
|
596
|
+
name: name,
|
|
597
|
+
instance_key: instance_key,
|
|
598
|
+
instance_cfg: instance_cfg,
|
|
599
|
+
callable: callable,
|
|
600
|
+
probe_coordinator: probe_coordinator,
|
|
601
|
+
publisher_token: pub_token,
|
|
602
|
+
sequence: 0,
|
|
603
|
+
offerings: offerings,
|
|
604
|
+
evidence_now: now,
|
|
484
605
|
physical_id: physical_id
|
|
485
606
|
}
|
|
486
|
-
|
|
607
|
+
Legion::Extensions::Llm::Inventory::WeightReconciler.track_initializing!(
|
|
608
|
+
states: @instance_states,
|
|
609
|
+
state_key: instance_id,
|
|
610
|
+
state: state,
|
|
611
|
+
mutex: @state_mutex
|
|
612
|
+
)
|
|
613
|
+
probe_token = publisher.readiness_probe_started(instance_id: instance_id, publisher_token: pub_token,
|
|
614
|
+
physical_id: physical_id)
|
|
615
|
+
settled = activate_or_fail_instance(
|
|
616
|
+
instance_id: instance_id,
|
|
617
|
+
probe_token: probe_token,
|
|
618
|
+
state: state
|
|
619
|
+
)
|
|
620
|
+
return unless settled
|
|
621
|
+
|
|
622
|
+
sync_instance_health(
|
|
623
|
+
name: name, instance_key: instance_key, offerings: state[:offerings]
|
|
624
|
+
)
|
|
487
625
|
end
|
|
488
626
|
|
|
489
|
-
def activate_or_fail_instance(instance_id:,
|
|
490
|
-
|
|
491
|
-
readiness = check_health(instance_cfg: instance_cfg)
|
|
627
|
+
def activate_or_fail_instance(instance_id:, probe_token:, state:)
|
|
628
|
+
readiness = check_health(instance_cfg: state[:instance_cfg])
|
|
492
629
|
if readiness.ready?
|
|
493
|
-
|
|
494
|
-
offerings: offerings, sequence: 0, probe_token: probe_token,
|
|
495
|
-
physical_id: physical_id)
|
|
630
|
+
activate_tracked_instance(instance_id: instance_id, state: state, probe_token: probe_token)
|
|
496
631
|
else
|
|
497
|
-
|
|
498
|
-
|
|
632
|
+
@state_mutex.synchronize do
|
|
633
|
+
next false unless @instance_states[instance_id].equal?(state)
|
|
634
|
+
|
|
635
|
+
publisher.readiness_failed(
|
|
636
|
+
instance_id: instance_id,
|
|
637
|
+
probe_token: probe_token,
|
|
638
|
+
reason: readiness.reason,
|
|
639
|
+
physical_id: state[:physical_id]
|
|
640
|
+
)
|
|
641
|
+
true
|
|
642
|
+
end
|
|
499
643
|
end
|
|
500
644
|
end
|
|
501
645
|
end
|
|
@@ -599,7 +743,9 @@ module Legion
|
|
|
599
743
|
include DiscoveryRefreshCapabilityEvidence
|
|
600
744
|
include DiscoveryRefreshConfigHelpers
|
|
601
745
|
include DiscoveryRefreshProbeHelpers
|
|
746
|
+
include DiscoveryRefreshReactiveProbeHelpers
|
|
602
747
|
include DiscoveryRefreshOfferingHelpers
|
|
748
|
+
include DiscoveryRefreshWeightPublication
|
|
603
749
|
include DiscoveryRefreshClaimHelpers
|
|
604
750
|
include DiscoveryRefreshHealthDisplay
|
|
605
751
|
include DiscoveryRefreshLifecycleHelpers
|
|
@@ -480,8 +480,11 @@ module Legion
|
|
|
480
480
|
end
|
|
481
481
|
|
|
482
482
|
def message_parts(message)
|
|
483
|
-
|
|
484
|
-
return
|
|
483
|
+
tool_calls = message.tool_calls
|
|
484
|
+
return tool_call_parts(message) if tool_calls && !tool_calls.empty?
|
|
485
|
+
|
|
486
|
+
tool_call_id = message.tool_call_id
|
|
487
|
+
return tool_result_parts(message) if tool_call_id && !tool_call_id.empty?
|
|
485
488
|
|
|
486
489
|
content_parts(message.content)
|
|
487
490
|
end
|
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.3.
|
|
4
|
+
version: 0.3.4
|
|
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.7.
|
|
74
|
+
version: 0.7.6
|
|
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.7.
|
|
81
|
+
version: 0.7.6
|
|
82
82
|
description: Google Cloud Vertex AI provider integration for the LegionIO LLM routing
|
|
83
83
|
framework.
|
|
84
84
|
email:
|