lex-llm-vllm 0.1.8 → 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: 3dd53d60a8e1aed0d2e1af84c39bf869b31070b927a932d18a69f79990fdd1ec
4
- data.tar.gz: 739b79d90f9b6744b3eef3ff355978820692337f909cb1bb863270fd0d8114d9
3
+ metadata.gz: 261a83f8a4243c795e10e759e5f7f0681cd02d91b91041ff6f11f50d498d1f2a
4
+ data.tar.gz: c136080255383ca0d3417937d3e4988701075fef4df4c33de3e85fbd9f6ae04b
5
5
  SHA512:
6
- metadata.gz: 3f1c76258f803a948b304fca1e887d4b2d8368057914b761033e7cde5f3f44d926209f1598de2acba905f783443a8cd1015318193dfd594a11febacc9821334a
7
- data.tar.gz: af6c18324720d51fb6460b45463955a1944beff9f16edca35b271fb1168dc0a1b3598b4ee3dbce7c1724b0dcef772b83770f330003cea13fee03187761968d23
6
+ metadata.gz: a521587328074e46f4403783b85d001f6a9a4cab77e31556ea565432dea12535ed9d0c656b8f8212b20d37f196d8c08f86be25955ff25813ca908e03b5fa8e60
7
+ data.tar.gz: 3b74fa8c6ecfd4c71fb027a5eb13d3d41b7acf1c42cebcbfeea0e4143ec5ef9ad2693020c284eb3377a8f77fe6cd492c85ed9b9af8f515ad32918f152df224a8
data/.rubocop.yml CHANGED
@@ -12,7 +12,12 @@ Metrics/BlockLength:
12
12
  Exclude:
13
13
  - "*.gemspec"
14
14
  - spec/**/*
15
+ Metrics/ClassLength:
16
+ Exclude:
17
+ - lib/legion/extensions/llm/vllm/provider.rb
15
18
  Metrics/MethodLength:
16
19
  Enabled: false
20
+ RSpec/ExampleLength:
21
+ Max: 8
17
22
  RSpec/MultipleExpectations:
18
23
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
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
+
10
+ ## 0.1.9 - 2026-04-30
11
+
12
+ - Adopt base provider contract from lex-llm 0.1.9
13
+ - Replace local `RegistryEventBuilder` and `RegistryPublisher` with parameterized base versions
14
+ - Delete local `transport/` directory; base gem now ships shared exchange and message classes
15
+ - Remove deprecated `Provider.register` call; provider options registered via `Configuration.register_provider_options`
16
+ - Simplify `default_settings` to a flat hash (no longer delegates to `ProviderSettings.build`)
17
+ - Override `parse_list_models_response` to populate `context_length` from vLLM `max_model_len` field
18
+ - Require `lex-llm >= 0.1.9`
19
+
3
20
  ## 0.1.8 - 2026-04-30
4
21
 
5
22
  - Add `Legion::Logging::Helper` to all modules and classes for structured logging
data/lex-llm-vllm.gemspec CHANGED
@@ -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
@@ -13,16 +13,14 @@ module Legion
13
13
  include Legion::Logging::Helper
14
14
 
15
15
  class << self
16
- attr_writer :registry_publisher
17
-
18
16
  def slug = 'vllm'
19
- def local? = true
17
+ def local? = false
20
18
  def configuration_options = %i[vllm_api_base vllm_api_key]
21
19
  def configuration_requirements = []
22
20
  def capabilities = Capabilities
23
21
 
24
22
  def registry_publisher
25
- @registry_publisher ||= RegistryPublisher.new
23
+ Vllm.registry_publisher
26
24
  end
27
25
  end
28
26
 
@@ -48,8 +46,12 @@ module Legion
48
46
 
49
47
  def stream_usage_supported? = true
50
48
 
49
+ def settings
50
+ Vllm.default_settings
51
+ end
52
+
51
53
  def api_base
52
- config.vllm_api_base || 'http://localhost:8000'
54
+ normalize_url(config.vllm_api_base || 'localhost:8000')
53
55
  end
54
56
 
55
57
  def headers
@@ -135,6 +137,22 @@ module Legion
135
137
  false
136
138
  end
137
139
 
140
+ def parse_list_models_response(response, provider, capabilities)
141
+ response.body.fetch('data', []).map do |model|
142
+ critical_capabilities = critical_capabilities_for(capabilities, model)
143
+ Legion::Extensions::Llm::Model::Info.from_hash(
144
+ id: model.fetch('id'),
145
+ name: model['id'],
146
+ provider: provider,
147
+ created_at: model_created_at(model['created']),
148
+ context_length: model['max_model_len'],
149
+ capabilities: critical_capabilities,
150
+ modalities: modalities_for_capabilities(critical_capabilities),
151
+ metadata: model
152
+ )
153
+ end
154
+ end
155
+
138
156
  def with_query(path, positional = [], **params)
