lex-openai 0.1.3 → 0.1.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 +10 -0
- data/CLAUDE.md +10 -7
- data/README.md +4 -1
- data/lib/legion/extensions/openai/identity.rb +44 -0
- data/lib/legion/extensions/openai/runners/audio.rb +27 -3
- data/lib/legion/extensions/openai/runners/chat.rb +10 -1
- data/lib/legion/extensions/openai/runners/embeddings.rb +10 -1
- data/lib/legion/extensions/openai/runners/files.rb +45 -5
- data/lib/legion/extensions/openai/runners/images.rb +27 -3
- data/lib/legion/extensions/openai/runners/models.rb +27 -3
- data/lib/legion/extensions/openai/runners/moderations.rb +10 -1
- data/lib/legion/extensions/openai/version.rb +1 -1
- data/lib/legion/extensions/openai.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90c6d63dfd5f751c914a536a7392e0d8ddfb9e8c50b06b29d714c9a27f20d958
|
|
4
|
+
data.tar.gz: 734c7f7eb7089efecbe0217b75f1bde7d2203593bd7298715024e1fb12ed4a5b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d85f2b69e108f1b81b8596aed84763f66e9a02b54e981ac2228791ff017ca44238e3afc8d906e3effb47ea6c9217dfc7785b46bb04792cf7fc5bb9bf534c621
|
|
7
|
+
data.tar.gz: b7b973d63e125b00d5503aadcaca25127cc5529779022671af6c9fb2259de630336450cd48243e88cd0169e9c3c0973f978a3423760ec0fa4f6ac3f152eb924f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.5] - 2026-04-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Credential-only identity module for Phase 8 Broker integration (`Identity` module with `provide_token`)
|
|
7
|
+
|
|
8
|
+
## [0.1.4] - 2026-03-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- add standardized usage tracking to all runner responses; all methods now return a `usage:` hash with `input_tokens`, `output_tokens`, `cache_read_tokens`, and `cache_write_tokens` keys compatible with legion-llm's CostEstimator (#2)
|
|
12
|
+
|
|
3
13
|
## [0.1.3] - 2026-03-30
|
|
4
14
|
|
|
5
15
|
### Changed
|
data/CLAUDE.md
CHANGED
|
@@ -10,8 +10,8 @@ Legion Extension that connects LegionIO to OpenAI. Provides runners for chat com
|
|
|
10
10
|
|
|
11
11
|
**GitHub**: https://github.com/LegionIO/lex-openai
|
|
12
12
|
**License**: MIT
|
|
13
|
-
**Version**: 0.1.
|
|
14
|
-
**Specs**:
|
|
13
|
+
**Version**: 0.1.5
|
|
14
|
+
**Specs**: 66 examples (8 spec files)
|
|
15
15
|
|
|
16
16
|
## Architecture
|
|
17
17
|
|
|
@@ -31,15 +31,16 @@ Legion::Extensions::Openai
|
|
|
31
31
|
|
|
32
32
|
There is no standalone `Client` class in lex-openai. Runner modules are used directly via `extend` or by including them in a consuming class. This differs from lex-azure-ai, lex-bedrock, lex-claude, lex-foundry, and lex-xai which all ship a `Client` class.
|
|
33
33
|
|
|
34
|
-
`Helpers::Client` is a **module** (not a class).
|
|
34
|
+
`Helpers::Client` is a **module** (not a class). Runner modules `extend` it so `client(...)` is available as a module-level method. `DEFAULT_BASE_URL = 'https://api.openai.com'`.
|
|
35
35
|
|
|
36
36
|
## Key Design Decisions
|
|
37
37
|
|
|
38
38
|
- `faraday/multipart` is required unconditionally in `Helpers::Client` — the `:multipart` middleware is always loaded. This is a hard dependency (listed in gemspec), unlike lex-gemini where it is optional.
|
|
39
|
-
- Images
|
|
39
|
+
- `Images#edit` and `Images#variation` use `Faraday::Multipart::FilePart` directly.
|
|
40
40
|
- `Images#generate` uses DALL-E 3 by default; `Images#edit` and `Images#variation` use DALL-E 2 by default.
|
|
41
41
|
- Audio defaults: `model: 'tts-1'`, `voice: 'alloy'`, `response_format: 'mp3'` for speech; `model: 'whisper-1'` for transcription/translation.
|
|
42
|
-
- All runners return `{ result: response.body }` (no `:status` key).
|
|
42
|
+
- All runners return `{ result: response.body }` (no `:status` key).
|
|
43
|
+
- `multi_json` is NOT a declared dependency of lex-openai (unlike lex-azure-ai, lex-claude, lex-foundry, lex-xai). JSON parsing uses Faraday's built-in response middleware.
|
|
43
44
|
- `include Legion::Extensions::Helpers::Lex` is guarded with `Legion::Extensions.const_defined?(:Helpers)` pattern.
|
|
44
45
|
|
|
45
46
|
## Dependencies
|
|
@@ -48,17 +49,19 @@ There is no standalone `Client` class in lex-openai. Runner modules are used dir
|
|
|
48
49
|
|-----|---------|
|
|
49
50
|
| `faraday` >= 2.0 | HTTP client |
|
|
50
51
|
| `faraday-multipart` >= 1.0 | Multipart file uploads (images, audio, files) |
|
|
52
|
+
| `legion-cache`, `legion-crypt`, `legion-data`, `legion-json`, `legion-logging`, `legion-settings`, `legion-transport` | LegionIO core |
|
|
51
53
|
|
|
52
|
-
Note: `multi_json` is NOT a declared dependency
|
|
54
|
+
Note: `multi_json` is NOT a declared dependency (differs from all other extensions in this category).
|
|
53
55
|
|
|
54
56
|
## Testing
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
bundle install
|
|
58
|
-
bundle exec rspec #
|
|
60
|
+
bundle exec rspec # 66 examples
|
|
59
61
|
bundle exec rubocop
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
---
|
|
63
65
|
|
|
64
66
|
**Maintained By**: Matthew Iverson (@Esity)
|
|
67
|
+
**Last Updated**: 2026-04-06
|
data/README.md
CHANGED
|
@@ -100,7 +100,6 @@ puts image[:result]['data'].first['url']
|
|
|
100
100
|
|
|
101
101
|
- `faraday` >= 2.0 - HTTP client
|
|
102
102
|
- `faraday-multipart` >= 1.0 - Multipart file uploads (images, audio, files)
|
|
103
|
-
- `multi_json` - JSON parser abstraction
|
|
104
103
|
|
|
105
104
|
## Requirements
|
|
106
105
|
|
|
@@ -114,6 +113,10 @@ puts image[:result]['data'].first['url']
|
|
|
114
113
|
- `legion-llm` — High-level LLM interface including OpenAI via ruby_llm
|
|
115
114
|
- `extensions-ai/CLAUDE.md` — Architecture patterns shared across all AI extensions
|
|
116
115
|
|
|
116
|
+
## Version
|
|
117
|
+
|
|
118
|
+
0.1.5
|
|
119
|
+
|
|
117
120
|
## License
|
|
118
121
|
|
|
119
122
|
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Openai
|
|
6
|
+
module Identity
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def provider_name = :openai
|
|
10
|
+
def provider_type = :credential
|
|
11
|
+
def facing = nil
|
|
12
|
+
def capabilities = %i[credentials]
|
|
13
|
+
|
|
14
|
+
def resolve(canonical_name: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def provide_token
|
|
19
|
+
api_key = resolve_api_key
|
|
20
|
+
return nil unless api_key
|
|
21
|
+
|
|
22
|
+
Legion::Identity::Lease.new(
|
|
23
|
+
provider: :openai,
|
|
24
|
+
credential: api_key,
|
|
25
|
+
expires_at: nil,
|
|
26
|
+
renewable: false,
|
|
27
|
+
issued_at: Time.now,
|
|
28
|
+
metadata: { credential_type: :api_key }
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def resolve_api_key
|
|
33
|
+
return nil unless defined?(Legion::Settings)
|
|
34
|
+
|
|
35
|
+
value = Legion::Settings.dig(:llm, :providers, :openai, :api_key)
|
|
36
|
+
value = value.find { |v| v && !v.empty? } if value.is_a?(Array)
|
|
37
|
+
value unless value.nil? || (value.is_a?(String) && (value.empty? || value.start_with?('env://')))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private_class_method :resolve_api_key
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -14,7 +14,15 @@ module Legion
|
|
|
14
14
|
body[:speed] = speed if speed
|
|
15
15
|
|
|
16
16
|
response = client(api_key: api_key, **).post('/v1/audio/speech', body)
|
|
17
|
-
{
|
|
17
|
+
{
|
|
18
|
+
result: response.body,
|
|
19
|
+
usage: {
|
|
20
|
+
input_tokens: 0,
|
|
21
|
+
output_tokens: 0,
|
|
22
|
+
cache_read_tokens: 0,
|
|
23
|
+
cache_write_tokens: 0
|
|
24
|
+
}
|
|
25
|
+
}
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def transcribe(file:, api_key:, model: 'whisper-1', language: nil, prompt: nil, response_format: nil, **)
|
|
@@ -27,7 +35,15 @@ module Legion
|
|
|
27
35
|
payload[:response_format] = response_format if response_format
|
|
28
36
|
|
|
29
37
|
response = client(api_key: api_key, **).post('/v1/audio/transcriptions', payload)
|
|
30
|
-
{
|
|
38
|
+
{
|
|
39
|
+
result: response.body,
|
|
40
|
+
usage: {
|
|
41
|
+
input_tokens: 0,
|
|
42
|
+
output_tokens: 0,
|
|
43
|
+
cache_read_tokens: 0,
|
|
44
|
+
cache_write_tokens: 0
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
end
|
|
32
48
|
|
|
33
49
|
def translate(file:, api_key:, model: 'whisper-1', prompt: nil, response_format: nil, **)
|
|
@@ -39,7 +55,15 @@ module Legion
|
|
|
39
55
|
payload[:response_format] = response_format if response_format
|
|
40
56
|
|
|
41
57
|
response = client(api_key: api_key, **).post('/v1/audio/translations', payload)
|
|
42
|
-
{
|
|
58
|
+
{
|
|
59
|
+
result: response.body,
|
|
60
|
+
usage: {
|
|
61
|
+
input_tokens: 0,
|
|
62
|
+
output_tokens: 0,
|
|
63
|
+
cache_read_tokens: 0,
|
|
64
|
+
cache_write_tokens: 0
|
|
65
|
+
}
|
|
66
|
+
}
|
|
43
67
|
end
|
|
44
68
|
|
|
45
69
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -20,7 +20,16 @@ module Legion
|
|
|
20
20
|
body[:stop] = stop if stop
|
|
21
21
|
|
|
22
22
|
response = client(api_key: api_key, **).post('/v1/chat/completions', body)
|
|
23
|
-
|
|
23
|
+
resp_body = response.body
|
|
24
|
+
{
|
|
25
|
+
result: resp_body,
|
|
26
|
+
usage: {
|
|
27
|
+
input_tokens: resp_body.dig('usage', 'prompt_tokens') || 0,
|
|
28
|
+
output_tokens: resp_body.dig('usage', 'completion_tokens') || 0,
|
|
29
|
+
cache_read_tokens: resp_body.dig('usage', 'prompt_tokens_details', 'cached_tokens') || 0,
|
|
30
|
+
cache_write_tokens: 0
|
|
31
|
+
}
|
|
32
|
+
}
|
|
24
33
|
end
|
|
25
34
|
|
|
26
35
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -15,7 +15,16 @@ module Legion
|
|
|
15
15
|
body[:dimensions] = dimensions if dimensions
|
|
16
16
|
|
|
17
17
|
response = client(api_key: api_key, **).post('/v1/embeddings', body)
|
|
18
|
-
|
|
18
|
+
resp_body = response.body
|
|
19
|
+
{
|
|
20
|
+
result: resp_body,
|
|
21
|
+
usage: {
|
|
22
|
+
input_tokens: resp_body.dig('usage', 'prompt_tokens') || 0,
|
|
23
|
+
output_tokens: resp_body.dig('usage', 'completion_tokens') || 0,
|
|
24
|
+
cache_read_tokens: resp_body.dig('usage', 'prompt_tokens_details', 'cached_tokens') || 0,
|
|
25
|
+
cache_write_tokens: 0
|
|
26
|
+
}
|
|
27
|
+
}
|
|
19
28
|
end
|
|
20
29
|
|
|
21
30
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -14,7 +14,15 @@ module Legion
|
|
|
14
14
|
path += "?purpose=#{purpose}" if purpose
|
|
15
15
|
|
|
16
16
|
response = client(api_key: api_key, **).get(path)
|
|
17
|
-
{
|
|
17
|
+
{
|
|
18
|
+
result: response.body,
|
|
19
|
+
usage: {
|
|
20
|
+
input_tokens: 0,
|
|
21
|
+
output_tokens: 0,
|
|
22
|
+
cache_read_tokens: 0,
|
|
23
|
+
cache_write_tokens: 0
|
|
24
|
+
}
|
|
25
|
+
}
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def upload(file:, purpose:, api_key:, **)
|
|
@@ -24,22 +32,54 @@ module Legion
|
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
response = client(api_key: api_key, **).post('/v1/files', payload)
|
|
27
|
-
{
|
|
35
|
+
{
|
|
36
|
+
result: response.body,
|
|
37
|
+
usage: {
|
|
38
|
+
input_tokens: 0,
|
|
39
|
+
output_tokens: 0,
|
|
40
|
+
cache_read_tokens: 0,
|
|
41
|
+
cache_write_tokens: 0
|
|
42
|
+
}
|
|
43
|
+
}
|
|
28
44
|
end
|
|
29
45
|
|
|
30
46
|
def retrieve(file_id:, api_key:, **)
|
|
31
47
|
response = client(api_key: api_key, **).get("/v1/files/#{file_id}")
|
|
32
|
-
{
|
|
48
|
+
{
|
|
49
|
+
result: response.body,
|
|
50
|
+
usage: {
|
|
51
|
+
input_tokens: 0,
|
|
52
|
+
output_tokens: 0,
|
|
53
|
+
cache_read_tokens: 0,
|
|
54
|
+
cache_write_tokens: 0
|
|
55
|
+
}
|
|
56
|
+
}
|
|
33
57
|
end
|
|
34
58
|
|
|
35
59
|
def delete(file_id:, api_key:, **)
|
|
36
60
|
response = client(api_key: api_key, **).delete("/v1/files/#{file_id}")
|
|
37
|
-
{
|
|
61
|
+
{
|
|
62
|
+
result: response.body,
|
|
63
|
+
usage: {
|
|
64
|
+
input_tokens: 0,
|
|
65
|
+
output_tokens: 0,
|
|
66
|
+
cache_read_tokens: 0,
|
|
67
|
+
cache_write_tokens: 0
|
|
68
|
+
}
|
|
69
|
+
}
|
|
38
70
|
end
|
|
39
71
|
|
|
40
72
|
def content(file_id:, api_key:, **)
|
|
41
73
|
response = client(api_key: api_key, **).get("/v1/files/#{file_id}/content")
|
|
42
|
-
{
|
|
74
|
+
{
|
|
75
|
+
result: response.body,
|
|
76
|
+
usage: {
|
|
77
|
+
input_tokens: 0,
|
|
78
|
+
output_tokens: 0,
|
|
79
|
+
cache_read_tokens: 0,
|
|
80
|
+
cache_write_tokens: 0
|
|
81
|
+
}
|
|
82
|
+
}
|
|
43
83
|
end
|
|
44
84
|
|
|
45
85
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -17,7 +17,15 @@ module Legion
|
|
|
17
17
|
body[:response_format] = response_format if response_format
|
|
18
18
|
|
|
19
19
|
response = client(api_key: api_key, **).post('/v1/images/generations', body)
|
|
20
|
-
{
|
|
20
|
+
{
|
|
21
|
+
result: response.body,
|
|
22
|
+
usage: {
|
|
23
|
+
input_tokens: 0,
|
|
24
|
+
output_tokens: 0,
|
|
25
|
+
cache_read_tokens: 0,
|
|
26
|
+
cache_write_tokens: 0
|
|
27
|
+
}
|
|
28
|
+
}
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
def edit(image:, prompt:, api_key:, model: 'dall-e-2', mask: nil, n: 1, size: '1024x1024', **)
|
|
@@ -31,7 +39,15 @@ module Legion
|
|
|
31
39
|
payload[:mask] = Faraday::Multipart::FilePart.new(mask, 'image/png') if mask
|
|
32
40
|
|
|
33
41
|
response = client(api_key: api_key, **).post('/v1/images/edits', payload)
|
|
34
|
-
{
|
|
42
|
+
{
|
|
43
|
+
result: response.body,
|
|
44
|
+
usage: {
|
|
45
|
+
input_tokens: 0,
|
|
46
|
+
output_tokens: 0,
|
|
47
|
+
cache_read_tokens: 0,
|
|
48
|
+
cache_write_tokens: 0
|
|
49
|
+
}
|
|
50
|
+
}
|
|
35
51
|
end
|
|
36
52
|
|
|
37
53
|
def variation(image:, api_key:, model: 'dall-e-2', n: 1, size: '1024x1024', **)
|
|
@@ -43,7 +59,15 @@ module Legion
|
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
response = client(api_key: api_key, **).post('/v1/images/variations', payload)
|
|
46
|
-
{
|
|
62
|
+
{
|
|
63
|
+
result: response.body,
|
|
64
|
+
usage: {
|
|
65
|
+
input_tokens: 0,
|
|
66
|
+
output_tokens: 0,
|
|
67
|
+
cache_read_tokens: 0,
|
|
68
|
+
cache_write_tokens: 0
|
|
69
|
+
}
|
|
70
|
+
}
|
|
47
71
|
end
|
|
48
72
|
|
|
49
73
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -11,17 +11,41 @@ module Legion
|
|
|
11
11
|
|
|
12
12
|
def list(api_key:, **)
|
|
13
13
|
response = client(api_key: api_key, **).get('/v1/models')
|
|
14
|
-
{
|
|
14
|
+
{
|
|
15
|
+
result: response.body,
|
|
16
|
+
usage: {
|
|
17
|
+
input_tokens: 0,
|
|
18
|
+
output_tokens: 0,
|
|
19
|
+
cache_read_tokens: 0,
|
|
20
|
+
cache_write_tokens: 0
|
|
21
|
+
}
|
|
22
|
+
}
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def retrieve(model:, api_key:, **)
|
|
18
26
|
response = client(api_key: api_key, **).get("/v1/models/#{model}")
|
|
19
|
-
{
|
|
27
|
+
{
|
|
28
|
+
result: response.body,
|
|
29
|
+
usage: {
|
|
30
|
+
input_tokens: 0,
|
|
31
|
+
output_tokens: 0,
|
|
32
|
+
cache_read_tokens: 0,
|
|
33
|
+
cache_write_tokens: 0
|
|
34
|
+
}
|
|
35
|
+
}
|
|
20
36
|
end
|
|
21
37
|
|
|
22
38
|
def delete(model:, api_key:, **)
|
|
23
39
|
response = client(api_key: api_key, **).delete("/v1/models/#{model}")
|
|
24
|
-
{
|
|
40
|
+
{
|
|
41
|
+
result: response.body,
|
|
42
|
+
usage: {
|
|
43
|
+
input_tokens: 0,
|
|
44
|
+
output_tokens: 0,
|
|
45
|
+
cache_read_tokens: 0,
|
|
46
|
+
cache_write_tokens: 0
|
|
47
|
+
}
|
|
48
|
+
}
|
|
25
49
|
end
|
|
26
50
|
|
|
27
51
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -14,7 +14,16 @@ module Legion
|
|
|
14
14
|
body[:model] = model if model
|
|
15
15
|
|
|
16
16
|
response = client(api_key: api_key, **).post('/v1/moderations', body)
|
|
17
|
-
|
|
17
|
+
resp_body = response.body
|
|
18
|
+
{
|
|
19
|
+
result: resp_body,
|
|
20
|
+
usage: {
|
|
21
|
+
input_tokens: resp_body.dig('usage', 'prompt_tokens') || 0,
|
|
22
|
+
output_tokens: resp_body.dig('usage', 'completion_tokens') || 0,
|
|
23
|
+
cache_read_tokens: resp_body.dig('usage', 'prompt_tokens_details', 'cached_tokens') || 0,
|
|
24
|
+
cache_write_tokens: 0
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
end
|
|
19
28
|
|
|
20
29
|
include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
|
|
@@ -9,6 +9,7 @@ require 'legion/extensions/openai/runners/audio'
|
|
|
9
9
|
require 'legion/extensions/openai/runners/embeddings'
|
|
10
10
|
require 'legion/extensions/openai/runners/files'
|
|
11
11
|
require 'legion/extensions/openai/runners/moderations'
|
|
12
|
+
require 'legion/extensions/openai/identity'
|
|
12
13
|
|
|
13
14
|
module Legion
|
|
14
15
|
module Extensions
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-openai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -157,6 +157,7 @@ files:
|
|
|
157
157
|
- lex-openai.gemspec
|
|
158
158
|
- lib/legion/extensions/openai.rb
|
|
159
159
|
- lib/legion/extensions/openai/helpers/client.rb
|
|
160
|
+
- lib/legion/extensions/openai/identity.rb
|
|
160
161
|
- lib/legion/extensions/openai/runners/audio.rb
|
|
161
162
|
- lib/legion/extensions/openai/runners/chat.rb
|
|
162
163
|
- lib/legion/extensions/openai/runners/embeddings.rb
|