lex-llm-openai 0.4.1 → 0.4.5
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 +20 -0
- data/Gemfile +0 -6
- data/lex-llm-openai.gemspec +1 -1
- data/lib/legion/extensions/llm/openai/actors/discovery_refresh.rb +7 -0
- data/lib/legion/extensions/llm/openai/provider.rb +146 -7
- data/lib/legion/extensions/llm/openai/translator.rb +20 -2
- data/lib/legion/extensions/llm/openai/version.rb +1 -1
- data/lib/legion/extensions/llm/openai.rb +19 -9
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4091bfdcfbcd60f52453e29e42e30a4564edd48dabcd9b0f8edadfa68440f52b
|
|
4
|
+
data.tar.gz: 0de0b799f80f47f937a9ecdc797996c4c0d3e64784fc518bf0b5de8628df135b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0abdd81c00439b7ccba758adbd7f8576e83ce5e367673ce2092708dc3f61290c406db462e0736e56402957c32f47b0fd781bdcf6486508a5b8486ab48f6d2edf
|
|
7
|
+
data.tar.gz: 29b4b768e0bd91aee7cd05283b62caf93d4ffbd6215063c7fa480d3ed1a2b164341e669d6badcb4e7f804db55e9500e81c016eef5f4a7223c20039730942ec37
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.5 - 2026-06-17
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **Policy-aware default model** — `default_model` is no longer a hardcoded literal forced via `||=`. The `gpt-5.5` fallback is now a named `DEFAULT_MODEL` constant applied through `Provider.policy_safe_default_model`, so a configured `model_whitelist`/`model_blacklist` is never overridden: if neither the configured default nor the fallback is permitted, `default_model` is left unset and routing resolves an allowed discovered model instead. Requires lex-llm >= 0.5.4.
|
|
7
|
+
|
|
8
|
+
## 0.4.4 - 2026-06-16
|
|
9
|
+
|
|
10
|
+
- dependency updates, code quality improvements
|
|
11
|
+
|
|
12
|
+
## 0.4.3 - 2026-06-15
|
|
13
|
+
|
|
14
|
+
- **CapabilityPolicy integration** — CAPABILITY_MAP fed as `:provider_catalog` source. Settings overrides at provider/instance/model level supported.
|
|
15
|
+
|
|
16
|
+
## 0.4.2 — 2026-06-13
|
|
17
|
+
|
|
18
|
+
- **Gemfile cleanup** — Remove local path overrides; dependencies resolve from gemspec via rubygems.
|
|
19
|
+
- **Capabilities** — Add canonical `:tools` to capability declarations.
|
|
20
|
+
- **Bug fix** — Extract nested `cached_tokens` from usage details (G26).
|
|
21
|
+
- 153 examples, 0 failures; 16 files, 0 rubocop offenses.
|
|
22
|
+
|
|
3
23
|
## 0.4.1 — 2026-06-10
|
|
4
24
|
|
|
5
25
|
- Canonical translator (`Translator`): `render_request`, `parse_response`, `parse_chunk`, `capabilities` — provider-boundary contract per N×N routing Amendment A
|
data/Gemfile
CHANGED
|
@@ -2,12 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
source 'https://rubygems.org'
|
|
4
4
|
|
|
5
|
-
group :test do
|
|
6
|
-
transport_path = ENV.fetch('LEGION_TRANSPORT_PATH', File.expand_path('../../legion-transport', __dir__))
|
|
7
|
-
gem 'legion-transport', path: transport_path if File.directory?(transport_path)
|
|
8
|
-
# lex-llm resolved from rubygems (>= 0.5.0) via gemspec - no local path dep
|
|
9
|
-
end
|
|
10
|
-
|
|
11
5
|
gemspec
|
|
12
6
|
|
|
13
7
|
group :development do
|
data/lex-llm-openai.gemspec
CHANGED
|
@@ -27,5 +27,5 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency 'legion-logging', '>= 1.3.2'
|
|
28
28
|
spec.add_dependency 'legion-settings', '>= 1.3.14'
|
|
29
29
|
spec.add_dependency 'legion-transport', '>= 1.4.14'
|
|
30
|
-
spec.add_dependency 'lex-llm', '>= 0.5.
|
|
30
|
+
spec.add_dependency 'lex-llm', '>= 0.5.4'
|
|
31
31
|
end
|
|
@@ -37,6 +37,13 @@ module Legion
|
|
|
37
37
|
return unless defined?(Legion::LLM::Discovery)
|
|
38
38
|
|
|
39
39
|
Legion::LLM::Discovery.refresh_discovered_models!(provider: :openai)
|
|
40
|
+
|
|
41
|
+
if defined?(Legion::LLM::Router) && Legion::LLM::Router.respond_to?(:populate_auto_rules)
|
|
42
|
+
Legion::LLM::Router.populate_auto_rules(Legion::LLM::Discovery.discovered_instances)
|
|
43
|
+
end
|
|
44
|
+
if defined?(Legion::LLM::Inventory) && Legion::LLM::Inventory.respond_to?(:invalidate_offerings_cache!)
|
|
45
|
+
Legion::LLM::Inventory.invalidate_offerings_cache!
|
|
46
|
+
end
|
|
40
47
|
rescue StandardError => e
|
|
41
48
|
handle_exception(e, level: :warn, handled: true, operation: 'openai.actor.discovery_refresh')
|
|
42
49
|
end
|
|
@@ -17,43 +17,43 @@ module Legion
|
|
|
17
17
|
# the raw /v1/models response.
|
|
18
18
|
CAPABILITY_MAP = {
|
|
19
19
|
'gpt-4o' => {
|
|
20
|
-
capabilities: %i[completion streaming function_calling vision structured_output],
|
|
20
|
+
capabilities: %i[completion streaming function_calling tools vision structured_output],
|
|
21
21
|
modalities_input: %w[text image audio],
|
|
22
22
|
modalities_output: %w[text],
|
|
23
23
|
context_window: 128_000
|
|
24
24
|
},
|
|
25
25
|
'gpt-4.1' => {
|
|
26
|
-
capabilities: %i[completion streaming function_calling vision structured_output],
|
|
26
|
+
capabilities: %i[completion streaming function_calling tools vision structured_output],
|
|
27
27
|
modalities_input: %w[text image],
|
|
28
28
|
modalities_output: %w[text],
|
|
29
29
|
context_window: 1_047_576
|
|
30
30
|
},
|
|
31
31
|
'gpt-4' => {
|
|
32
|
-
capabilities: %i[completion streaming function_calling vision],
|
|
32
|
+
capabilities: %i[completion streaming function_calling tools vision],
|
|
33
33
|
modalities_input: %w[text image],
|
|
34
34
|
modalities_output: %w[text],
|
|
35
35
|
context_window: 128_000
|
|
36
36
|
},
|
|
37
37
|
'gpt-5' => {
|
|
38
|
-
capabilities: %i[completion streaming function_calling vision structured_output reasoning],
|
|
38
|
+
capabilities: %i[completion streaming function_calling tools vision structured_output reasoning],
|
|
39
39
|
modalities_input: %w[text image],
|
|
40
40
|
modalities_output: %w[text],
|
|
41
41
|
context_window: 1_047_576
|
|
42
42
|
},
|
|
43
43
|
'o4' => {
|
|
44
|
-
capabilities: %i[completion streaming function_calling vision reasoning],
|
|
44
|
+
capabilities: %i[completion streaming function_calling tools vision reasoning],
|
|
45
45
|
modalities_input: %w[text image],
|
|
46
46
|
modalities_output: %w[text],
|
|
47
47
|
context_window: 200_000
|
|
48
48
|
},
|
|
49
49
|
'o3' => {
|
|
50
|
-
capabilities: %i[completion streaming function_calling vision reasoning],
|
|
50
|
+
capabilities: %i[completion streaming function_calling tools vision reasoning],
|
|
51
51
|
modalities_input: %w[text image],
|
|
52
52
|
modalities_output: %w[text],
|
|
53
53
|
context_window: 200_000
|
|
54
54
|
},
|
|
55
55
|
'o1' => {
|
|
56
|
-
capabilities: %i[completion streaming function_calling vision reasoning],
|
|
56
|
+
capabilities: %i[completion streaming function_calling tools vision reasoning],
|
|
57
57
|
modalities_input: %w[text image],
|
|
58
58
|
modalities_output: %w[text],
|
|
59
59
|
context_window: 200_000
|
|
@@ -211,6 +211,36 @@ module Legion
|
|
|
211
211
|
raise
|
|
212
212
|
end
|
|
213
213
|
|
|
214
|
+
def discover_offerings(live: false, **)
|
|
215
|
+
models = if live
|
|
216
|
+
@cached_models = list_models
|
|
217
|
+
else
|
|
218
|
+
Array(@cached_models)
|
|
219
|
+
end
|
|
220
|
+
offerings = models.filter_map { |model_info| offering_from_model(model_info) }
|
|
221
|
+
log.debug { "built #{offerings.size} OpenAI offering(s) live=#{live}" }
|
|
222
|
+
offerings
|
|
223
|
+
rescue StandardError => e
|
|
224
|
+
handle_exception(e, level: :warn, handled: true, operation: 'openai.discover_offerings')
|
|
225
|
+
[]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# ── CapabilityPolicy integration ─────────────────────────────────
|
|
229
|
+
# Maps raw CAPABILITY_MAP symbol arrays to the boolean hash format
|
|
230
|
+
# that CapabilityPolicy.resolve expects as :provider_catalog.
|
|
231
|
+
CATALOG_CAPABILITY_MAPPING = {
|
|
232
|
+
streaming: :streaming,
|
|
233
|
+
function_calling: :tools,
|
|
234
|
+
tools: :tools,
|
|
235
|
+
vision: :vision,
|
|
236
|
+
structured_output: :structured_output,
|
|
237
|
+
reasoning: :thinking,
|
|
238
|
+
embedding: :embeddings,
|
|
239
|
+
image_generation: :image,
|
|
240
|
+
audio_transcription: :audio_transcription,
|
|
241
|
+
audio_generation: :audio_speech
|
|
242
|
+
}.freeze
|
|
243
|
+
|
|
214
244
|
private
|
|
215
245
|
|
|
216
246
|
def build_model_infos(body)
|
|
@@ -248,6 +278,115 @@ module Legion
|
|
|
248
278
|
}
|
|
249
279
|
end
|
|
250
280
|
|
|
281
|
+
def offering_from_model(model_info)
|
|
282
|
+
policy = resolve_model_policy(model_info)
|
|
283
|
+
|
|
284
|
+
Legion::Extensions::Llm::Routing::ModelOffering.new(
|
|
285
|
+
offering_attrs_for(model_info, policy)
|
|
286
|
+
)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def resolve_model_policy(model_info)
|
|
290
|
+
catalog = capabilities_to_boolean_hash(capability_entry_for(model_info.id)[:capabilities])
|
|
291
|
+
|
|
292
|
+
Legion::Extensions::Llm::CapabilityPolicy.resolve(
|
|
293
|
+
real: {},
|
|
294
|
+
provider_catalog: catalog,
|
|
295
|
+
probe: {},
|
|
296
|
+
provider_envelope: {},
|
|
297
|
+
provider_config: provider_capability_config,
|
|
298
|
+
instance_config: instance_capability_config,
|
|
299
|
+
model_config: model_capability_config(model_info.id)
|
|
300
|
+
)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def offering_attrs_for(model_info, policy)
|
|
304
|
+
{
|
|
305
|
+
provider_family: :openai,
|
|
306
|
+
instance_id: config.respond_to?(:instance_id) ? config.instance_id : :default,
|
|
307
|
+
transport: :http,
|
|
308
|
+
tier: :frontier,
|
|
309
|
+
model: model_info.id,
|
|
310
|
+
canonical_model_alias: model_info.respond_to?(:name) ? model_info.name : nil,
|
|
311
|
+
model_family: infer_model_family(model_info.id),
|
|
312
|
+
usage_type: infer_usage_type(model_info),
|
|
313
|
+
capabilities: policy[:capabilities],
|
|
314
|
+
capability_sources: policy[:sources],
|
|
315
|
+
limits: { context_window: model_info.context_length }.compact,
|
|
316
|
+
metadata: { capability_sources: policy[:sources] }
|
|
317
|
+
}
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def capabilities_to_boolean_hash(capability_symbols)
|
|
321
|
+
return {} unless capability_symbols.is_a?(Array)
|
|
322
|
+
|
|
323
|
+
result = {}
|
|
324
|
+
capability_symbols.each do |sym|
|
|
325
|
+
mapped = CATALOG_CAPABILITY_MAPPING[sym]
|
|
326
|
+
result[mapped] = true if mapped
|
|
327
|
+
end
|
|
328
|
+
result
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def provider_capability_config
|
|
332
|
+
return {} unless defined?(Legion::Extensions::Llm::CredentialSources)
|
|
333
|
+
|
|
334
|
+
conf = Legion::Extensions::Llm::CredentialSources.setting(:extensions, :llm, :openai)
|
|
335
|
+
conf.is_a?(Hash) ? conf.to_h.except(:instances, 'instances') : {}
|
|
336
|
+
rescue StandardError => e
|
|
337
|
+
handle_exception(e, level: :debug, handled: true, operation: 'openai.provider_capability_config')
|
|
338
|
+
{}
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def instance_capability_config
|
|
342
|
+
cfg = config
|
|
343
|
+
result = {}
|
|
344
|
+
%i[capabilities enable_thinking enable_tools enable_streaming enable_vision enable_embeddings
|
|
345
|
+
thinking_flag tools_flag streaming_flag vision_flag embedding_flag embeddings_flag
|
|
346
|
+
tool_flag images_flag image_flag].each do |key|
|
|
347
|
+
next unless cfg.respond_to?(key)
|
|
348
|
+
|
|
349
|
+
val = cfg.send(key)
|
|
350
|
+
result[key] = val unless val.nil?
|
|
351
|
+
rescue StandardError
|
|
352
|
+
next
|
|
353
|
+
end
|
|
354
|
+
result
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def model_capability_config(model_id)
|
|
358
|
+
models_conf = fetch_models_config
|
|
359
|
+
return {} unless models_conf.respond_to?(:to_h)
|
|
360
|
+
|
|
361
|
+
models_conf.to_h[model_id.to_s] || models_conf.to_h[model_id.to_sym] || {}
|
|
362
|
+
rescue StandardError => e
|
|
363
|
+
handle_exception(e, level: :debug, handled: true, operation: 'openai.model_capability_config')
|
|
364
|
+
{}
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def fetch_models_config
|
|
368
|
+
return config.models if config.respond_to?(:models)
|
|
369
|
+
return config[:models] if config.respond_to?(:[])
|
|
370
|
+
|
|
371
|
+
nil
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def infer_model_family(model_id)
|
|
375
|
+
CAPABILITY_MAP.each_key do |prefix|
|
|
376
|
+
return prefix.tr('-', '_').to_sym if model_id.start_with?(prefix)
|
|
377
|
+
end
|
|
378
|
+
:unknown
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def infer_usage_type(model_info)
|
|
382
|
+
caps = model_info.respond_to?(:capabilities) ? Array(model_info.capabilities) : []
|
|
383
|
+
return :embedding if caps.include?(:embedding)
|
|
384
|
+
return :moderation if caps.include?(:moderation)
|
|
385
|
+
return :image if caps.include?(:image_generation)
|
|
386
|
+
|
|
387
|
+
:inference
|
|
388
|
+
end
|
|
389
|
+
|
|
251
390
|
def fetch_model_detail(model_name)
|
|
252
391
|
entry = capability_entry_for(model_name)
|
|
253
392
|
ctx = entry[:context_window]
|
|
@@ -287,7 +287,8 @@ module Legion
|
|
|
287
287
|
end
|
|
288
288
|
|
|
289
289
|
def render_openai_tool_calls(tool_calls)
|
|
290
|
-
|
|
290
|
+
tc_array = tool_calls.is_a?(Hash) ? tool_calls.values : Array(tool_calls)
|
|
291
|
+
tc_array.map do |tc|
|
|
291
292
|
args = tc.arguments.is_a?(String) ? tc.arguments : Legion::JSON.generate(tc.arguments || {})
|
|
292
293
|
|
|
293
294
|
{
|
|
@@ -353,7 +354,24 @@ module Legion
|
|
|
353
354
|
def parse_usage(raw)
|
|
354
355
|
return nil unless raw&.any?
|
|
355
356
|
|
|
356
|
-
|
|
357
|
+
normalized = raw.dup
|
|
358
|
+
normalized[:cache_read_tokens] ||= extract_nested_cached_tokens(raw)
|
|
359
|
+
normalized[:thinking_tokens] ||= extract_nested_reasoning_tokens(raw)
|
|
360
|
+
Canonical::Usage.from_hash(normalized)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def extract_nested_cached_tokens(raw)
|
|
364
|
+
raw.dig(:prompt_tokens_details, :cached_tokens) ||
|
|
365
|
+
raw.dig('prompt_tokens_details', 'cached_tokens') ||
|
|
366
|
+
raw.dig(:input_tokens_details, :cached_tokens) ||
|
|
367
|
+
raw.dig('input_tokens_details', 'cached_tokens')
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def extract_nested_reasoning_tokens(raw)
|
|
371
|
+
raw.dig(:completion_tokens_details, :reasoning_tokens) ||
|
|
372
|
+
raw.dig('completion_tokens_details', 'reasoning_tokens') ||
|
|
373
|
+
raw.dig(:output_tokens_details, :reasoning_tokens) ||
|
|
374
|
+
raw.dig('output_tokens_details', 'reasoning_tokens')
|
|
357
375
|
end
|
|
358
376
|
|
|
359
377
|
def map_stop_reason(raw)
|
|
@@ -4,6 +4,7 @@ require 'legion/extensions/llm'
|
|
|
4
4
|
require 'legion/extensions/llm/openai/provider'
|
|
5
5
|
require 'legion/extensions/llm/openai/translator'
|
|
6
6
|
require 'legion/extensions/llm/openai/version'
|
|
7
|
+
require_relative 'openai/actors/discovery_refresh'
|
|
7
8
|
|
|
8
9
|
module Legion
|
|
9
10
|
module Extensions
|
|
@@ -15,13 +16,17 @@ module Legion
|
|
|
15
16
|
extend Legion::Extensions::Llm::AutoRegistration
|
|
16
17
|
|
|
17
18
|
PROVIDER_FAMILY = :openai
|
|
19
|
+
# Provider's preferred default when the operator configures none. Used only
|
|
20
|
+
# as a fallback and only when the configured model policy permits it
|
|
21
|
+
# (see resolve_default_model) — a whitelist/blacklist is never overridden.
|
|
22
|
+
DEFAULT_MODEL = 'gpt-5.5'
|
|
18
23
|
|
|
19
24
|
def self.default_settings
|
|
20
25
|
::Legion::Extensions::Llm.provider_settings(
|
|
21
26
|
family: PROVIDER_FAMILY,
|
|
22
27
|
instance: {
|
|
23
28
|
endpoint: 'https://api.openai.com',
|
|
24
|
-
default_model:
|
|
29
|
+
default_model: DEFAULT_MODEL,
|
|
25
30
|
tier: :frontier,
|
|
26
31
|
transport: :http,
|
|
27
32
|
credentials: {
|
|
@@ -40,10 +45,7 @@ module Legion
|
|
|
40
45
|
fleet: {
|
|
41
46
|
enabled: false,
|
|
42
47
|
respond_to_requests: false,
|
|
43
|
-
capabilities: %i[chat stream_chat embed image]
|
|
44
|
-
lanes: [],
|
|
45
|
-
concurrency: 4,
|
|
46
|
-
queue_suffix: nil
|
|
48
|
+
capabilities: %i[chat stream_chat embed image]
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
)
|
|
@@ -102,10 +104,10 @@ module Legion
|
|
|
102
104
|
candidates[name.to_sym] = normalized.merge(tier: :frontier)
|
|
103
105
|
end
|
|
104
106
|
|
|
105
|
-
# 8. Dedup + inject default_model
|
|
107
|
+
# 8. Dedup + inject a policy-aware default_model
|
|
106
108
|
discovered = CredentialSources.dedup_credentials(candidates).transform_values do |config|
|
|
107
109
|
sanitized = sanitize_instance_config(config)
|
|
108
|
-
sanitized[:default_model]
|
|
110
|
+
sanitized[:default_model] = resolve_default_model(sanitized)
|
|
109
111
|
sanitized
|
|
110
112
|
end
|
|
111
113
|
instance_names = discovered.keys.sort_by(&:to_s).join(', ')
|
|
@@ -113,6 +115,15 @@ module Legion
|
|
|
113
115
|
discovered
|
|
114
116
|
end
|
|
115
117
|
|
|
118
|
+
# Resolve a default_model that never violates the configured model policy
|
|
119
|
+
# (whitelist/blacklist stays authoritative over the DEFAULT_MODEL fallback).
|
|
120
|
+
def self.resolve_default_model(config)
|
|
121
|
+
provider_class.policy_safe_default_model(
|
|
122
|
+
configured: config[:default_model], fallback: DEFAULT_MODEL,
|
|
123
|
+
**provider_class.model_policy(config, PROVIDER_FAMILY)
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
116
127
|
def self.settings_instances(config)
|
|
117
128
|
return {} unless config.is_a?(Hash)
|
|
118
129
|
|
|
@@ -135,8 +146,7 @@ module Legion
|
|
|
135
146
|
config.except(:api_key, :organization_id, :project_id)
|
|
136
147
|
end
|
|
137
148
|
|
|
138
|
-
Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
|
|
139
|
-
Legion::Extensions::Llm::Configuration.respond_to?(:register_provider_options)
|
|
149
|
+
Legion::Extensions::Llm::Configuration.register_provider_options(Provider.configuration_options)
|
|
140
150
|
end
|
|
141
151
|
end
|
|
142
152
|
end
|
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.4.
|
|
4
|
+
version: 0.4.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LegionIO
|
|
@@ -71,14 +71,14 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.5.
|
|
74
|
+
version: 0.5.4
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.5.
|
|
81
|
+
version: 0.5.4
|
|
82
82
|
description: OpenAI provider integration for the LegionIO LLM routing framework.
|
|
83
83
|
email:
|
|
84
84
|
- matthewdiverson@gmail.com
|