lex-llm-mlx 0.1.5 → 0.1.7
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 +12 -0
- data/README.md +42 -1
- data/lib/legion/extensions/llm/mlx/provider.rb +22 -0
- data/lib/legion/extensions/llm/mlx/registry_event_builder.rb +118 -0
- data/lib/legion/extensions/llm/mlx/registry_publisher.rb +97 -0
- data/lib/legion/extensions/llm/mlx/transport/exchanges/llm_registry.rb +24 -0
- data/lib/legion/extensions/llm/mlx/transport/messages/registry_event.rb +42 -0
- data/lib/legion/extensions/llm/mlx/version.rb +1 -1
- data/lib/legion/extensions/llm/mlx.rb +6 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b4c018fd3b44a629665783ce38fd5c0f5429ab41452b2e8829079a08c2807860
|
|
4
|
+
data.tar.gz: d75bf991619c7d3bf8bc4d7b0aaf77cdb11e6a352595e210ef36f2b6e094cecb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cee424e550ad1ee7cf3a19f791f3bf72434b170efbb50e3e952d5eb502943f442b14ba29344b26575657a390785ad4749ad8d95f7c34454f9d7852160584ff4a
|
|
7
|
+
data.tar.gz: 287b5609ee6788ce4865d1b4f1fb49e344198fe5ca1b828f806742810d82d29e1a18fa28c2404b9a8b0144878ea3d52154fb3e702286f02f2a836239f0d7f316
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7 - 2026-04-30
|
|
4
|
+
|
|
5
|
+
- Add `Legion::Logging::Helper` to `Mlx` module and `RegistryPublisher` for standardized logging.
|
|
6
|
+
- Replace all bare rescue blocks with `handle_exception` calls for full observability.
|
|
7
|
+
- Add info-level action logging for health checks, readiness, model discovery, and registry publishing.
|
|
8
|
+
- Remove custom `log_publish_failure` method in favor of `handle_exception`.
|
|
9
|
+
- Update README to document registry event publishing, transport layer, and architecture.
|
|
10
|
+
|
|
11
|
+
## 0.1.6 - 2026-04-28
|
|
12
|
+
|
|
13
|
+
- Publish best-effort `llm.registry` live readiness and discovered-model availability events using `lex-llm` registry envelopes when transport is already available.
|
|
14
|
+
|
|
3
15
|
## 0.1.5 - 2026-04-28
|
|
4
16
|
|
|
5
17
|
- Require current shared Legion JSON, logging, settings, and `lex-llm >= 0.1.5` runtime dependencies.
|
data/README.md
CHANGED
|
@@ -12,7 +12,21 @@ Load it with `require 'legion/extensions/llm/mlx'`.
|
|
|
12
12
|
- OpenAI-compatible chat, streaming, model listing, and embeddings endpoint wrappers.
|
|
13
13
|
- Heuristic chat, embedding, and vision capability mapping for discovered local models.
|
|
14
14
|
- Local-first defaults for MLX servers running on MacBook, Mac Studio, or local Apple Silicon hosts.
|
|
15
|
-
-
|
|
15
|
+
- Best-effort `llm.registry` event publishing for readiness and model availability when transport is available.
|
|
16
|
+
- Shared Legion settings, JSON, and logging dependencies with full `Legion::Logging::Helper` integration.
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Legion::Extensions::Llm::Mlx
|
|
22
|
+
Mlx (module) # Extension namespace, provider registration, default settings
|
|
23
|
+
Provider # MLX provider — health, readiness, model listing, OpenAI-compatible adapter
|
|
24
|
+
RegistryPublisher # Async publisher for llm.registry readiness/model availability events
|
|
25
|
+
RegistryEventBuilder # Builds sanitized lex-llm registry envelopes for MLX provider state
|
|
26
|
+
Transport::
|
|
27
|
+
Messages::RegistryEvent # AMQP message for llm.registry exchange
|
|
28
|
+
Exchanges::LlmRegistry # Topic exchange definition for llm.registry
|
|
29
|
+
```
|
|
16
30
|
|
|
17
31
|
## Default Settings
|
|
18
32
|
|
|
@@ -41,3 +55,30 @@ end
|
|
|
41
55
|
- `health_url`: `/health`
|
|
42
56
|
|
|
43
57
|
The provider uses the shared `Legion::Extensions::Llm::Provider::OpenAICompatible` adapter so Legion routing can treat MLX, vLLM, OpenAI, and other compatible servers consistently while preserving provider-specific settings and health behavior.
|
|
58
|
+
|
|
59
|
+
## Registry Event Publishing
|
|
60
|
+
|
|
61
|
+
When `Legion::Transport` and `lex-llm` routing are available, the provider publishes best-effort events to the `llm.registry` topic exchange:
|
|
62
|
+
|
|
63
|
+
- **Readiness events** — published asynchronously when `readiness(live: true)` is called.
|
|
64
|
+
- **Model availability events** — published asynchronously after `list_models` discovers models.
|
|
65
|
+
|
|
66
|
+
Publishing is fire-and-forget in background threads; failures never block the provider.
|
|
67
|
+
|
|
68
|
+
## Dependencies
|
|
69
|
+
|
|
70
|
+
| Gem | Required | Purpose |
|
|
71
|
+
|-----|----------|---------|
|
|
72
|
+
| `legion-json` (>= 1.2.1) | Yes | JSON serialization |
|
|
73
|
+
| `legion-logging` (>= 1.3.2) | Yes | Structured logging via Helper |
|
|
74
|
+
| `legion-settings` (>= 1.3.14) | Yes | Configuration |
|
|
75
|
+
| `lex-llm` (>= 0.1.5) | Yes | Shared provider base, routing, fleet |
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bundle install
|
|
81
|
+
bundle exec rspec # 0 failures
|
|
82
|
+
bundle exec rubocop -A # auto-fix
|
|
83
|
+
bundle exec rubocop # lint check
|
|
84
|
+
```
|
|
@@ -11,11 +11,17 @@ module Legion
|
|
|
11
11
|
include Legion::Extensions::Llm::Provider::OpenAICompatible
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
|
+
attr_writer :registry_publisher
|
|
15
|
+
|
|
14
16
|
def slug = 'mlx'
|
|
15
17
|
def local? = true
|
|
16
18
|
def configuration_options = %i[mlx_api_base mlx_api_key]
|
|
17
19
|
def configuration_requirements = []
|
|
18
20
|
def capabilities = Capabilities
|
|
21
|
+
|
|
22
|
+
def registry_publisher
|
|
23
|
+
@registry_publisher ||= RegistryPublisher.new
|
|
24
|
+
end
|
|
19
25
|
end
|
|
20
26
|
|
|
21
27
|
# Conservative capability predicates for local MLX OpenAI-compatible servers.
|
|
@@ -56,8 +62,24 @@ module Legion
|
|
|
56
62
|
def health_url = '/health'
|
|
57
63
|
|
|
58
64
|
def health
|
|
65
|
+
log.info("Checking MLX health at #{api_base}#{health_url}")
|
|
59
66
|
connection.get(health_url).body
|
|
60
67
|
end
|
|
68
|
+
|
|
69
|
+
def readiness(live: false)
|
|
70
|
+
log.info("Checking MLX readiness (live=#{live})")
|
|
71
|
+
super.tap do |metadata|
|
|
72
|
+
self.class.registry_publisher.publish_readiness_async(metadata) if live
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def list_models
|
|
77
|
+
log.info('Listing available MLX models')
|
|
78
|
+
super.tap do |models|
|
|
79
|
+
log.info("Discovered #{Array(models).size} MLX models")
|
|
80
|
+
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
61
83
|
end
|
|
62
84
|
end
|
|
63
85
|
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Mlx
|
|
7
|
+
# Builds sanitized lex-llm registry envelopes for MLX provider state.
|
|
8
|
+
class RegistryEventBuilder
|
|
9
|
+
def readiness(readiness)
|
|
10
|
+
registry_event_class.public_send(
|
|
11
|
+
readiness[:ready] ? :available : :unavailable,
|
|
12
|
+
provider_offering(readiness),
|
|
13
|
+
runtime: runtime_metadata,
|
|
14
|
+
health: readiness_health(readiness),
|
|
15
|
+
metadata: readiness_metadata(readiness)
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def model_available(model, readiness:)
|
|
20
|
+
registry_event_class.available(
|
|
21
|
+
model_offering(model),
|
|
22
|
+
runtime: runtime_metadata,
|
|
23
|
+
health: model_health(readiness),
|
|
24
|
+
metadata: model_metadata(model)
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def provider_offering(readiness)
|
|
31
|
+
{
|
|
32
|
+
provider_family: :mlx,
|
|
33
|
+
provider_instance: provider_instance,
|
|
34
|
+
transport: :http,
|
|
35
|
+
model: 'provider-readiness',
|
|
36
|
+
usage_type: :inference,
|
|
37
|
+
capabilities: [],
|
|
38
|
+
health: readiness_health(readiness),
|
|
39
|
+
metadata: { lex: :llm_mlx, provider_readiness: true }
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def model_offering(model)
|
|
44
|
+
{
|
|
45
|
+
provider_family: :mlx,
|
|
46
|
+
provider_instance: provider_instance,
|
|
47
|
+
transport: :http,
|
|
48
|
+
model: model.id,
|
|
49
|
+
usage_type: usage_type_for(model),
|
|
50
|
+
capabilities: Array(model.capabilities).map(&:to_sym),
|
|
51
|
+
limits: model_limits(model),
|
|
52
|
+
metadata: { lex: :llm_mlx, model_name: model.name }.compact
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def readiness_health(readiness)
|
|
57
|
+
health = {
|
|
58
|
+
ready: readiness[:ready] == true,
|
|
59
|
+
status: readiness[:ready] ? :available : :unavailable,
|
|
60
|
+
checked: readiness.dig(:health, :checked) != false
|
|
61
|
+
}
|
|
62
|
+
add_readiness_error(health, readiness[:health])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_readiness_error(health, source)
|
|
66
|
+
error = source.is_a?(Hash) ? source : {}
|
|
67
|
+
error_class = error[:error] || error['error']
|
|
68
|
+
error_message = error[:message] || error['message']
|
|
69
|
+
health[:error_class] = error_class if error_class
|
|
70
|
+
health[:error] = error_message if error_message
|
|
71
|
+
health
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def model_health(readiness)
|
|
75
|
+
ready = readiness.fetch(:ready, true) == true
|
|
76
|
+
{ ready:, status: ready ? :available : :degraded }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def readiness_metadata(readiness)
|
|
80
|
+
{
|
|
81
|
+
extension: :lex_llm_mlx,
|
|
82
|
+
provider: :mlx,
|
|
83
|
+
configured: readiness[:configured] == true,
|
|
84
|
+
live: readiness[:live] == true
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def model_metadata(model)
|
|
89
|
+
{ extension: :lex_llm_mlx, provider: :mlx, model_type: model.type }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def runtime_metadata
|
|
93
|
+
{ node: provider_instance }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def model_limits(model)
|
|
97
|
+
{
|
|
98
|
+
context_window: model.context_window,
|
|
99
|
+
max_output_tokens: model.max_output_tokens
|
|
100
|
+
}.compact
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def usage_type_for(model)
|
|
104
|
+
model.type == 'embedding' ? :embedding : :inference
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def provider_instance
|
|
108
|
+
:mlx
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def registry_event_class
|
|
112
|
+
::Legion::Extensions::Llm::Routing::RegistryEvent
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Mlx
|
|
7
|
+
# Best-effort publisher for MLX provider availability events.
|
|
8
|
+
class RegistryPublisher
|
|
9
|
+
include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
10
|
+
|
|
11
|
+
APP_ID = 'lex-llm-mlx'
|
|
12
|
+
|
|
13
|
+
def initialize(builder: RegistryEventBuilder.new)
|
|
14
|
+
@builder = builder
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def publish_readiness_async(readiness)
|
|
18
|
+
log.info('Publishing MLX readiness event asynchronously')
|
|
19
|
+
schedule { publish_event(@builder.readiness(readiness)) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def publish_models_async(models, readiness:)
|
|
23
|
+
log.info("Publishing #{Array(models).size} MLX model availability events asynchronously")
|
|
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: 'mlx.registry_publisher.schedule_thread')
|
|
41
|
+
end
|
|
42
|
+
rescue StandardError => e
|
|
43
|
+
handle_exception(e, level: :debug, handled: true, operation: 'mlx.registry_publisher.schedule')
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def publish_event(event)
|
|
48
|
+
return false unless publishing_available?
|
|
49
|
+
|
|
50
|
+
log.info("Publishing MLX registry event: #{event.class}")
|
|
51
|
+
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
52
|
+
rescue StandardError => e
|
|
53
|
+
handle_exception(e, level: :warn, handled: true, operation: 'mlx.registry_publisher.publish_event')
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def publishing_available?
|
|
58
|
+
return false unless registry_event_available?
|
|
59
|
+
return false unless transport_message_available?
|
|
60
|
+
return true unless defined?(::Legion::Transport::Connection)
|
|
61
|
+
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
62
|
+
|
|
63
|
+
::Legion::Transport::Connection.session_open?
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
handle_exception(e, level: :debug, handled: true,
|
|
66
|
+
operation: 'mlx.registry_publisher.publishing_available?')
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def registry_event_available?
|
|
71
|
+
defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def transport_message_available?
|
|
75
|
+
return true if message_class_defined?
|
|
76
|
+
return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
|
|
77
|
+
|
|
78
|
+
require 'legion/extensions/llm/mlx/transport/messages/registry_event'
|
|
79
|
+
message_class_defined?
|
|
80
|
+
rescue LoadError => e
|
|
81
|
+
handle_exception(e, level: :debug, handled: true,
|
|
82
|
+
operation: 'mlx.registry_publisher.transport_message_available?')
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def message_class_defined?
|
|
87
|
+
defined?(::Legion::Extensions::Llm::Mlx::Transport::Messages::RegistryEvent)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def message_class
|
|
91
|
+
::Legion::Extensions::Llm::Mlx::Transport::Messages::RegistryEvent
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Llm
|
|
6
|
+
module Mlx
|
|
7
|
+
module Transport
|
|
8
|
+
module Exchanges
|
|
9
|
+
# Topic exchange for MLX 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
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/llm/mlx/transport/exchanges/llm_registry'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Extensions
|
|
7
|
+
module Llm
|
|
8
|
+
module Mlx
|
|
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
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
4
|
require 'legion/extensions/llm/mlx/provider'
|
|
5
|
+
require 'legion/extensions/llm/mlx/registry_event_builder'
|
|
6
|
+
require 'legion/extensions/llm/mlx/registry_publisher'
|
|
5
7
|
require 'legion/extensions/llm/mlx/version'
|
|
6
8
|
|
|
7
9
|
module Legion
|
|
@@ -9,6 +11,7 @@ module Legion
|
|
|
9
11
|
module Llm
|
|
10
12
|
# Mlx provider extension namespace.
|
|
11
13
|
module Mlx
|
|
14
|
+
extend Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
12
15
|
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
13
16
|
|
|
14
17
|
PROVIDER_FAMILY = :mlx
|
|
@@ -36,3 +39,6 @@ end
|
|
|
36
39
|
|
|
37
40
|
Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::Mlx::PROVIDER_FAMILY,
|
|
38
41
|
Legion::Extensions::Llm::Mlx::Provider)
|
|
42
|
+
if Legion::Extensions::Llm::Mlx.respond_to?(:log)
|
|
43
|
+
Legion::Extensions::Llm::Mlx.log.info('Registered MLX provider as :mlx')
|
|
44
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-llm-mlx
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -84,6 +84,10 @@ files:
|
|
|
84
84
|
- lex-llm-mlx.gemspec
|
|
85
85
|
- lib/legion/extensions/llm/mlx.rb
|
|
86
86
|
- lib/legion/extensions/llm/mlx/provider.rb
|
|
87
|
+
- lib/legion/extensions/llm/mlx/registry_event_builder.rb
|
|
88
|
+
- lib/legion/extensions/llm/mlx/registry_publisher.rb
|
|
89
|
+
- lib/legion/extensions/llm/mlx/transport/exchanges/llm_registry.rb
|
|
90
|
+
- lib/legion/extensions/llm/mlx/transport/messages/registry_event.rb
|
|
87
91
|
- lib/legion/extensions/llm/mlx/version.rb
|
|
88
92
|
homepage: https://github.com/LegionIO/lex-llm-mlx
|
|
89
93
|
licenses:
|