139
157
  pairs = positional + params.compact.map { |key, value| [key.to_s, value] }
140
158
  return path if pairs.empty?
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Vllm
7
- VERSION = '0.1.8'
7
+ VERSION = '0.2.0'
8
8
  end
9
9
  end
10
10
  end
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/llm'
4
- require 'legion/extensions/llm/vllm/registry_event_builder'
5
4
  require 'legion/extensions/llm/vllm/provider'
6
- require 'legion/extensions/llm/vllm/registry_publisher'
7
5
  require 'legion/extensions/llm/vllm/version'
8
6
 
9
7
  module Legion
@@ -13,29 +11,59 @@ module Legion
13
11
  module Vllm
14
12
  extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
15
13
  extend Legion::Logging::Helper
14
+ extend Legion::Extensions::Llm::AutoRegistration
16
15
 
17
16
  PROVIDER_FAMILY = :vllm
18
17
 
19
18
  def self.default_settings
20
- ::Legion::Extensions::Llm.provider_settings(
21
- family: PROVIDER_FAMILY,
22
- instance: {
23
- endpoint: 'http://localhost:8000',
24
- tier: :private,
25
- transport: :http,
26
- usage: { inference: true, embedding: true },
27
- limits: { concurrency: 8 }
28
- }
29
- )
19
+ {
20
+ enabled: false,
21
+ base_url: 'localhost:8000/v1',
22
+ default_model: nil,
23
+ enable_thinking: true,
24
+ model_whitelist: [],
25
+ model_blacklist: [],
26
+ model_cache_ttl: 300,
27
+ tls: { enabled: false, verify: :peer },
28
+ instances: {}
29
+ }
30
30
  end
31
31
 
32
32
  def self.provider_class
33
33
  Provider
34
34
  end
35
+
36
+ def self.registry_publisher
37
+ @registry_publisher ||= Legion::Extensions::Llm::RegistryPublisher.new(provider_family: PROVIDER_FAMILY)
38
+ end
39
+
40
+ def self.discover_instances
41
+ instances = {}
42
+
43
+ if CredentialSources.http_ok?('http://localhost:8000', path: '/health', timeout: 0.1)
44
+ instances[:local] = {
45
+ vllm_api_base: 'http://localhost:8000',
46
+ tier: :local,
47
+ capabilities: [:completion]
48
+ }
49
+ end
50
+
51
+ configured = CredentialSources.setting(:extensions, :llm, :vllm, :instances)
52
+ if configured.is_a?(Hash)
53
+ configured.each do |name, config|
54
+ instances[name.to_sym] = config.merge(tier: :direct)
55
+ end
56
+ end
57
+
58
+ instances
59
+ end
60
+
61
+ if Legion::Extensions::Llm::Configuration.respond_to?(:register_provider_options)
62
+ Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
63
+ end
35
64
  end
36
65
  end
37
66
  end
38
67
  end
39
68
 
40
- Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::Vllm::PROVIDER_FAMILY,
41
- Legion::Extensions::Llm::Vllm::Provider)
69
+ Legion::Extensions::Llm::Vllm.register_discovered_instances
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-vllm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
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: vLLM provider integration for the LegionIO LLM routing framework.
69
69
  email:
70
70
  - matthewdiverson@gmail.com
@@ -84,10 +84,6 @@ files:
84
84
  - lex-llm-vllm.gemspec
85
85
  - lib/legion/extensions/llm/vllm.rb
86
86
  - lib/legion/extensions/llm/vllm/provider.rb
87
- - lib/legion/extensions/llm/vllm/registry_event_builder.rb
88
- - lib/legion/extensions/llm/vllm/registry_publisher.rb
89
- - lib/legion/extensions/llm/vllm/transport/exchanges/llm_registry.rb
90
- - lib/legion/extensions/llm/vllm/transport/messages/registry_event.rb
91
87
  - lib/legion/extensions/llm/vllm/version.rb
92
88
  homepage: https://github.com/LegionIO/lex-llm-vllm
