lex-llm-azure-foundry 0.1.4 → 0.1.6
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 +17 -0
- data/README.md +45 -5
- data/lex-llm-azure-foundry.gemspec +1 -1
- data/lib/legion/extensions/llm/azure_foundry/provider.rb +25 -12
- data/lib/legion/extensions/llm/azure_foundry/version.rb +1 -1
- data/lib/legion/extensions/llm/azure_foundry.rb +22 -24
- metadata +3 -7
- data/lib/legion/extensions/llm/azure_foundry/registry_event_builder.rb +0 -121
- data/lib/legion/extensions/llm/azure_foundry/registry_publisher.rb +0 -100
- data/lib/legion/extensions/llm/azure_foundry/transport/exchanges/llm_registry.rb +0 -24
- data/lib/legion/extensions/llm/azure_foundry/transport/messages/registry_event.rb +0 -42
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 279b43dc868c44a7b0f39f737cfff512ee6f5383097b886c5ceb77da7cbb6367
|
|
4
|
+
data.tar.gz: 8c6d1c95608344743ae5ebbf181cd9eb016dc389ddb88efb9a338bf7590112a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98254832116e7e04fc487d0477a791f0aac57fa2123035b87d8ca73d6e88c4691706f0ee23c95fdd8833db77898646ec21378d4eaf6faa799b46840d695732a4
|
|
7
|
+
data.tar.gz: fc8551e98c53413ad263b0e78903a24c2d1e2ee49822bba32a5a2e183d5a205af5e1fc00af80cd8f1b6501b02638f5a22286741ea800caf2267ad200438040f7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.6] - 2026-05-01
|
|
4
|
+
|
|
5
|
+
- Require lex-llm >= 0.1.9 for the shared base contract
|
|
6
|
+
- Replace local RegistryPublisher and RegistryEventBuilder with base classes from lex-llm
|
|
7
|
+
- Delete local transport/ directory (exchange and message classes now live in lex-llm)
|
|
8
|
+
- Remove deprecated Provider.register call; use Configuration.register_provider_options directly
|
|
9
|
+
- Simplify default_settings to flat provider hash (no provider_settings builder)
|
|
10
|
+
- Fix Model::Info construction to use modalities_input/modalities_output keywords
|
|
11
|
+
|
|
12
|
+
## [0.1.5] - 2026-04-30
|
|
13
|
+
|
|
14
|
+
- Audit all rescue blocks for handle_exception compliance across Provider, RegistryPublisher, and RegistryEventBuilder
|
|
15
|
+
- Add Legion::Logging::Helper to AzureFoundry module, RegistryPublisher, and RegistryEventBuilder
|
|
16
|
+
- Add info-level action logging for discover_offerings, health, readiness, list_models, chat, stream, embed, and registry publish
|
|
17
|
+
- Remove custom log_publish_failure in favour of standard handle_exception
|
|
18
|
+
- Update README to reflect current architecture, file map, and observability
|
|
19
|
+
|
|
3
20
|
## [0.1.4] - 2026-04-30
|
|
4
21
|
|
|
5
22
|
- Enable stream_usage_supported? for streaming token usage reporting
|
data/README.md
CHANGED
|
@@ -13,11 +13,49 @@ Load it with `require 'legion/extensions/llm/azure_foundry'`.
|
|
|
13
13
|
- Azure AI Foundry model inference embeddings through `POST /models/embeddings?api-version=...`
|
|
14
14
|
- Azure AI Foundry model info health check through `GET /models/info?api-version=...` when `live: true`
|
|
15
15
|
- Azure OpenAI v1-compatible endpoint support through `/openai/v1/chat/completions` and `/openai/v1/embeddings`
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
16
|
+
- Deployment-name-preserving routing offerings for hosted Azure deployments
|
|
17
|
+
- Explicit `model_family` and `canonical_model_alias` metadata for deployments whose base model cannot be proven from Azure metadata
|
|
18
|
+
- Offline-first discovery from configured deployments
|
|
19
|
+
- Shared OpenAI-compatible request and response mapping via `Legion::Extensions::Llm::Provider::OpenAICompatible`
|
|
20
|
+
- Conservative token-counting metadata when no portable Azure token-counting REST endpoint is configured
|
|
21
|
+
- Best-effort `llm.registry` event publishing for readiness and model availability via AMQP when transport is available
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Legion::Extensions::Llm::AzureFoundry
|
|
27
|
+
├── Provider # Azure AI Foundry and Azure OpenAI hosted provider surface
|
|
28
|
+
│ └── Capabilities # Capability predicates inferred from deployment metadata and model naming
|
|
29
|
+
├── RegistryPublisher # Best-effort async publisher for llm.registry availability events
|
|
30
|
+
├── RegistryEventBuilder # Builds sanitized lex-llm registry envelopes for provider state
|
|
31
|
+
├── Transport/
|
|
32
|
+
│ ├── Messages::RegistryEvent # AMQP message for llm.registry events
|
|
33
|
+
│ └── Exchanges::LlmRegistry # Topic exchange for provider availability events
|
|
34
|
+
└── VERSION
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## File Map
|
|
38
|
+
|
|
39
|
+
| Path | Purpose |
|
|
40
|
+
|------|---------|
|
|
41
|
+
| `lib/legion/extensions/llm/azure_foundry.rb` | Entry point, provider registration, default settings |
|
|
42
|
+
| `lib/legion/extensions/llm/azure_foundry/provider.rb` | Provider implementation with chat, stream, embed, health, readiness, discovery |
|
|
43
|
+
| `lib/legion/extensions/llm/azure_foundry/registry_publisher.rb` | Async registry event publishing with transport guards |
|
|
44
|
+
| `lib/legion/extensions/llm/azure_foundry/registry_event_builder.rb` | Sanitized registry envelope construction |
|
|
45
|
+
| `lib/legion/extensions/llm/azure_foundry/transport/messages/registry_event.rb` | AMQP message class for registry events |
|
|
46
|
+
| `lib/legion/extensions/llm/azure_foundry/transport/exchanges/llm_registry.rb` | Topic exchange definition for llm.registry |
|
|
47
|
+
| `lib/legion/extensions/llm/azure_foundry/version.rb` | `VERSION` constant |
|
|
48
|
+
|
|
49
|
+
## Observability
|
|
50
|
+
|
|
51
|
+
Every class and module uses `Legion::Logging::Helper`:
|
|
52
|
+
|
|
53
|
+
- **AzureFoundry** module: `extend Legion::Logging::Helper`
|
|
54
|
+
- **Provider**: inherits `include Legion::Logging::Helper` from `Legion::Extensions::Llm::Provider`
|
|
55
|
+
- **RegistryPublisher**: `include Legion::Logging::Helper`
|
|
56
|
+
- **RegistryEventBuilder**: `include Legion::Logging::Helper`
|
|
57
|
+
|
|
58
|
+
All rescue blocks call `handle_exception(e, level:, handled:, operation:)` for structured exception reporting. Key actions emit info-level log lines including discover_offerings, health checks, readiness, model listing, chat, stream, embed, and registry publish operations.
|
|
21
59
|
|
|
22
60
|
## API Contract
|
|
23
61
|
|
|
@@ -97,6 +135,8 @@ provider = Legion::Extensions::Llm::AzureFoundry.provider_class.new(Legion::Exte
|
|
|
97
135
|
provider.discover_offerings(live: false)
|
|
98
136
|
provider.offering_for(model: "gpt-4o-prod", model_family: :openai, canonical_model_alias: "gpt-4o")
|
|
99
137
|
provider.health(live: false)
|
|
138
|
+
provider.readiness(live: false)
|
|
139
|
+
provider.list_models
|
|
100
140
|
provider.chat(messages, model: "gpt-4o-prod")
|
|
101
141
|
provider.stream(messages, model: "gpt-4o-prod") { |chunk| puts chunk.content }
|
|
102
142
|
provider.embed(["hello"], model: "embedding-prod")
|
|
@@ -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.
|
|
29
|
+
spec.add_dependency 'lex-llm', '>= 0.2.0'
|
|
30
30
|
end
|
|
@@ -17,8 +17,6 @@ module Legion
|
|
|
17
17
|
OPENAI_V1_SURFACE = :openai_v1
|
|
18
18
|
|
|
19
19
|
class << self
|
|
20
|
-
attr_writer :registry_publisher
|
|
21
|
-
|
|
22
20
|
def slug = 'azure_foundry'
|
|
23
21
|
def configuration_requirements = %i[azure_foundry_endpoint]
|
|
24
22
|
|
|
@@ -36,7 +34,7 @@ module Legion
|
|
|
36
34
|
def capabilities = Capabilities
|
|
37
35
|
|
|
38
36
|
def registry_publisher
|
|
39
|
-
|
|
37
|
+
AzureFoundry.registry_publisher
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def resolve_model_id(model_id, config: nil)
|
|
@@ -144,12 +142,14 @@ module Legion
|
|
|
144
142
|
def health_url = models_url
|
|
145
143
|
|
|
146
144
|
def discover_offerings(live: false, **filters)
|
|
145
|
+
log.info { "discovering offerings live=#{live} from #{api_base}" }
|
|
147
146
|
offerings = configured_deployments.filter_map { |deployment| offering_from_config(deployment) }
|
|
148
147
|
return filter_offerings(offerings, **filters) unless live
|
|
149
148
|
|
|
150
149
|
filter_offerings(offerings, **filters).map do |offering|
|
|
151
150
|
with_live_metadata(offering)
|
|
152
151
|
rescue StandardError => e
|
|
152
|
+
handle_exception(e, level: :warn, handled: true, operation: 'azure_foundry.discover_offerings')
|
|
153
153
|
with_health(offering, ready: false, checked: true, error: e)
|
|
154
154
|
end
|
|
155
155
|
end
|
|
@@ -172,43 +172,43 @@ module Legion
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
def health(live: false)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
configured: configured?,
|
|
178
|
-
ready: configured?,
|
|
179
|
-
live: live,
|
|
180
|
-
api_base: api_base,
|
|
181
|
-
surface: surface
|
|
182
|
-
}
|
|
175
|
+
log.info { "checking health live=#{live} at #{api_base}" }
|
|
176
|
+
baseline = health_baseline(live)
|
|
183
177
|
return baseline.merge(checked: false) unless live
|
|
184
178
|
|
|
185
179
|
response = connection.get(health_url)
|
|
186
180
|
baseline.merge(checked: true, model_info: response.body)
|
|
187
181
|
rescue StandardError => e
|
|
182
|
+
handle_exception(e, level: :warn, handled: true, operation: 'azure_foundry.health')
|
|
188
183
|
baseline.merge(checked: true, ready: false, error: e.class.name, message: e.message)
|
|
189
184
|
end
|
|
190
185
|
|
|
191
186
|
def readiness(live: false)
|
|
187
|
+
log.info { "checking readiness live=#{live} at #{api_base}" }
|
|
192
188
|
health(live: live).merge(local: false, remote: true, endpoints: endpoint_manifest).tap do |metadata|
|
|
193
189
|
self.class.registry_publisher.publish_readiness_async(metadata) if live
|
|
194
190
|
end
|
|
195
191
|
end
|
|
196
192
|
|
|
197
193
|
def list_models
|
|
194
|
+
log.info { "listing configured deployment models from #{api_base}" }
|
|
198
195
|
models = discover_offerings(live: false).map { |offering| model_info_from_offering(offering) }
|
|
199
196
|
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
200
197
|
models
|
|
201
198
|
end
|
|
202
199
|
|
|
203
200
|
def chat(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}) # rubocop:disable Metrics/ParameterLists
|
|
201
|
+
log.info { "chat request model=#{model} messages=#{messages.size}" }
|
|
204
202
|
complete(messages, tools:, temperature:, model: model_info(model, max_tokens:), params:, tool_prefs:)
|
|
205
203
|
end
|
|
206
204
|
|
|
207
205
|
def stream(messages, model:, temperature: nil, max_tokens: nil, tools: {}, tool_prefs: nil, params: {}, &) # rubocop:disable Metrics/ParameterLists
|
|
206
|
+
log.info { "stream request model=#{model} messages=#{messages.size}" }
|
|
208
207
|
complete(messages, tools:, temperature:, model: model_info(model, max_tokens:), params:, tool_prefs:, &)
|
|
209
208
|
end
|
|
210
209
|
|
|
211
210
|
def embed(text, model:, dimensions: nil, input_type: nil)
|
|
211
|
+
log.info { "embed request model=#{model}" }
|
|
212
212
|
payload = render_embedding_payload(text, model: model_id(model), dimensions:)
|
|
213
213
|
payload[:input_type] = input_type if input_type
|
|
214
214
|
response = connection.post(embedding_url(model:), payload)
|
|
@@ -231,15 +231,28 @@ module Legion
|
|
|
231
231
|
(config.azure_foundry_surface || MODEL_INFERENCE_SURFACE).to_sym
|
|
232
232
|
end
|
|
233
233
|
|
|
234
|
+
def health_baseline(live)
|
|
235
|
+
{
|
|
236
|
+
provider: :azure_foundry,
|
|
237
|
+
configured: configured?,
|
|
238
|
+
ready: configured?,
|
|
239
|
+
live: live,
|
|
240
|
+
api_base: api_base,
|
|
241
|
+
surface: surface
|
|
242
|
+
}
|
|
243
|
+
end
|
|
244
|
+
|
|
234
245
|
def model_info_from_offering(offering)
|
|
235
246
|
capabilities = offering.capabilities.map(&:to_s)
|
|
247
|
+
modalities = modalities_for_capabilities(capabilities)
|
|
236
248
|
Legion::Extensions::Llm::Model::Info.new(
|
|
237
249
|
id: offering.model,
|
|
238
250
|
name: offering.metadata[:canonical_model_alias] || offering.model,
|
|
239
251
|
provider: :azure_foundry,
|
|
240
252
|
family: offering.metadata[:model_family],
|
|
241
253
|
capabilities: capabilities,
|
|
242
|
-
modalities:
|
|
254
|
+
modalities_input: modalities[:input],
|
|
255
|
+
modalities_output: modalities[:output],
|
|
243
256
|
metadata: offering.to_h
|
|
244
257
|
)
|
|
245
258
|
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/llm'
|
|
4
4
|
require 'legion/extensions/llm/azure_foundry/provider'
|
|
5
|
-
require 'legion/extensions/llm/azure_foundry/registry_event_builder'
|
|
6
|
-
require 'legion/extensions/llm/azure_foundry/registry_publisher'
|
|
7
5
|
require 'legion/extensions/llm/azure_foundry/version'
|
|
8
6
|
|
|
9
7
|
module Legion
|
|
@@ -12,38 +10,38 @@ module Legion
|
|
|
12
10
|
# Azure AI Foundry provider extension namespace.
|
|
13
11
|
module AzureFoundry
|
|
14
12
|
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
13
|
+
extend Legion::Logging::Helper
|
|
15
14
|
|
|
16
15
|
PROVIDER_FAMILY = :azure_foundry
|
|
17
16
|
|
|
18
17
|
def self.default_settings
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
usage: { inference: true, embedding: true, token_counting: false },
|
|
35
|
-
limits: { concurrency: 4 }
|
|
36
|
-
}
|
|
37
|
-
)
|
|
18
|
+
{
|
|
19
|
+
enabled: false,
|
|
20
|
+
default_model: nil,
|
|
21
|
+
endpoint: nil,
|
|
22
|
+
api_key: nil,
|
|
23
|
+
bearer_token: nil,
|
|
24
|
+
api_version: '2024-05-01-preview',
|
|
25
|
+
surface: nil,
|
|
26
|
+
deployments: [],
|
|
27
|
+
model_whitelist: [],
|
|
28
|
+
model_blacklist: [],
|
|
29
|
+
model_cache_ttl: 3600,
|
|
30
|
+
tls: { enabled: false, verify: :peer },
|
|
31
|
+
instances: {}
|
|
32
|
+
}
|
|
38
33
|
end
|
|
39
34
|
|
|
40
35
|
def self.provider_class
|
|
41
36
|
Provider
|
|
42
37
|
end
|
|
38
|
+
|
|
39
|
+
def self.registry_publisher
|
|
40
|
+
@registry_publisher ||= Legion::Extensions::Llm::RegistryPublisher.new(provider_family: PROVIDER_FAMILY)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
|
-
|
|
48
|
-
Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::AzureFoundry::PROVIDER_FAMILY,
|
|
49
|
-
Legion::Extensions::Llm::AzureFoundry::Provider)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-llm-azure-foundry
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
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.
|
|
60
|
+
version: 0.2.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.
|
|
67
|
+
version: 0.2.0
|
|
68
68
|
description: Azure AI Foundry and Azure OpenAI hosted provider integration for LegionIO
|
|
69
69
|
LLM routing.
|
|
70
70
|
email:
|
|
@@ -85,10 +85,6 @@ files:
|
|
|
85
85
|
- lex-llm-azure-foundry.gemspec
|
|
86
86
|
- lib/legion/extensions/llm/azure_foundry.rb
|
|
87
87
|
- lib/legion/extensions/llm/azure_foundry/provider.rb
|
|
88
|
-
- lib/legion/extensions/llm/azure_foundry/registry_event_builder.rb
|
|
89
|
-
- lib/legion/extensions/llm/azure_foundry/registry_publisher.rb
|
|
90
|
-
- lib/legion/extensions/llm/azure_foundry/transport/exchanges/llm_registry.rb
|
|
91
|
-
- lib/legion/extensions/llm/azure_foundry/transport/messages/registry_event.rb
|
|
92
88
|
- lib/legion/extensions/llm/azure_foundry/version.rb
|
|
93
89
|
homepage: https://github.com/LegionIO/lex-llm-azure-foundry
|
|
94
90
|
licenses:
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Legion
|
|
4
|
-
module Extensions
|
|
5
|
-
module Llm
|
|
6
|
-
module AzureFoundry
|
|
7
|
-
# Builds sanitized lex-llm registry envelopes for Azure Foundry 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: :azure_foundry,
|
|
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_azure_foundry, provider_readiness: true }
|
|
40
|
-
}
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def model_offering(model)
|
|
44
|
-
metadata = model.metadata if model.respond_to?(:metadata)
|
|
45
|
-
return metadata if metadata.is_a?(Hash) && metadata[:provider_family]
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
provider_family: :azure_foundry,
|
|
49
|
-
provider_instance: provider_instance,
|
|
50
|
-
transport: :http,
|
|
51
|
-
model: model.id,
|
|
52
|
-
usage_type: usage_type_for(model),
|
|
53
|
-
capabilities: Array(model.capabilities).map(&:to_sym),
|
|
54
|
-
limits: model_limits(model),
|
|
55
|
-
metadata: { lex: :llm_azure_foundry, model_name: model.name }.compact
|
|
56
|
-
}
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def readiness_health(readiness)
|
|
60
|
-
health = {
|
|
61
|
-
ready: readiness[:ready] == true,
|
|
62
|
-
status: readiness[:ready] ? :available : :unavailable,
|
|
63
|
-
checked: readiness.dig(:health, :checked) != false
|
|
64
|
-
}
|
|
65
|
-
add_readiness_error(health, readiness[:health])
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def add_readiness_error(health, source)
|
|
69
|
-
error = source.is_a?(Hash) ? source : {}
|
|
70
|
-
error_class = error[:error] || error['error']
|
|
71
|
-
error_message = error[:message] || error['message']
|
|
72
|
-
health[:error_class] = error_class if error_class
|
|
73
|
-
health[:error] = error_message if error_message
|
|
74
|
-
health
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def model_health(readiness)
|
|
78
|
-
ready = readiness.fetch(:ready, true) == true
|
|
79
|
-
{ ready:, status: ready ? :available : :degraded }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def readiness_metadata(readiness)
|
|
83
|
-
{
|
|
84
|
-
extension: :lex_llm_azure_foundry,
|
|
85
|
-
provider: :azure_foundry,
|
|
86
|
-
configured: readiness[:configured] == true,
|
|
87
|
-
live: readiness[:live] == true
|
|
88
|
-
}
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def model_metadata(model)
|
|
92
|
-
{ extension: :lex_llm_azure_foundry, provider: :azure_foundry, model_type: model.type }
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def runtime_metadata
|
|
96
|
-
{ node: provider_instance }
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def model_limits(model)
|
|
100
|
-
{
|
|
101
|
-
context_window: model.context_window,
|
|
102
|
-
max_output_tokens: model.max_output_tokens
|
|
103
|
-
}.compact
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def usage_type_for(model)
|
|
107
|
-
model.type == 'embedding' ? :embedding : :inference
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def provider_instance
|
|
111
|
-
:azure_foundry
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def registry_event_class
|
|
115
|
-
::Legion::Extensions::Llm::Routing::RegistryEvent
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Legion
|
|
4
|
-
module Extensions
|
|
5
|
-
module Llm
|
|
6
|
-
module AzureFoundry
|
|
7
|
-
# Best-effort publisher for Azure Foundry provider availability events.
|
|
8
|
-
class RegistryPublisher
|
|
9
|
-
APP_ID = 'lex-llm-azure-foundry'
|
|
10
|
-
|
|
11
|
-
def initialize(builder: RegistryEventBuilder.new)
|
|
12
|
-
@builder = builder
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def publish_readiness_async(readiness)
|
|
16
|
-
schedule { publish_event(@builder.readiness(readiness)) }
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def publish_models_async(models, readiness:)
|
|
20
|
-
schedule do
|
|
21
|
-
Array(models).each do |model|
|
|
22
|
-
publish_event(@builder.model_available(model, readiness:))
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def schedule(&)
|
|
30
|
-
return false unless publishing_available?
|
|
31
|
-
|
|
32
|
-
Thread.new do
|
|
33
|
-
Thread.current.abort_on_exception = false
|
|
34
|
-
yield
|
|
35
|
-
rescue StandardError => e
|
|
36
|
-
log_publish_failure(e, level: :debug)
|
|
37
|
-
end
|
|
38
|
-
rescue StandardError => e
|
|
39
|
-
log_publish_failure(e, level: :debug)
|
|
40
|
-
false
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def publish_event(event)
|
|
44
|
-
return false unless publishing_available?
|
|
45
|
-
|
|
46
|
-
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
47
|
-
rescue StandardError => e
|
|
48
|
-
log_publish_failure(e)
|
|
49
|
-
false
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def publishing_available?
|
|
53
|
-
return false unless registry_event_available?
|
|
54
|
-
return false unless transport_message_available?
|
|
55
|
-
return true unless defined?(::Legion::Transport::Connection)
|
|
56
|
-
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
57
|
-
|
|
58
|
-
::Legion::Transport::Connection.session_open?
|
|
59
|
-
rescue StandardError
|
|
60
|
-
false
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def registry_event_available?
|
|
64
|
-
defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def transport_message_available?
|
|
68
|
-
return true if message_class_defined?
|
|
69
|
-
return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
|
|
70
|
-
|
|
71
|
-
require 'legion/extensions/llm/azure_foundry/transport/messages/registry_event'
|
|
72
|
-
message_class_defined?
|
|
73
|
-
rescue LoadError
|
|
74
|
-
false
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def message_class_defined?
|
|
78
|
-
defined?(::Legion::Extensions::Llm::AzureFoundry::Transport::Messages::RegistryEvent)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def message_class
|
|
82
|
-
::Legion::Extensions::Llm::AzureFoundry::Transport::Messages::RegistryEvent
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def log_publish_failure(error, level: :warn)
|
|
86
|
-
message = "[lex-llm-azure-foundry] llm.registry publish failed: #{error.class}: #{error.message}"
|
|
87
|
-
logger = ::Legion::Extensions::Llm.logger if defined?(::Legion::Extensions::Llm)
|
|
88
|
-
if logger.respond_to?(level)
|
|
89
|
-
logger.public_send(level, message)
|
|
90
|
-
elsif logger.respond_to?(:debug)
|
|
91
|
-
logger.debug(message)
|
|
92
|
-
end
|
|
93
|
-
rescue StandardError
|
|
94
|
-
nil
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Legion
|
|
4
|
-
module Extensions
|
|
5
|
-
module Llm
|
|
6
|
-
module AzureFoundry
|
|
7
|
-
module Transport
|
|
8
|
-
module Exchanges
|
|
9
|
-
# Topic exchange for Azure Foundry 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/azure_foundry/transport/exchanges/llm_registry'
|
|
4
|
-
|
|
5
|
-
module Legion
|
|
6
|
-
module Extensions
|
|
7
|
-
module Llm
|
|
8
|
-
module AzureFoundry
|
|
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
|