lex-llm-vertex 0.2.10 → 0.2.13

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: 25083d8e7cc928d57127feab13406395a6600421913559042fecfe7e2a376267
4
- data.tar.gz: '028cb717f3299a2683bdd0ed10cc59f91a75f3de5f773d730ec75e2374762170'
3
+ metadata.gz: 31112ce3580ae2a99c27285cbc5385d742faf52f4aaa854212802254c423112a
4
+ data.tar.gz: 64ba36b8b97732e2d2b63605e2f7edb33f82b42065f5f79d8c2328d0dd3e3b35
5
5
  SHA512:
6
- metadata.gz: 69f44a1e8ac1d5d0da6b9e4ca5b5d5f7f0fba37b49021e5a512db86884958a89abd20323c336b9292c1973fb47e303d37b715322aaf37da75ab1f3d5da33a48f
7
- data.tar.gz: 53cb2cfeb3039b78ca84722943e220178b6a7c1a060972ca329aacd4acb742bac4e4a581d9df819fa1902f315ca73858d9401d29ec08318c7ce0290899b11fc7
6
+ metadata.gz: 924fe07b104555a416524b8d7e9994e0a09e6051e307034161d485d285cad2bd44c7cc26653420cfee4d629e757b6cdbd02088d2c375e032c437eb660fd7a28d
7
+ data.tar.gz: a1467f3de89eba4f4cc7e852e49f33153fe8caa79878a2da1b57e147a95a50a8122a1b1c60ab018cbb90e12c6b2a7c0b774dc9962f0288815b7e871fae6fdcc6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.13 - 2026-06-16
4
+
5
+ - Dependency updates and code quality improvements.
6
+
7
+ ## 0.2.12 - 2026-06-15
8
+
9
+ - **CapabilityPolicy integration** — Model-family heuristics tagged as `:provider_catalog`; Vertex features as `:model_metadata`. Settings overrides at provider/instance/model level supported.
10
+
11
+ ## 0.2.11 - 2026-06-13
12
+
13
+ - **Gemfile cleanup** — Remove local path overrides; dependencies resolve from gemspec via rubygems.
14
+ - **Dependency bump** — Require `lex-llm >= 0.5.0` for canonical types support.
15
+ - **Canonical tool support** — Use `ToolSchema.extract` and add `:tools` capability.
16
+ - **Bug fix** — Handle Array tool_calls in `tool_call_parts`.
17
+ - 29 examples, 0 failures; 13 files, 0 rubocop offenses.
18
+
3
19
  ## 0.2.10 - 2026-06-02
4
20
 
5
21
  - Add per-provider scoped discovery refresh actor
data/Gemfile CHANGED
@@ -2,13 +2,6 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- group :test do
6
- llm_base_path = ENV.fetch('LEX_LLM_PATH', File.expand_path('../lex-llm', __dir__))
7
- transport_path = ENV.fetch('LEGION_TRANSPORT_PATH', File.expand_path('../../legion-transport', __dir__))
8
- gem 'legion-transport', path: transport_path if File.directory?(transport_path)
9
- gem 'lex-llm', path: llm_base_path if File.directory?(llm_base_path)
10
- end
11
-
12
5
  gemspec
13
6
 
14
7
  group :development do
@@ -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.4.3'
30
+ spec.add_dependency 'lex-llm', '>= 0.5.0'
31
31
  end
@@ -36,6 +36,13 @@ module Legion
36
36
  return unless defined?(Legion::LLM::Discovery)
37
37
 
38
38
  Legion::LLM::Discovery.refresh_discovered_models!(provider: :vertex)
39
+
40
+ if defined?(Legion::LLM::Router) && Legion::LLM::Router.respond_to?(:populate_auto_rules)
41
+ Legion::LLM::Router.populate_auto_rules(Legion::LLM::Discovery.discovered_instances)
42
+ end
43
+ if defined?(Legion::LLM::Inventory) && Legion::LLM::Inventory.respond_to?(:invalidate_offerings_cache!)
44
+ Legion::LLM::Inventory.invalidate_offerings_cache!
45
+ end
39
46
  rescue StandardError => e
40
47
  handle_exception(e, level: :warn, handled: true, operation: 'vertex.actor.discovery_refresh')
41
48
  end
