lex-llm-gemini 0.3.10 → 0.3.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: 101069a5f61b6c4a90fe236e40c54ef3af55bdd00d7b6816495eb255aa61cad4
4
- data.tar.gz: adf84ed406fb7419fab87b7a0fc42b123abda739b20889dec8aead89c3adffef
3
+ metadata.gz: d81052fa4455e1c73b7567864422ae494442f0fce0d9206fb0634bc238c939eb
4
+ data.tar.gz: 9f56e59676cc93aa0b55e4d8958975ce3e0a2c02308daaaeecb40c46c8535564
5
5
  SHA512:
6
- metadata.gz: 4b9d7c32d2953485fcd5000b7139012eb1d69470936d8e7a0eadf56b030bbf1b412754f392248c58ff028d17d2e037464384442674a7d5e46699b8844c25202a
7
- data.tar.gz: 941d8fa9761befe73c8324db0452807f073c644f958a0d174dd68fd27be9448172653110dc6cb4e6cd739ea88419c1e0a4f8c5ce614939590171fb36db8f6233
6
+ metadata.gz: 0fe88e33de1fa4575867f35ffe717528a3759aa1b0e3c15fe42a9cfe99ff8e00826b0a8069f1d4788091b17448c307cf8e4ad694684c166249b9a57360c8dccc
7
+ data.tar.gz: 999f873f14e211211232b72ba3f5d5dd6dd31560de9e44081a1e2355c6574baa7a4a169701492a00803fcb9fb1bc6b9d73de37f399cb2439505a7c3c970717a7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.13 - 2026-06-16
4
+
5
+ - Dependency updates, code quality improvements.
6
+
7
+ ## 0.3.12 - 2026-06-15
8
+
9
+ - **CapabilityPolicy integration** — `supportedGenerationMethods` mapped to `:model_metadata`. Settings overrides at provider/instance/model level supported.
10
+
11
+ ## 0.3.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
+ - 22 examples, 0 failures; 13 files, 0 rubocop offenses.
18
+
3
19
  ## 0.3.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: :gemini)
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: 'gemini.actor.discovery_refresh')
41
48
  end
@@ -187,7 +187,9 @@ module Legion
187
187
  end
188
188
 
189
189
  def tool_call_parts(message)
190
- message.tool_calls.values.map do |tool_call|
190
+ # Array is canonical (name-keyed hashes dropped parallel same-name calls)
191
+ calls = message.tool_calls.is_a?(Hash) ? message.tool_calls.values : Array(message.tool_calls)
192
+ calls.map do |tool_call|
191
193
  { functionCall: { name: tool_call.name, args: tool_call.arguments } }
192
194
  end
193
195
  end
@@ -204,9 +206,12 @@ module Legion
204
206
  def format_tools(tools)
205
207
  [{
206
208
  functionDeclarations: tools.values.map do |tool|
207
- declaration = { name: tool.name, description: tool.description }
208
- declaration[:parameters] = tool.params_schema if tool.params_schema
209
- declaration
209
+ schema = Legion::Extensions::Llm::Canonical::ToolSchema.extract(tool)
210
+ {
211
+ name: Legion::Extensions::Llm::Canonical::ToolSchema.tool_name(tool),
212
+ description: Legion::Extensions::Llm::Canonical::ToolSchema.tool_description(tool),
213
+ parameters: schema
214
+ }
210
215
  end
211
216
  }]
212
217
  end
@@ -307,6 +312,95 @@ module Legion
307
312
  end
308
313
  end
309
314
 
