lex-llm-openai 0.1.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d290fddaf838579b445dedeefb35628d83ca2826e10df699f3c8bd41f4ab9de
4
- data.tar.gz: 7da1c5be7431cb7e4991cf5ae8a04b470932851caf06f5a1368a2024323466a9
3
+ metadata.gz: aab8c5aaa3a502b204f0f97f9eb28672a81d18464090f22eb1f7afd1e1d4a95e
4
+ data.tar.gz: d4dfda3e0961f241fd71e8f0d3a1a8612ee5911efbe4aac0983111317e9b6481
5
5
  SHA512:
6
- metadata.gz: 1783aa4651f954f932af3be3f48f080b0537101882fbc0fc60acebe1c956c5029ed30842686140f480aa93a745311fde887c8ebd5040e88979ae6f7f7614b278
7
- data.tar.gz: 2311adb58da6d9465f1a48560fa40d74c5e0c532c41268f5f92bf0c7f3015721a5ccc5ff767a813d832a8bdeb17ac79c2ca82321df4d6a5e0865e19cf9bc130c
6
+ metadata.gz: 9e0cca53fa0c55b5577da00780013abf66455259c6c996eee570ab68e910bf589c5cb00661281210d26779bd683a1489914cb45b2a8b8096c7e141399ad16a18
7
+ data.tar.gz: 3a7869846152c7b902a9fdc08bee13f058dec8ebfa57a9ac4538901d07724e550248d0dae7542f4c62bbc0a917c88127f369cdfa2c19791dad83327052f3f27a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.6 - 2026-04-28
4
+
5
+ - Publish best-effort `llm.registry` discovered-model availability events when transport is already loaded.
6
+
7
+ ## 0.1.5 - 2026-04-28
8
+
9
+ - Require current shared Legion JSON, logging, settings, and `lex-llm >= 0.1.5` runtime dependencies.
10
+
11
+ ## 0.1.4 - 2026-04-28
12
+
13
+ - Require `lex-llm >= 0.1.4` so OpenAI model discovery exposes normalized capabilities and modalities.
14
+ - Cover discovered chat and embedding model metadata mapping for routing.
15
+
3
16
  ## 0.1.3 - 2026-04-28
4
17
 
5
18
  - Remove the leftover compatibility entrypoint outside the Legion namespace.
data/README.md CHANGED
@@ -20,6 +20,7 @@ Load it with `require 'legion/extensions/llm/openai'`.
20
20
  - image variation endpoint helper for `POST /v1/images/variations`
21
21
  - audio transcription through `POST /v1/audio/transcriptions`
22
22
  - shared OpenAI-compatible request/response mapping via `Legion::Extensions::Llm::Provider::OpenAICompatible`
23
+ - normalized chat, embedding, moderation, image, and audio capability mapping for discovered models
23
24
  - shared fleet/default settings via `Legion::Extensions::Llm.provider_settings`
24
25
 
25
26
  ## Defaults
@@ -23,5 +23,8 @@ Gem::Specification.new do |spec|
23
23
  spec.files = `git ls-files -z`.split("\x0").reject { |file| file.match(%r{^(spec|test|features|tmp|coverage)/}) }
24
24
  spec.require_paths = ['lib']
25
25
 
26
- spec.add_dependency 'lex-llm', '>= 0.1.3'
26
+ spec.add_dependency 'legion-json', '>= 1.2.1'
27
+ spec.add_dependency 'legion-logging', '>= 1.3.2'
28
+ spec.add_dependency 'legion-settings', '>= 1.3.14'
29
+ spec.add_dependency 'lex-llm', '>= 0.1.5'
27
30
  end
@@ -11,6 +11,8 @@ 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 = 'openai'
15
17
  def configuration_requirements = %i[openai_api_key]
16
18
 
@@ -25,6 +27,10 @@ module Legion
25
27
  end
26
28
 
27
29
  def capabilities = Capabilities