93
89
  licenses:
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vllm
7
- # Builds sanitized lex-llm registry envelopes for vLLM 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 model_available(model, readiness:)
22
- registry_event_class.available(
23
- model_offering(model),
24
- runtime: runtime_metadata,
25
- health: model_health(readiness),
26
- metadata: model_metadata(model)
27
- )
28
- end
29
-
30
- private
31
-
32
- def provider_offering(readiness)
33
- {
34
- provider_family: :vllm,
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_vllm, provider_readiness: true }
42
- }
43
- end
44
-
45
- def model_offering(model)
46
- {
47
- provider_family: :vllm,
48
- provider_instance: provider_instance,
49
- transport: :http,
50
- model: model.id,
51
- usage_type: usage_type_for(model),
52
- capabilities: Array(model.capabilities).map(&:to_sym),
53
- limits: model_limits(model),
54
- metadata: { lex: :llm_vllm, model_name: model.name }.compact
55
- }
56
- end
57
-
58
- def readiness_health(readiness)
59
- health = {
60
- ready: readiness[:ready] == true,
61
- status: readiness[:ready] ? :available : :unavailable,
62
- checked: readiness.dig(:health, :checked) != false
63
- }
64
- add_readiness_error(health, readiness[:health])
65
- end
66
-
67
- def add_readiness_error(health, source)
68
- error = source.is_a?(Hash) ? source : {}
69
- error_class = error[:error] || error['error']
70
- error_message = error[:message] || error['message']
71
- health[:error_class] = error_class if error_class
72
- health[:error] = error_message if error_message
73
- health
74
- end
75
-
76
- def model_health(readiness)
77
- ready = readiness.fetch(:ready, true) == true
78
- { ready:, status: ready ? :available : :degraded }
79
- end
80
-
81
- def readiness_metadata(readiness)
82
- {
83
- extension: :lex_llm_vllm,
84
- provider: :vllm,
85
- configured: readiness[:configured] == true,
86
- live: readiness[:live] == true
87
- }
88
- end
89
-
90
- def model_metadata(model)
91
- { extension: :lex_llm_vllm, provider: :vllm, model_type: model.type }
92
- end
93
-
94
- def runtime_metadata
95
- { node: provider_instance }
96
- end
97
-
98
- def model_limits(model)
99
- {
100
- context_window: model.context_window,
101
- max_output_tokens: model.max_output_tokens
102
- }.compact
103
- end
104
-
105
- def usage_type_for(model)
106
- model.type == 'embedding' ? :embedding : :inference
107
- end
108
-
109
- def provider_instance
110
- configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
111
- value = configured_node.to_s.strip
112
- value.empty? ? :vllm : value.to_sym
113
- rescue StandardError => e
114
- handle_exception(e, level: :debug, handled: true, operation: 'vllm.registry.provider_instance')
115
- :vllm
116
- end
117
-
118
- def registry_event_class
119
- ::Legion::Extensions::Llm::Routing::RegistryEvent
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vllm
7
- # Best-effort publisher for vLLM provider availability events.
8
- class RegistryPublisher
9
- include Legion::Logging::Helper
10
-
11
- APP_ID = 'lex-llm-vllm'
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_models_async(models, readiness:)
23
- log.info { "publishing #{Array(models).size} model event(s) to llm.registry" }
24
- schedule do
25
- Array(models).each do |model|
26
- publish_event(@builder.model_available(model, 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: 'vllm.registry.schedule_thread')
41
- end
42
- rescue StandardError => e
43
- handle_exception(e, level: :debug, handled: true, operation: 'vllm.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: 'vllm.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: 'vllm.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/vllm/transport/messages/registry_event'
77
- message_class_defined?
78
- rescue LoadError => e
79
- handle_exception(e, level: :debug, handled: true, operation: 'vllm.registry.transport_load')
80
- false
81
- end
82
-
83
- def message_class_defined?
84
- defined?(::Legion::Extensions::Llm::Vllm::Transport::Messages::RegistryEvent)
85
- end
86
-
87
- def message_class
88
- ::Legion::Extensions::Llm::Vllm::Transport::Messages::RegistryEvent
89
- end
90
- end
91
- end
92
- end
93
- end
94
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Legion
4
- module Extensions
5
- module Llm
6
- module Vllm
7
- module Transport
8
- module Exchanges
9
- # Topic exchange for vLLM provider availability events.
10
- class LlmRegistry < ::Legion::Transport::Exchange
11
- def exchange_name
12
- 'llm.registry'
13
- end
14
-
15
- def default_type
16
- 'topic'
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'legion/extensions/llm/vllm/transport/exchanges/llm_registry'
4
-
5
- module Legion
6
- module Extensions
7
- module Llm
8
- module Vllm
9
- module Transport
10
- module Messages
11
- # Publishes lex-llm RegistryEvent envelopes to the llm.registry exchange.
12
- class RegistryEvent < ::Legion::Transport::Message
13
- def initialize(event:, **options)
14
- super(**event.to_h.merge(options))
15
- end
16
-
17
- def exchange
18
- Transport::Exchanges::LlmRegistry
19
- end
20
-
21
- def routing_key
22
- @options[:routing_key] || "llm.registry.#{@options.fetch(:event_type)}"
23
- end
24
-
25
- def type
26
- 'llm.registry.event'
27
- end
28
-
29
- def app_id
30
- @options[:app_id] || RegistryPublisher::APP_ID
31
- end
32
-
33
- def persistent # rubocop:disable Naming/PredicateMethod
34
- false
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end