315
+ # -- policy resolution requires many sources
316
+ def offering_from_model(model, health: {})
317
+ policy = Legion::Extensions::Llm::CapabilityPolicy.resolve(
318
+ real: real_capabilities_from(model),
319
+ provider_catalog: {},
320
+ probe: {},
321
+ provider_envelope: {},
322
+ provider_config: provider_capability_config,
323
+ instance_config: instance_capability_config,
324
+ model_config: model_capability_config(model.id)
325
+ )
326
+
327
+ build_model_offering(model, policy, health)
328
+ end
329
+
330
+ def build_model_offering(model, policy, health)
331
+ Routing::ModelOffering.new(
332
+ provider_family: slug.to_sym,
333
+ provider_instance: model.instance || provider_instance_id,
334
+ transport: offering_transport,
335
+ tier: offering_tier,
336
+ model: model.id,
337
+ canonical_model_alias: model.name,
338
+ model_family: model.family,
339
+ usage_type: offering_usage_type(model),
340
+ capabilities: policy[:capabilities],
341
+ capability_sources: policy[:sources],
342
+ limits: offering_limits(model),
343
+ health:,
344
+ metadata: offering_metadata(model)
345
+ )
346
+ end
347
+
348
+ def real_capabilities_from(model)
349
+ meta = model.respond_to?(:metadata) ? (model.metadata || {}) : {}
350
+ methods = Array(meta[:supported_generation_methods] || meta['supported_generation_methods'])
351
+ {
352
+ streaming: methods.include?('streamGenerateContent'),
353
+ embeddings: methods.include?('embedContent'),
354
+ vision: Capabilities.vision?(meta.merge('name' => "models/#{model.id}"))
355
+ }.compact
356
+ end
357
+
358
+ def provider_capability_config
359
+ return {} unless defined?(Legion::Extensions::Llm::CredentialSources)
360
+
361
+ conf = Legion::Extensions::Llm::CredentialSources.setting(:extensions, :llm, :gemini)
362
+ conf.is_a?(Hash) ? conf.to_h.except(:instances, 'instances') : {}
363
+ rescue StandardError => e
364
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.provider_capability_config')
365
+ {}
366
+ end
367
+
368
+ def instance_capability_config
369
+ cfg = config
370
+ result = {}
371
+ %i[capabilities enable_streaming enable_tools enable_thinking enable_vision enable_embeddings
372
+ thinking_flag tools_flag streaming_flag vision_flag embedding_flag embeddings_flag
373
+ tool_flag images_flag image_flag].each do |key|
374
+ next unless cfg.respond_to?(key)
375
+
376
+ val = cfg.send(key)
377
+ result[key] = val unless val.nil?
378
+ rescue StandardError
379
+ next
380
+ end
381
+ result
382
+ end
383
+
384
+ def model_capability_config(model_id)
385
+ models_conf = resolve_models_config
386
+ return {} unless models_conf.respond_to?(:to_h)
387
+
388
+ hash = models_conf.to_h
389
+ hash[model_id.to_s] || hash[model_id.to_sym] || {}
390
+ rescue StandardError => e
391
+ handle_exception(e, level: :debug, handled: true, operation: 'gemini.model_capability_config')
392
+ {}
393
+ end
394
+
395
+ def resolve_models_config
396
+ return config.models if config.respond_to?(:models)
397
+ return config[:models] if config.respond_to?(:[])
398
+
399
+ nil
400
+ rescue StandardError
401
+ nil
402
+ end
403
+
310
404
  def modalities_for(methods)
311
405
  return [%w[text], %w[embeddings]] if methods.include?('embedContent')
312
406
 
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Gemini
7
- VERSION = '0.3.10'
7
+ VERSION = '0.3.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/gemini/provider'
5
5
  require 'legion/extensions/llm/gemini/version'
6
+ require_relative 'gemini/actors/discovery_refresh'
6
7
 
7
8
  module Legion
8
9
  module Extensions
@@ -30,10 +31,7 @@ module Legion
30
31
  fleet: {
31
32
  enabled: false,
32
33
  respond_to_requests: false,
33
- capabilities: %i[chat stream_chat embed],
34
- lanes: [],
35
- concurrency: 4,
36
- queue_suffix: nil
34
+ capabilities: %i[chat stream_chat embed tools]
37
35
  }
38
36
  }
39
37
  )
@@ -106,8 +104,7 @@ module Legion
106
104
  :add_settings_api_key, :add_settings_instances, :normalize_instance_config,
107
105
  :sanitize_instance_config
108
106
 
109
- Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options) if
110
- Legion::Extensions::Llm::Configuration.respond_to?(:register_provider_options)
107
+ Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
111
108
  end
112
109
  end
113
110
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-gemini
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.10
4
+ version: 0.3.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: Gemini provider integration for the LegionIO LLM routing framework.
83
83
  email:
84
84
  - matthewdiverson@gmail.com