lex-llm-vllm 0.2.11 → 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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +213 -31
- data/lib/legion/extensions/llm/vllm/actors/discovery_refresh.rb +48 -0
- data/lib/legion/extensions/llm/vllm/version.rb +1 -1
- data/lib/legion/extensions/llm/vllm.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6adc86b9d3286821c0efa59e4c820f3d99ee0acb5327f133a96010383d154505
|
|
4
|
+
data.tar.gz: 73ecff7ccc309eb0469a79edc3970fdf3d766199a6df31edde4cbaf2016dc970
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b2c498e26f09fa27edfa7abf08bf6fae656313cf6e2ce625772a9ce809ff1fcfae55a8746261b943b6b41111a46784fa97d9d5004f4be69f58761de05c6383d
|
|
7
|
+
data.tar.gz: 94b867bd099f8e062f23aee550be30d4549bbc3d4f10d40b6e4e9b3dcabca7c8837e246198a6ea9a208035da3dcfaaf41be626709de9c8e642e3a4035f6681b0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.13 - 2026-06-05
|
|
4
|
+
|
|
5
|
+
- Fix missing documentation comment on `DiscoveryRefresh` actor (RuboCop Style/Documentation)
|
|
6
|
+
|
|
7
|
+
## 0.2.12 - 2026-05-29
|
|
8
|
+
|
|
9
|
+
- Add capabilities `[:completion, :streaming, :vision, :tools]` to `DEFAULT_INSTANCE_TIER` so routing can match vLLM instances by required capability without live discovery
|
|
10
|
+
|
|
3
11
|
## 0.2.11 - 2026-05-21
|
|
4
12
|
|
|
5
13
|
- Add `default_transport`/`default_tier` class declarations, remove duplicate instance methods
|
data/README.md
CHANGED
|
@@ -2,24 +2,138 @@
|
|
|
2
2
|
|
|
3
3
|
LegionIO LLM provider extension for [vLLM](https://docs.vllm.ai/).
|
|
4
4
|
|
|
5
|
-
This gem
|
|
5
|
+
This gem provides a complete vLLM adapter for the LegionIO LLM routing layer. It speaks the OpenAI-compatible API, discovers models at runtime, publishes availability events, and supports vLLM-specific features like thinking mode and server lifecycle management.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Namespace:** `Legion::Extensions::Llm::Vllm`
|
|
8
|
+
**Provider slug:** `:vllm`
|
|
9
|
+
**Dependency:** `lex-llm >= 0.4.3`
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Load with:
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
13
|
+
```ruby
|
|
14
|
+
require 'legion/extensions/llm/vllm'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Architecture at a Glance
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Legion::Extensions::Llm::Vllm # Root module (namespace, discovery, defaults)
|
|
23
|
+
|-- Provider # Per-instance provider (chat, models, management)
|
|
24
|
+
| |-- OpenAICompatible (mixin) # Shared request/response handling
|
|
25
|
+
| |-- Capabilities (module) # Capability predicates for offerings
|
|
26
|
+
|
|
|
27
|
+
|-- Actor::DiscoveryRefresh # Periodic actor: refreshes discovered model list
|
|
28
|
+
|-- Actor::FleetWorker # Subscription actor: consumes fleet requests
|
|
29
|
+
|
|
|
30
|
+
|-- Runners::FleetWorker # Runner: delegates to Fleet::ProviderResponder
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### File Map
|
|
34
|
+
|
|
35
|
+
| File | What |
|
|
36
|
+
|------|------|
|
|
37
|
+
| `lib/legion/extensions/llm/vllm.rb` | Root module, `discover_instances`, `default_settings`, alias normalization |
|
|
38
|
+
| `lib/legion/extensions/llm/vllm/version.rb` | `VERSION` constant |
|
|
39
|
+
| `lib/legion/extensions/llm/vllm/provider.rb` | Provider class, chat/embeddings/model discovery, management endpoints |
|
|
40
|
+
| `lib/legion/extensions/llm/vllm/actors/discovery_refresh.rb` | Periodic actor to refresh model discovery cache |
|
|
41
|
+
| `lib/legion/extensions/llm/vllm/actors/fleet_worker.rb` | Subscription actor for fleet request consumption |
|
|
42
|
+
| `lib/legion/extensions/llm/vllm/runners/fleet_worker.rb` | Runner entrypoint that delegates to `Fleet::ProviderResponder` |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Key Classes
|
|
47
|
+
|
|
48
|
+
### `Legion::Extensions::Llm::Vllm` (Root Module)
|
|
49
|
+
|
|
50
|
+
The top-level module. It handles auto-registration via `Legion::Extensions::Llm::AutoRegistration`, instance discovery, and configuration normalization.
|
|
51
|
+
|
|
52
|
+
**Constants:**
|
|
53
|
+
- `PROVIDER_FAMILY` — `:vllm`
|
|
54
|
+
- `DEFAULT_INSTANCE_TIER` — `{ tier: :direct, capabilities: [:completion, :streaming, :vision, :tools] }`
|
|
55
|
+
|
|
56
|
+
**Class methods:**
|
|
57
|
+
|
|
58
|
+
| Method | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| `default_settings` | Returns the full default settings hash (endpoint, fleet, thinking, etc.) |
|
|
61
|
+
| `provider_class` | Returns `Provider` |
|
|
62
|
+
| `registry_publisher` | Memoized `Legion::Extensions::Llm::RegistryPublisher` instance |
|
|
63
|
+
| `discover_instances` | Probes `localhost:8000` health endpoint, merges configured instances from `Legion::Settings` |
|
|
64
|
+
| `normalize_instance_config(config)` | Normalizes config keys (`base_url`/`api_base`/`endpoint` -> `vllm_api_base`), infers tier |
|
|
65
|
+
| `normalize_api_base(url)` | Strips trailing `/v1` from URLs |
|
|
66
|
+
| `infer_tier_from_endpoint(url)` | Returns `:local` for localhost addresses, `:direct` otherwise |
|
|
67
|
+
|
|
68
|
+
**Instance discovery sources:**
|
|
69
|
+
1. HTTP health probe against `http://localhost:8000` (0.1s timeout) -> `:local` tier
|
|
70
|
+
2. Configured instances under `Legion::Settings[:extensions][:llm][:vllm][:instances]`
|
|
71
|
+
|
|
72
|
+
### `Legion::Extensions::Llm::Vllm::Provider`
|
|
73
|
+
|
|
74
|
+
The per-instance provider class. Inherits from `Legion::Extensions::Llm::Provider` and mixes in `OpenAICompatible` for shared HTTP request/response handling.
|
|
75
|
+
|
|
76
|
+
**Class methods:**
|
|
77
|
+
|
|
78
|
+
| Method | Returns |
|
|
79
|
+
|--------|---------|
|
|
80
|
+
| `slug` | `'vllm'` |
|
|
81
|
+
| `local?` | `false` |
|
|
82
|
+
| `default_transport` | `:http` |
|
|
83
|
+
| `default_tier` | `:direct` |
|
|
84
|
+
| `configuration_options` | `[:vllm_api_base, :vllm_api_key]` |
|
|
85
|
+
| `configuration_requirements` | `[]` (no required fields) |
|
|
86
|
+
| `capabilities` | `Capabilities` module |
|
|
87
|
+
| `registry_publisher` | Delegates to `Vllm.registry_publisher` |
|
|
88
|
+
|
|
89
|
+
**Instance methods:**
|
|
90
|
+
|
|
91
|
+
| Method | Description |
|
|
92
|
+
|--------|-------------|
|
|
93
|
+
| `api_base` | Normalized API root from config, settings, or `http://localhost:8000` |
|
|
94
|
+
| `headers` | Identity headers + optional Bearer token |
|
|
95
|
+
| `settings` | Returns `Vllm.default_settings` |
|
|
96
|
+
| `health(live:)` | `GET /health` |
|
|
97
|
+
| `readiness(live:)` | Checks readiness, publishes async readiness event when `live: true` |
|
|
98
|
+
| `list_models` | `GET /v1/models`, publishes async model availability events |
|
|
99
|
+
| `discover_offerings(live:, **)` | Builds `ModelOffering` instances from discovered models (uses cache when not live) |
|
|
100
|
+
| `version` | `GET /version` |
|
|
101
|
+
| `fetch_model_detail(model_name)` | Re-fetches `/v1/models` to resolve `context_window` on cache miss |
|
|
102
|
+
| `stream_usage_supported?` | Always `true` for vLLM |
|
|
103
|
+
| `reset_prefix_cache(reset_running_requests:, reset_external:)` | `POST /reset_prefix_cache` |
|
|
104
|
+
| `reset_mm_cache` | `POST /reset_mm_cache` |
|
|
105
|
+
| `sleep(level:)` | `POST /sleep` |
|
|
106
|
+
| `wake_up(tags:)` | `POST /wake_up` |
|
|
107
|
+
|
|
108
|
+
**Payload rendering:** Overrides `render_payload` to support vLLM thinking mode via `chat_template_kwargs` and strips `reasoning_effort`.
|
|
109
|
+
|
|
110
|
+
### `Provider::Capabilities` (Module)
|
|
111
|
+
|
|
112
|
+
Predicate methods for model capability detection. All return `true` for vLLM by default:
|
|
113
|
+
|
|
114
|
+
- `chat?(model)`, `streaming?(model)`, `vision?(model)`, `functions?(model)`, `embeddings?(model)`
|
|
115
|
+
- `critical_capabilities_for(model)` — returns array of active capability names
|
|
116
|
+
|
|
117
|
+
### `Actor::DiscoveryRefresh`
|
|
118
|
+
|
|
119
|
+
Periodic actor (extends `Legion::Extensions::Actors::Every`) that refreshes the vLLM discovered model list.
|
|
120
|
+
|
|
121
|
+
- **Default interval:** 1800 seconds (30 minutes)
|
|
122
|
+
- **Configurable via:** `Legion::Settings[:extensions][:llm][:vllm][:discovery_interval]`
|
|
123
|
+
- **Action:** Calls `Legion::LLM::Discovery.refresh_discovered_models!(provider: :vllm)`
|
|
124
|
+
|
|
125
|
+
### `Actor::FleetWorker`
|
|
126
|
+
|
|
127
|
+
Subscription actor (extends `Legion::Extensions::Actors::Subscription`) that consumes LLM fleet requests routed to vLLM.
|
|
128
|
+
|
|
129
|
+
- Only activates when `Fleet::ProviderResponder.enabled_for?` returns true for discovered instances
|
|
130
|
+
- Delegates execution to `Runners::FleetWorker.handle_fleet_request`
|
|
131
|
+
|
|
132
|
+
### `Runners::FleetWorker`
|
|
133
|
+
|
|
134
|
+
Runner module that dispatches fleet requests to `Legion::Extensions::Llm::Fleet::ProviderResponder` with vLLM-specific context (provider family, class, instance discovery callback).
|
|
135
|
+
|
|
136
|
+
---
|
|
23
137
|
|
|
24
138
|
## Defaults
|
|
25
139
|
|
|
@@ -49,8 +163,12 @@ Legion::Extensions::Llm::Vllm.default_settings
|
|
|
49
163
|
# }
|
|
50
164
|
```
|
|
51
165
|
|
|
166
|
+
---
|
|
167
|
+
|
|
52
168
|
## Configuration
|
|
53
169
|
|
|
170
|
+
### Per-instance via Legion::Extensions::Llm.configure
|
|
171
|
+
|
|
54
172
|
```ruby
|
|
55
173
|
Legion::Extensions::Llm.configure do |config|
|
|
56
174
|
config.vllm_api_base = "http://localhost:8000"
|
|
@@ -60,9 +178,36 @@ Legion::Extensions::Llm.configure do |config|
|
|
|
60
178
|
end
|
|
61
179
|
```
|
|
62
180
|
|
|
181
|
+
### Multi-instance via Legion::Settings
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
extensions:
|
|
185
|
+
llm:
|
|
186
|
+
vllm:
|
|
187
|
+
discovery_interval: 1800 # seconds between model list refreshes
|
|
188
|
+
instances:
|
|
189
|
+
production:
|
|
190
|
+
vllm_api_base: "https://vllm.example.com"
|
|
191
|
+
tier: :direct
|
|
192
|
+
local:
|
|
193
|
+
vllm_api_base: "http://localhost:8000"
|
|
194
|
+
tier: :local
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Endpoint alias normalization
|
|
198
|
+
|
|
199
|
+
The following keys are all resolved to `vllm_api_base` during instance config normalization:
|
|
200
|
+
- `base_url`
|
|
201
|
+
- `api_base`
|
|
202
|
+
- `endpoint`
|
|
203
|
+
|
|
204
|
+
Trailing `/v1` is stripped automatically.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
63
208
|
## Fleet Responder
|
|
64
209
|
|
|
65
|
-
Provider instances can opt in to consuming Legion LLM fleet requests. The
|
|
210
|
+
Provider instances can opt in to consuming Legion LLM fleet requests. The fleet actor only starts when at least one configured instance enables `respond_to_requests`.
|
|
66
211
|
|
|
67
212
|
```yaml
|
|
68
213
|
extensions:
|
|
@@ -79,29 +224,51 @@ extensions:
|
|
|
79
224
|
- embed
|
|
80
225
|
```
|
|
81
226
|
|
|
82
|
-
|
|
227
|
+
Execution flows: `Actor::FleetWorker` (receives message) -> `Runners::FleetWorker.handle_fleet_request` -> `Fleet::ProviderResponder.call`.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Thinking Mode
|
|
83
232
|
|
|
84
|
-
|
|
233
|
+
vLLM supports a "thinking" mode that enables extended reasoning. Enable via:
|
|
85
234
|
|
|
235
|
+
**Instance-level:**
|
|
236
|
+
```yaml
|
|
237
|
+
extensions:
|
|
238
|
+
llm:
|
|
239
|
+
vllm:
|
|
240
|
+
instances:
|
|
241
|
+
default:
|
|
242
|
+
enable_thinking: true
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Global:**
|
|
86
246
|
```ruby
|
|
87
|
-
#
|
|
247
|
+
# Legion::Settings or settings JSON
|
|
88
248
|
{ llm: { providers: { vllm: { enable_thinking: true } } } }
|
|
89
249
|
```
|
|
90
250
|
|
|
91
|
-
|
|
251
|
+
**Per-request:**
|
|
252
|
+
```ruby
|
|
253
|
+
# Pass thinking: { enabled: true } in the chat kwargs
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
When enabled, the provider adds `chat_template_kwargs: { enable_thinking: true }` to the chat payload and strips the OpenAI-specific `reasoning_effort` key.
|
|
257
|
+
|
|
258
|
+
---
|
|
92
259
|
|
|
93
260
|
## Management Endpoints
|
|
94
261
|
|
|
95
|
-
|
|
262
|
+
| Method | Endpoint | Kwargs | Description |
|
|
263
|
+
|--------|----------|--------|-------------|
|
|
264
|
+
| `health(live:)` | `GET /health` | `live:` | Server health check |
|
|
265
|
+
| `version` | `GET /version` | none | Server version info |
|
|
266
|
+
| `reset_prefix_cache` | `POST /reset_prefix_cache` | `reset_running_requests:`, `reset_external:` | Clear prefix cache |
|
|
267
|
+
| `reset_mm_cache` | `POST /reset_mm_cache` | none | Clear multimodal cache |
|
|
268
|
+
| `sleep(level:)` | `POST /sleep` | `level:` (default: 1) | Put worker to sleep |
|
|
269
|
+
| `wake_up(tags:)` | `POST /wake_up` | `tags:` | Wake worker up |
|
|
96
270
|
|
|
97
|
-
|
|
98
|
-
|--------|----------|-------------|
|
|
99
|
-
| `health` | `GET /health` | Server health check |
|
|
100
|
-
| `version` | `GET /version` | Server version info |
|
|
101
|
-
| `reset_prefix_cache` | `POST /reset_prefix_cache` | Clear prefix cache |
|
|
102
|
-
| `reset_mm_cache` | `POST /reset_mm_cache` | Clear multimodal cache |
|
|
103
|
-
| `sleep(level:)` | `POST /sleep` | Put server to sleep |
|
|
104
|
-
| `wake_up(tags:)` | `POST /wake_up` | Wake server up |
|
|
271
|
+
---
|
|
105
272
|
|
|
106
273
|
## Registry Publishing
|
|
107
274
|
|
|
@@ -110,16 +277,31 @@ When `lex-llm` routing and Legion transport are available, the provider publishe
|
|
|
110
277
|
- **Readiness events** on `readiness(live: true)` calls
|
|
111
278
|
- **Model availability events** on `list_models` discovery
|
|
112
279
|
|
|
113
|
-
|
|
280
|
+
All publishing is async (background threads) and never blocks the caller. Failures are logged via `handle_exception`.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Model Discovery & Offerings
|
|
285
|
+
|
|
286
|
+
On `list_models`, vLLM returns `max_model_len` which is mapped to `context_length`. This value is:
|
|
287
|
+
1. Attached to `Model::Info` objects
|
|
288
|
+
2. Cached via `cache_set` with 86400s TTL keyed by `model_detail_cache_key`
|
|
289
|
+
3. Available in routing offerings via `limits: { context_window: ctx }`
|
|
290
|
+
|
|
291
|
+
`discover_offerings(live: false)` serves from the cached model list without hitting the network.
|
|
292
|
+
|
|
293
|
+
---
|
|
114
294
|
|
|
115
295
|
## Development
|
|
116
296
|
|
|
117
297
|
```bash
|
|
118
298
|
bundle install
|
|
119
|
-
bundle exec rspec
|
|
299
|
+
bundle exec rspec
|
|
120
300
|
bundle exec rubocop -A
|
|
121
301
|
```
|
|
122
302
|
|
|
303
|
+
---
|
|
304
|
+
|
|
123
305
|
## License
|
|
124
306
|
|
|
125
307
|
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'legion/extensions/actors/every'
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
warn(e.message) if $VERBOSE
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
return unless defined?(Legion::Extensions::Actors::Every)
|
|
10
|
+
|
|
11
|
+
module Legion
|
|
12
|
+
module Extensions
|
|
13
|
+
module Llm
|
|
14
|
+
module Vllm
|
|
15
|
+
module Actor
|
|
16
|
+
# Periodic actor that refreshes the vLLM discovered model list.
|
|
17
|
+
class DiscoveryRefresh < Legion::Extensions::Actors::Every
|
|
18
|
+
include Legion::Logging::Helper
|
|
19
|
+
|
|
20
|
+
REFRESH_INTERVAL = 1800
|
|
21
|
+
|
|
22
|
+
def runner_class = self.class
|
|
23
|
+
def runner_function = 'manual'
|
|
24
|
+
def run_now? = true
|
|
25
|
+
def use_runner? = false
|
|
26
|
+
def check_subtask? = false
|
|
27
|
+
def generate_task? = false
|
|
28
|
+
|
|
29
|
+
def time
|
|
30
|
+
return REFRESH_INTERVAL unless defined?(Legion::Settings)
|
|
31
|
+
|
|
32
|
+
Legion::Settings.dig(:extensions, :llm, :vllm, :discovery_interval) || REFRESH_INTERVAL
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def manual
|
|
36
|
+
log.debug('[vllm][discovery_refresh] refreshing model list')
|
|
37
|
+
return unless defined?(Legion::LLM::Discovery)
|
|
38
|
+
|
|
39
|
+
Legion::LLM::Discovery.refresh_discovered_models!(provider: :vllm)
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
handle_exception(e, level: :warn, handled: true, operation: 'vllm.actor.discovery_refresh')
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -15,7 +15,7 @@ module Legion
|
|
|
15
15
|
extend Legion::Extensions::Llm::AutoRegistration
|
|
16
16
|
|
|
17
17
|
PROVIDER_FAMILY = :vllm
|
|
18
|
-
DEFAULT_INSTANCE_TIER = { tier: :direct }.freeze
|
|
18
|
+
DEFAULT_INSTANCE_TIER = { tier: :direct, capabilities: %i[completion streaming vision tools] }.freeze
|
|
19
19
|
|
|
20
20
|
def self.default_settings
|
|
21
21
|
::Legion::Extensions::Llm.provider_settings(
|
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.2.
|
|
4
|
+
version: 0.2.13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -97,6 +97,7 @@ files:
|
|
|
97
97
|
- README.md
|
|
98
98
|
- lex-llm-vllm.gemspec
|
|
99
99
|
- lib/legion/extensions/llm/vllm.rb
|
|
100
|
+
- lib/legion/extensions/llm/vllm/actors/discovery_refresh.rb
|
|
100
101
|
- lib/legion/extensions/llm/vllm/actors/fleet_worker.rb
|
|
101
102
|
- lib/legion/extensions/llm/vllm/provider.rb
|
|
102
103
|
- lib/legion/extensions/llm/vllm/runners/fleet_worker.rb
|