30
+
31
+ def registry_publisher
32
+ @registry_publisher ||= RegistryPublisher.new
33
+ end
28
34
  end
29
35
 
30
36
  # Provider-level capability checks based on current OpenAI model families.
@@ -56,6 +62,8 @@ module Legion
56
62
  end
57
63
 
58
64
  def model_id(model)
65
+ return model.fetch('id', '') if model.is_a?(Hash)
66
+
59
67
  model.respond_to?(:id) ? model.id.to_s : model.to_s
60
68
  end
61
69
 
@@ -87,6 +95,12 @@ module Legion
87
95
  connection.get("#{models_url}/#{model}").body
88
96
  end
89
97
 
98
+ def list_models
99
+ super.tap do |models|
100
+ self.class.registry_publisher.publish_models_async(models, readiness: readiness(live: false))
101
+ end
102
+ end
103
+
90
104
  private
91
105
 
92
106
  def maybe_normalize_temperature(temperature, model)
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Openai
7
+ # Builds sanitized lex-llm registry envelopes for OpenAI provider state.
8
+ class RegistryEventBuilder
9
+ def model_available(model, readiness:)
10
+ registry_event_class.available(
11
+ model_offering(model),
12
+ runtime: runtime_metadata,
13
+ health: model_health(readiness),
14
+ metadata: model_metadata(model)
15
+ )
16
+ end
17
+
18
+ private
19
+
20
+ def model_offering(model)
21
+ {
22
+ provider_family: :openai,
23
+ provider_instance: provider_instance,
24
+ transport: :http,
25
+ model: model.id,
26
+ usage_type: usage_type_for(model),
27
+ capabilities: Array(model.capabilities).map(&:to_sym),
28
+ limits: model_limits(model),
29
+ metadata: { lex: :llm_openai, model_name: model.name }.compact
30
+ }
31
+ end
32
+
33
+ def model_health(readiness)
34
+ ready = readiness.fetch(:ready, true) == true
35
+ { ready:, status: ready ? :available : :degraded }
36
+ end
37
+
38
+ def model_metadata(model)
39
+ { extension: :lex_llm_openai, provider: :openai, model_type: model.type }
40
+ end
41
+
42
+ def runtime_metadata
43
+ { node: provider_instance }
44
+ end
45
+
46
+ def model_limits(model)
47
+ {
48
+ context_window: model.context_window,
49
+ max_output_tokens: model.max_output_tokens
50
+ }.compact
51
+ end
52
+
53
+ def usage_type_for(model)
54
+ model.type == 'embedding' ? :embedding : :inference
55
+ end
56
+
57
+ def provider_instance
58
+ configured_node = (::Legion::Settings.dig(:node, :canonical_name) if defined?(::Legion::Settings))
59
+ value = configured_node.to_s.strip
60
+ value.empty? ? :openai : value.to_sym
61
+ rescue StandardError
62
+ :openai
63
+ end
64
+
65
+ def registry_event_class
66
+ ::Legion::Extensions::Llm::Routing::RegistryEvent
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Openai
7
+ # Best-effort publisher for OpenAI provider availability events.
8
+ class RegistryPublisher
9
+ APP_ID = 'lex-llm-openai'
10
+
11
+ def initialize(builder: RegistryEventBuilder.new)
12
+ @builder = builder
13
+ end
14
+
15
+ def publish_models_async(models, readiness:)
16
+ schedule do
17
+ Array(models).each do |model|
18
+ publish_event(@builder.model_available(model, readiness:))
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def schedule(&)
26
+ return false unless publishing_available?
27
+
28
+ Thread.new do
29
+ Thread.current.abort_on_exception = false
30
+ yield
31
+ rescue StandardError => e
32
+ log_publish_failure(e, level: :debug)
33
+ end
34
+ rescue StandardError => e
35
+ log_publish_failure(e, level: :debug)
36
+ false
37
+ end
38
+
39
+ def publish_event(event)
40
+ return false unless publishing_available?
41
+
42
+ message_class.new(event:, app_id: APP_ID).publish(spool: false)
43
+ rescue StandardError => e
44
+ log_publish_failure(e)
45
+ false
46
+ end
47
+
48
+ def publishing_available?
49
+ return false unless registry_event_available?
50
+ return false unless transport_message_available?
51
+ return true unless defined?(::Legion::Transport::Connection)
52
+ return true unless ::Legion::Transport::Connection.respond_to?(:session_open?)
53
+
54
+ ::Legion::Transport::Connection.session_open?
55
+ rescue StandardError
56
+ false
57
+ end
58
+
59
+ def registry_event_available?
60
+ defined?(::Legion::Extensions::Llm::Routing::RegistryEvent)
61
+ end
62
+
63
+ def transport_message_available?
64
+ return true if message_class_defined?
65
+ return false unless defined?(::Legion::Transport::Message) && defined?(::Legion::Transport::Exchange)
66
+
67
+ require 'legion/extensions/llm/openai/transport/messages/registry_event'
68
+ message_class_defined?
69
+ rescue LoadError
70
+ false
71
+ end
72
+
73
+ def message_class_defined?
74
+ defined?(::Legion::Extensions::Llm::Openai::Transport::Messages::RegistryEvent)
75
+ end
76
+
77
+ def message_class
78
+ ::Legion::Extensions::Llm::Openai::Transport::Messages::RegistryEvent
79
+ 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
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Openai
7
+ module Transport
8
+ module Exchanges
9
+ # Topic exchange for OpenAI 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/openai/transport/exchanges/llm_registry'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Llm
8
+ module Openai
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
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Openai
7
- VERSION = '0.1.3'
7
+ VERSION = '0.1.6'
8
8
  end