@@ -327,6 +327,8 @@ module Legion
327
327
 
328
328
  def build_offering(model:, model_family:, usage_type:, publisher:, api:, instance_id: :default,
329
329
  alias_name: nil, metadata: {})
330
+ policy = resolve_capability_policy(model, api:, metadata:, instance_id:)
331
+
330
332
  Legion::Extensions::Llm::Routing::ModelOffering.new(
331
333
  provider_family: :vertex,
332
334
  instance_id: instance_id,
@@ -334,7 +336,8 @@ module Legion
334
336
  tier: offering_tier,
335
337
  model: model,
336
338
  usage_type: usage_type,
337
- capabilities: default_capabilities(model, api:),
339
+ capabilities: base_capabilities(model, api:) + policy[:capabilities],
340
+ capability_sources: policy[:sources],
338
341
  limits: metadata.delete(:limits) || {},
339
342
  metadata: metadata.merge(
340
343
  model_family: model_family,
@@ -478,7 +481,9 @@ module Legion
478
481
  end
479
482
 
480
483
  def tool_call_parts(message)
481
- message.tool_calls.values.map do |tool_call|
484
+ # Array is canonical (name-keyed hashes dropped parallel same-name calls)
485
+ calls = message.tool_calls.is_a?(Hash) ? message.tool_calls.values : Array(message.tool_calls)
486
+ calls.map do |tool_call|
482
487
  { functionCall: { name: tool_call.name, args: tool_call.arguments } }
483
488
  end
484
489
  end
@@ -497,9 +502,11 @@ module Legion
497
502
 
498
503
  [{
499
504
  functionDeclarations: tools.values.map do |tool|
500
- declaration = { name: tool.name, description: tool.description }
501
- declaration[:parameters] = tool.params_schema if tool.respond_to?(:params_schema) && tool.params_schema
502
- declaration
505
+ {
506
+ name: Legion::Extensions::Llm::Canonical::ToolSchema.tool_name(tool),
507
+ description: Legion::Extensions::Llm::Canonical::ToolSchema.tool_description(tool),
508
+ parameters: Legion::Extensions::Llm::Canonical::ToolSchema.extract(tool)
509
+ }
503
510
  end
504
511
  }]
505
512
  end
@@ -623,15 +630,109 @@ module Legion
623
630
  end
624
631
 
625
632
  def default_capabilities(model, api:)
633
+ base_capabilities(model, api:) + policy_optional_capabilities(model, api:)
634
+ end
635
+
636
+ def base_capabilities(model, api:)
626
637
  return %i[embedding] if Capabilities.embeddings?(model)
627
638
 
628
639
  capabilities = %i[chat]
629
640
  capabilities << :streaming if %i[generate_content raw_predict].include?(api)
630
- capabilities << :vision if Capabilities.vision?(model)
631
- capabilities << :functions if generate_content_model?(model)
632
641
  capabilities
633
642
  end
634
643
 
644
+ def policy_optional_capabilities(model, api:)
645
+ return [] if Capabilities.embeddings?(model)
646
+
647
+ caps = []
648
+ caps << :vision if Capabilities.vision?(model)
649
+ caps << :tools if generate_content_model?(model) && api == :generate_content
650
+ caps
651
+ end
652
+
653
+ def resolve_capability_policy(model, api:, metadata:, instance_id:)
654
+ provider_catalog = capability_catalog_for(model, api:)
655
+ real_caps = capability_real_for(metadata)
656
+ provider_cfg = vertex_provider_config
657
+ instance_cfg = vertex_instance_config(instance_id)
658
+ model_cfg = vertex_model_config(model)
659
+
660
+ Legion::Extensions::Llm::CapabilityPolicy.resolve(
661
+ real: real_caps,
662
+ provider_catalog: provider_catalog,
663
+ probe: {},
664
+ provider_envelope: {},
665
+ provider_config: provider_cfg,
666
+ instance_config: instance_cfg,
667
+ model_config: model_cfg
668
+ )
669
+ end
670
+
671
+ def capability_catalog_for(model, api:)
672
+ return {} if Capabilities.embeddings?(model)
673
+
674
+ catalog = {}
675
+ catalog[:vision] = Capabilities.vision?(model)
676
+ catalog[:tools] = api == :generate_content
677
+ catalog[:streaming] = %i[generate_content raw_predict].include?(api)
678
+ catalog
679
+ end
680
+
681
+ def capability_real_for(metadata)
682
+ return {} unless metadata.is_a?(Hash)
683
+
684
+ features = metadata[:supportedFeatures] || metadata['supportedFeatures']
685
+ return {} unless features.is_a?(Hash)
686
+
687
+ real = {}
688
+ real[:tools] = features['functionCalling'] if features.key?('functionCalling')
689
+ real[:vision] = features['multimodalInput'] if features.key?('multimodalInput')
690
+ real[:thinking] = features['thinking'] if features.key?('thinking')
691
+ real
692
+ end
693
+
694
+ def vertex_provider_config
695
+ cfg = CredentialSources.setting(:extensions, :llm, :vertex)
696
+ return {} unless cfg.is_a?(Hash)
697
+
698
+ cfg.except(:instances, 'instances')
699
+ rescue StandardError => e
700
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.provider.capability_policy_config')
701
+ {}
702
+ end
703
+
704
+ def vertex_instance_config(instance_id)
705
+ cfg = CredentialSources.setting(:extensions, :llm, :vertex)
706
+ return {} unless cfg.is_a?(Hash)
707
+
708
+ instances = cfg[:instances] || cfg['instances']
709
+ return {} unless instances.is_a?(Hash)
710
+
711
+ (instances[instance_id] || instances[instance_id.to_s] || {}).to_h
712
+ rescue StandardError => e
713
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.provider.instance_config')
714
+ {}
715
+ end
716
+
717
+ def vertex_model_config(model)
718
+ cfg = CredentialSources.setting(:extensions, :llm, :vertex)
719
+ return {} unless cfg.is_a?(Hash)
720
+
721
+ models = cfg[:models] || cfg['models']
722
+ return {} unless models.is_a?(Hash)
723
+
724
+ id = short_model_id(model)
725
+ (models[id.to_sym] || models[id.to_s] || {}).to_h
726
+ rescue StandardError => e
727
+ handle_exception(e, level: :debug, handled: true, operation: 'vertex.provider.model_config')
728
+ {}
729
+ end
730
+
731
+ def short_model_id(model)
732
+ id = model_id(model)
733
+ id.include?('/') ? id.split('/').last : id
734
+ end
735
+
635
736
  def bearer_token
636
737
  token = config.vertex_access_token
637
738
  token ? "Bearer #{token}" : nil
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Vertex
7
- VERSION = '0.2.10'
7
+ VERSION = '0.2.13'
8
8
  end
9
9
  end
10
10
  end
@@ -3,6 +3,7 @@
3
3
  require 'legion/extensions/llm'
4
4
  require 'legion/extensions/llm/vertex/provider'
5
5
  require 'legion/extensions/llm/vertex/version'
6
+ require_relative 'vertex/actors/discovery_refresh'
6
7
 
7
8
  module Legion
8
9
  module Extensions
@@ -39,10 +40,7 @@ module Legion
39
40
  fleet: {
40
41
  enabled: false,
41
42
  respond_to_requests: false,
42
- capabilities: %i[chat stream_chat embed],
43
- lanes: [],
44
- concurrency: 4,
45
- queue_suffix: nil
43
+ capabilities: %i[chat stream_chat embed tools]
46
44
  }
47
45
  }
48
46
  )
@@ -103,12 +101,7 @@ module Legion
103
101
  end
104
102
 
105
103
  def self.register_provider_options
106
- configuration = Legion::Extensions::Llm::Configuration
107
- if configuration.respond_to?(:register_provider_options)
108
- configuration.register_provider_options(Provider.configuration_options)
109
- elsif configuration.respond_to?(:option, true)
110
- Provider.configuration_options.each { |key| configuration.send(:option, key) }
111
- end
104
+ Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
112
105
  end
113
106
 
114
107
  private_class_method :discover_default_instance, :discover_named_instances, :vertex_credentials_present?,
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.10
4
+ version: 0.2.13
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.4.3
74
+ version: 0.5.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.4.3
81
+ version: 0.5.0
82
82
  description: Google Cloud Vertex AI provider integration for the LegionIO LLM routing
83
83
  framework.
84
84
  email: