lex-llm-openai 0.1.7 → 0.1.8
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 +7 -0
- data/README.md +61 -13
- data/lib/legion/extensions/llm/openai/provider.rb +16 -0
- data/lib/legion/extensions/llm/openai/registry_event_builder.rb +7 -1
- data/lib/legion/extensions/llm/openai/registry_publisher.rb +19 -17
- data/lib/legion/extensions/llm/openai/version.rb +1 -1
- data/lib/legion/extensions/llm/openai.rb +4 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 923d77b5cf761e20eccb817d66dd36bec5310842f1216c2b403432bf84bf11fa
|
|
4
|
+
data.tar.gz: e84b863710db389adf98f1b06783c46e4e4b7797c51889026c62939a3e3ad9a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3ea2f8c543f3e8cbdf55a158bb0033d1711b34b3114ea8ebbccc530d398ac3931e95d3c729be869eac5070dac773edec62e3de1dc94b3c00a4560c6b6a998cbe
|
|
7
|
+
data.tar.gz: 5672bc7469d4a09a12c85fcd343c8008748ccb6a90f85dc54cfdf7feb9d81721a026aa91a11d555b826159fbe61a528732301cf19ab63470800068faa1971a42
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.8] - 2026-04-30
|
|
4
|
+
- Add Legion::Logging::Helper to all modules and classes for structured observability
|
|
5
|
+
- Replace bare rescue blocks with handle_exception for unified error telemetry
|
|
6
|
+
- Add info-level action logging for provider registration, model listing, model retrieval, and registry publishing
|
|
7
|
+
- Remove manual log_publish_failure helper in favor of handle_exception
|
|
8
|
+
- Update README to reflect current capabilities and architecture
|
|
9
|
+
|
|
3
10
|
## [0.1.7] - 2026-04-30
|
|
4
11
|
- Enable stream_usage_supported? for streaming token usage reporting
|
|
5
12
|
|
data/README.md
CHANGED
|
@@ -9,19 +9,42 @@ Load it with `require 'legion/extensions/llm/openai'`.
|
|
|
9
9
|
## What It Provides
|
|
10
10
|
|
|
11
11
|
- `Legion::Extensions::Llm::Provider` registration as `:openai`
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
12
|
+
- Chat completions via `POST /v1/chat/completions`
|
|
13
|
+
- Streaming chat completions (same endpoint, `stream: true`)
|
|
14
|
+
- Model discovery via `GET /v1/models`
|
|
15
|
+
- Model retrieval via `GET /v1/models/{model}`
|
|
16
|
+
- Embeddings via `POST /v1/embeddings`
|
|
17
|
+
- Moderation via `POST /v1/moderations`
|
|
18
|
+
- Image generation via `POST /v1/images/generations`
|
|
19
|
+
- Image editing via `POST /v1/images/edits`
|
|
20
|
+
- Image variation via `POST /v1/images/variations`
|
|
21
|
+
- Audio transcription via `POST /v1/audio/transcriptions`
|
|
22
|
+
- Streaming token usage reporting (`stream_usage_supported?`)
|
|
23
|
+
- Shared OpenAI-compatible request/response mapping via `Legion::Extensions::Llm::Provider::OpenAICompatible`
|
|
24
|
+
- Normalized chat, embedding, moderation, image, and audio capability mapping for discovered models
|
|
25
|
+
- Shared fleet/default settings via `Legion::Extensions::Llm.provider_settings`
|
|
26
|
+
- Best-effort `llm.registry` availability event publishing for discovered models
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Legion::Extensions::Llm::Openai
|
|
32
|
+
├── Provider # OpenAI provider implementation (chat, models, embeddings, etc.)
|
|
33
|
+
│ └── Capabilities # Model family capability predicates
|
|
34
|
+
├── RegistryPublisher # Async llm.registry event publisher
|
|
35
|
+
├── RegistryEventBuilder # Sanitized registry event envelope builder
|
|
36
|
+
└── Transport
|
|
37
|
+
├── Exchanges::LlmRegistry # Topic exchange for llm.registry
|
|
38
|
+
└── Messages::RegistryEvent # AMQP message for registry events
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Observability
|
|
42
|
+
|
|
43
|
+
All classes include `Legion::Logging::Helper` (when available) providing:
|
|
44
|
+
|
|
45
|
+
- Structured `handle_exception` calls on every rescue block
|
|
46
|
+
- Info-level action logging for provider registration, model listing, model retrieval, and registry publishing
|
|
47
|
+
- Automatic log segment derivation and component type tagging
|
|
25
48
|
|
|
26
49
|
## Defaults
|
|
27
50
|
|
|
@@ -47,6 +70,9 @@ Legion::Extensions::Llm::Openai.default_settings
|
|
|
47
70
|
```ruby
|
|
48
71
|
Legion::Extensions::Llm.configure do |config|
|
|
49
72
|
config.openai_api_key = ENV.fetch("OPENAI_API_KEY")
|
|
73
|
+
config.openai_api_base = nil # defaults to https://api.openai.com
|
|
74
|
+
config.openai_organization_id = nil # optional OpenAI-Organization header
|
|
75
|
+
config.openai_project_id = nil # optional OpenAI-Project header
|
|
50
76
|
config.default_model = "gpt-5.2"
|
|
51
77
|
config.default_embedding_model = "text-embedding-3-small"
|
|
52
78
|
config.default_moderation_model = "omni-moderation-latest"
|
|
@@ -54,3 +80,25 @@ Legion::Extensions::Llm.configure do |config|
|
|
|
54
80
|
config.default_transcription_model = "gpt-4o-transcribe"
|
|
55
81
|
end
|
|
56
82
|
```
|
|
83
|
+
|
|
84
|
+
## Dependencies
|
|
85
|
+
|
|
86
|
+
| Gem | Purpose |
|
|
87
|
+
|-----|---------|
|
|
88
|
+
| `lex-llm` (>= 0.1.5) | Shared provider contract, fleet settings, routing |
|
|
89
|
+
| `legion-json` (>= 1.2.1) | JSON serialization |
|
|
90
|
+
| `legion-logging` (>= 1.3.2) | Structured logging via Helper |
|
|
91
|
+
| `legion-settings` (>= 1.3.14) | Configuration management |
|
|
92
|
+
|
|
93
|
+
## Development
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
bundle install
|
|
97
|
+
bundle exec rspec --format progress # all pass
|
|
98
|
+
bundle exec rubocop -A # auto-fix
|
|
99
|
+
bundle exec rubocop # lint check (0 offenses)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT
|
|
@@ -9,6 +9,7 @@ module Legion
|
|
|
9
9
|
# OpenAI provider implementation for the Legion::Extensions::Llm base provider contract.
|
|
10
10
|
class Provider < Legion::Extensions::Llm::Provider
|
|
11
11
|
include Legion::Extensions::Llm::Provider::OpenAICompatible
|
|
12
|
+
include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
12
13
|
|
|
13
14
|
class << self
|
|
14
15
|
attr_writer :registry_publisher
|
|
@@ -94,13 +95,28 @@ module Legion
|
|
|
94
95
|
def images_url(with: nil, mask: nil) = super
|
|
95
96
|
|
|
96
97
|
def retrieve_model(model)
|
|
98
|
+
log.info("Retrieving model: #{model}") if respond_to?(:log)
|
|
97
99
|
connection.get("#{models_url}/#{model}").body
|
|
100
|
+
rescue StandardError => e
|
|
101
|
+
if respond_to?(:handle_exception)
|
|
102
|
+
handle_exception(e, level: :error, handled: true,
|
|
103
|
+
operation: 'retrieve_model')
|
|
104
|
+
end
|
|
105
|
+
raise
|
|
98
106
|
end
|
|
99
107
|
|
|
100
108
|
def list_models
|
|
109
|
+
log.info('Listing OpenAI models') if respond_to?(:log)
|
|
101
110
|
super.tap do |models|
|
|
111
|
+
log.info("Discovered #{models.size} OpenAI models") if respond_to?(:log)
|
|
102
112
|
self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
|
|
103
113
|
end
|
|
114
|
+
rescue StandardError => e
|
|
115
|
+
if respond_to?(:handle_exception)
|
|
116
|
+
handle_exception(e, level: :error, handled: true,
|
|
117
|
+
operation: 'list_models')
|
|
118
|
+
end
|
|
119
|
+
raise
|
|
104
120
|
end
|
|
105
121
|
|
|
106
122
|
private
|
|
@@ -6,6 +6,8 @@ module Legion
|
|
|
6
6
|
module Openai
|
|
7
7
|
# Builds sanitized lex-llm registry envelopes for OpenAI provider state.
|
|
8
8
|
class RegistryEventBuilder
|
|
9
|
+
include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
10
|
+
|
|
9
11
|
def model_available(model, readiness:)
|
|
10
12
|
registry_event_class.available(
|
|
11
13
|
model_offering(model),
|
|
@@ -58,7 +60,11 @@ module Legion
|
|
|
58
60
|
configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
|
|
59
61
|
value = configured_node.to_s.strip
|
|
60
62
|
value.empty? ? :openai : value.to_sym
|
|
61
|
-
rescue StandardError
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
if respond_to?(:handle_exception)
|
|
65
|
+
handle_exception(e, level: :debug, handled: true,
|
|
66
|
+
operation: 'provider_instance')
|
|
67
|
+
end
|
|
62
68
|
:openai
|
|
63
69
|
end
|
|
64
70
|
|
|
@@ -6,6 +6,8 @@ module Legion
|
|
|
6
6
|
module Openai
|
|
7
7
|
# Best-effort publisher for OpenAI provider availability events.
|
|
8
8
|
class RegistryPublisher
|
|
9
|
+
include Legion::Logging::Helper if defined?(Legion::Logging::Helper)
|
|
10
|
+
|
|
9
11
|
APP_ID = 'lex-llm-openai'
|
|
10
12
|
|
|
11
13
|
def initialize(builder: RegistryEventBuilder.new)
|
|
@@ -13,6 +15,7 @@ module Legion
|
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def publish_models_async(models, readiness:)
|
|
18
|
+
log.info("Publishing #{Array(models).size} model(s) to llm.registry") if respond_to?(:log)
|
|
16
19
|
schedule do
|
|
17
20
|
Array(models).each do |model|
|
|
18
21
|
publish_event(@builder.model_available(model, readiness:))
|
|
@@ -29,10 +32,10 @@ module Legion
|
|
|
29
32
|
Thread.current.abort_on_exception = false
|
|
30
33
|
yield
|
|
31
34
|
rescue StandardError => e
|
|
32
|
-
|
|
35
|
+
handle_exception(e, level: :debug, handled: true, operation: 'schedule') if respond_to?(:handle_exception)
|
|
33
36
|
end
|
|
34
37
|
rescue StandardError => e
|
|
35
|
-
|
|
38
|
+
handle_exception(e, level: :debug, handled: true, operation: 'schedule') if respond_to?(:handle_exception)
|
|
36
39
|
false
|
|
37
40
|
end
|
|
38
41
|
|
|
@@ -41,7 +44,10 @@ module Legion
|
|
|
41
44
|
|
|
42
45
|
message_class.new(event:, app_id: APP_ID).publish(spool: false)
|
|
43
46
|
rescue StandardError => e
|
|
44
|
-
|
|
47
|
+
if respond_to?(:handle_exception)
|
|
48
|
+
handle_exception(e, level: :warn, handled: true,
|
|
49
|
+
operation: 'publish_event')
|
|
50
|
+
end
|
|
45
51
|
false
|
|
46
52
|
end
|
|
47
53
|
|
|
@@ -52,7 +58,11 @@ module Legion
|
|
|
52
58
|
return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
|
|
53
59
|
|
|
54
60
|
::Legion::Transport::Connection.session_open?
|
|
55
|
-
rescue StandardError
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
if respond_to?(:handle_exception)
|
|
63
|
+
handle_exception(e, level: :debug, handled: true,
|
|
64
|
+
operation: 'publishing_available?')
|
|
65
|
+
end
|
|
56
66
|
false
|
|
57
67
|
end
|
|
58
68
|
|
|
@@ -66,7 +76,11 @@ module Legion
|
|
|
66
76
|
|
|
67
77
|
require 'legion/extensions/llm/openai/transport/messages/registry_event'
|
|
68
78
|
message_class_defined?
|
|
69
|
-
rescue LoadError
|
|
79
|
+
rescue LoadError => e
|
|
80
|
+
if respond_to?(:handle_exception)
|
|
81
|
+
handle_exception(e, level: :debug, handled: true,
|
|
82
|
+
operation: 'transport_message_available?')
|
|
83
|
+
end
|
|
70
84
|
false
|
|
71
85
|
end
|
|
72
86
|
|
|
@@ -77,18 +91,6 @@ module Legion
|
|
|
77
91
|
def message_class
|
|
78
92
|
::Legion::Extensions::Llm::Openai::Transport::Messages::RegistryEvent
|
|
79
93
|
end
|
|
80
|
-
|
|
81
|
-
def log_publish_failure(error, level: :warn)
|
|
82
|
-
message = "[lex-llm-openai] llm.registry publish failed: #{error.class}: #{error.message}"
|
|
83
|
-
logger = ::Legion::Extensions::Llm.logger if defined?(::Legion::Extensions::Llm)
|
|
84
|
-
if logger.respond_to?(level)
|
|
85
|
-
logger.public_send(level, message)
|
|
86
|
-
elsif logger.respond_to?(:debug)
|
|
87
|
-
logger.debug(message)
|
|
88
|
-
end
|
|
89
|
-
rescue StandardError
|
|
90
|
-
nil
|
|
91
|
-
end
|
|
92
94
|
end
|
|
93
95
|
end
|
|
94
96
|
end
|
|
@@ -12,6 +12,7 @@ module Legion
|
|
|
12
12
|
# Openai provider extension namespace.
|
|
13
13
|
module Openai
|
|
14
14
|
extend ::Legion::Extensions::Core if ::Legion::Extensions.const_defined?(:Core, false)
|
|
15
|
+
extend ::Legion::Logging::Helper if defined?(::Legion::Logging::Helper)
|
|
15
16
|
|
|
16
17
|
PROVIDER_FAMILY = :openai
|
|
17
18
|
|
|
@@ -39,3 +40,6 @@ end
|
|
|
39
40
|
|
|
40
41
|
Legion::Extensions::Llm::Provider.register(Legion::Extensions::Llm::Openai::PROVIDER_FAMILY,
|
|
41
42
|
Legion::Extensions::Llm::Openai::Provider)
|
|
43
|
+
if defined?(Legion::Logging::Helper) && Legion::Extensions::Llm::Openai.respond_to?(:log)
|
|
44
|
+
Legion::Extensions::Llm::Openai.log.info('Registered OpenAI provider for :openai family')
|
|
45
|
+
end
|