9
9
  end
10
10
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/llm'
4
+ require 'legion/extensions/llm/openai/registry_event_builder'
5
+ require 'legion/extensions/llm/openai/registry_publisher'
4
6
  require 'legion/extensions/llm/openai/provider'
5
7
  require 'legion/extensions/llm/openai/version'
6
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-openai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - LegionIO
@@ -9,20 +9,62 @@ bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: legion-json
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 1.2.1
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 1.2.1
26
+ - !ruby/object:Gem::Dependency
27
+ name: legion-logging
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.3.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: legion-settings
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.3.14
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.14
12
54
  - !ruby/object:Gem::Dependency
13
55
  name: lex-llm
14
56
  requirement: !ruby/object:Gem::Requirement
15
57
  requirements:
16
58
  - - ">="
17
59
  - !ruby/object:Gem::Version
18
- version: 0.1.3
60
+ version: 0.1.5
19
61
  type: :runtime
20
62
  prerelease: false
21
63
  version_requirements: !ruby/object:Gem::Requirement
22
64
  requirements:
23
65
  - - ">="
24
66
  - !ruby/object:Gem::Version
25
- version: 0.1.3
67
+ version: 0.1.5
26
68
  description: OpenAI provider integration for the LegionIO LLM routing framework.
27
69
  email:
28
70
  - matthewdiverson@gmail.com
@@ -42,6 +84,10 @@ files:
42
84
  - lex-llm-openai.gemspec
43
85
  - lib/legion/extensions/llm/openai.rb
44
86
  - lib/legion/extensions/llm/openai/provider.rb
87
+ - lib/legion/extensions/llm/openai/registry_event_builder.rb
88
+ - lib/legion/extensions/llm/openai/registry_publisher.rb
89
+ - lib/legion/extensions/llm/openai/transport/exchanges/llm_registry.rb
90
+ - lib/legion/extensions/llm/openai/transport/messages/registry_event.rb
45
91
  - lib/legion/extensions/llm/openai/version.rb
46
92
  homepage: https://github.com/LegionIO/lex-llm-openai
47
93
  licenses: