lex-gemini 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 +12 -0
- data/CLAUDE.md +10 -7
- data/README.md +4 -0
- data/lib/legion/extensions/gemini/helpers/usage.rb +28 -0
- data/lib/legion/extensions/gemini/identity.rb +44 -0
- data/lib/legion/extensions/gemini/runners/cached_contents.rb +12 -6
- data/lib/legion/extensions/gemini/runners/content.rb +8 -5
- data/lib/legion/extensions/gemini/runners/embeddings.rb +5 -2
- data/lib/legion/extensions/gemini/runners/files.rb +9 -4
- data/lib/legion/extensions/gemini/runners/models.rb +5 -2
- data/lib/legion/extensions/gemini/runners/tokens.rb +3 -1
- data/lib/legion/extensions/gemini/version.rb +1 -1
- data/lib/legion/extensions/gemini.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae34d00ad4e874b1b0b71979f4d7fe34632681f7b422626da992fa0ebe42b944
|
|
4
|
+
data.tar.gz: 2943e187a68e5759f67ae512b28e3a8b151307f5e53991d8cef54d67269d3791
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 417aa550be8ad9fa013f66046a264fd52fd9ea60d8ea2f2be6bc9eaae2f8881fd24936c087d0573059b686c9f34f5c6fab1a2bf73868f44f66c55f566f4abd00
|
|
7
|
+
data.tar.gz: 2b6fdc4f9ad52e63bcb2eac57c4fcac09b0a1e92eeab822636b7e667d80919f9c0a813c8d1de5dae22aa9ff424128ded13218d9fe956ee726ef2790b45bc4610
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
- `Helpers::Usage` module with `extract_usage` helper that parses `usageMetadata` from Gemini API responses
|
|
12
|
+
- All runner methods now return a `usage:` key with `input_tokens`, `output_tokens`, `cache_read_tokens`, and `cache_write_tokens`
|
|
13
|
+
- Usage specs for all runners and the new `Helpers::Usage` module (80 examples total, up from 36)
|
|
14
|
+
|
|
3
15
|
## [0.1.3] - 2026-03-30
|
|
4
16
|
|
|
5
17
|
### Changed
|
data/CLAUDE.md
CHANGED
|
@@ -10,8 +10,8 @@ Legion Extension that connects LegionIO to Google Gemini API. Generate content,
|
|
|
10
10
|
|
|
11
11
|
**GitHub**: https://github.com/LegionIO/lex-gemini
|
|
12
12
|
**License**: MIT
|
|
13
|
-
**Version**: 0.1.
|
|
14
|
-
**Specs**:
|
|
13
|
+
**Version**: 0.1.5
|
|
14
|
+
**Specs**: 88 examples (9 spec files)
|
|
15
15
|
|
|
16
16
|
## Architecture
|
|
17
17
|
|
|
@@ -25,19 +25,20 @@ Legion::Extensions::Gemini
|
|
|
25
25
|
│ ├── Files # upload(api_key:, file_path:, mime_type:, ...), list, get, delete
|
|
26
26
|
│ └── CachedContents # create(api_key:, model:, contents:, ...), list, get, update, delete
|
|
27
27
|
└── Helpers/
|
|
28
|
-
|
|
28
|
+
├── Client # Faraday-based HTTP client (class, instantiated per-request)
|
|
29
|
+
└── Usage # usage normalization helpers
|
|
29
30
|
```
|
|
30
31
|
|
|
31
|
-
Unlike
|
|
32
|
+
Unlike other extensions in this category, `Helpers::Client` is a **class** instantiated per-request. Each runner creates `Helpers::Client.new(api_key:, model:)` inline. No module-level `extend` is used in runners — runners call `Helpers::Client.new(...)` directly.
|
|
32
33
|
|
|
33
|
-
`include Legion::Extensions::Helpers::Lex` guard: uses `if defined?(Legion::Extensions::Helpers::Lex)` (note: slightly different guard pattern from
|
|
34
|
+
`include Legion::Extensions::Helpers::Lex` guard: uses `if defined?(Legion::Extensions::Helpers::Lex)` (note: slightly different guard pattern from other extensions which use `const_defined?`).
|
|
34
35
|
|
|
35
36
|
## Key Design Decisions
|
|
36
37
|
|
|
37
38
|
- `Helpers::Client` is a class to allow per-request model selection without global state.
|
|
38
39
|
- Authentication uses query parameter `?key=<api_key>` set on the Faraday connection params, so all requests automatically include it.
|
|
39
40
|
- File upload falls back to raw binary upload (`X-Goog-Upload-Protocol: raw`) when `faraday-multipart` is not loaded.
|
|
40
|
-
- `Helpers::Client#handle_response` returns the raw body on success and `{ error: body, status: code }` on failure. All runners wrap the
|
|
41
|
+
- `Helpers::Client#handle_response` returns the raw body on success and `{ error: body, status: code }` on failure. All runners wrap the return value in `{ result: ... }`.
|
|
41
42
|
- `gemini-2.0-flash` is the default model for Content, Tokens, and `Helpers::Client` initialization.
|
|
42
43
|
- `gemini-embedding-exp` is the default model for Embeddings runners.
|
|
43
44
|
|
|
@@ -51,15 +52,17 @@ Unlike lex-claude and lex-openai, `Helpers::Client` is a **class** instantiated
|
|
|
51
52
|
|-----|---------|
|
|
52
53
|
| `faraday` >= 2.0 | HTTP client for Gemini REST API |
|
|
53
54
|
| `faraday-multipart` | File uploads (optional — falls back to raw binary upload if not available) |
|
|
55
|
+
| `legion-cache`, `legion-crypt`, `legion-data`, `legion-json`, `legion-logging`, `legion-settings`, `legion-transport` | LegionIO core |
|
|
54
56
|
|
|
55
57
|
## Testing
|
|
56
58
|
|
|
57
59
|
```bash
|
|
58
60
|
bundle install
|
|
59
|
-
bundle exec rspec #
|
|
61
|
+
bundle exec rspec # 88 examples
|
|
60
62
|
bundle exec rubocop
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
---
|
|
64
66
|
|
|
65
67
|
**Maintained By**: Matthew Iverson (@Esity)
|
|
68
|
+
**Last Updated**: 2026-04-06
|
data/README.md
CHANGED
|
@@ -109,6 +109,10 @@ puts tokens['totalTokens']
|
|
|
109
109
|
- `legion-llm` — High-level LLM interface including Gemini via ruby_llm
|
|
110
110
|
- `extensions-ai/CLAUDE.md` — Architecture patterns shared across all AI extensions
|
|
111
111
|
|
|
112
|
+
## Version
|
|
113
|
+
|
|
114
|
+
0.1.5
|
|
115
|
+
|
|
112
116
|
## License
|
|
113
117
|
|
|
114
118
|
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Helpers
|
|
7
|
+
module Usage
|
|
8
|
+
def extract_usage(body)
|
|
9
|
+
return zero_usage unless body.is_a?(Hash)
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
input_tokens: body.dig('usageMetadata', 'promptTokenCount') || 0,
|
|
13
|
+
output_tokens: body.dig('usageMetadata', 'candidatesTokenCount') || 0,
|
|
14
|
+
cache_read_tokens: body.dig('usageMetadata', 'cachedContentTokenCount') || 0,
|
|
15
|
+
cache_write_tokens: 0
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def zero_usage
|
|
22
|
+
{ input_tokens: 0, output_tokens: 0, cache_read_tokens: 0, cache_write_tokens: 0 }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Identity
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def provider_name = :gemini
|
|
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: :gemini,
|
|
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, :gemini, :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
|
|
@@ -6,31 +6,37 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module CachedContents
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def create(api_key:, model:, contents:, ttl: nil, expire_time: nil, display_name: nil, system_instruction: nil, **)
|
|
11
12
|
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
body = client.create_cached_content(model: model, contents: contents, ttl: ttl, expire_time: expire_time,
|
|
14
|
+
display_name: display_name, system_instruction: system_instruction)
|
|
15
|
+
{ result: body, usage: extract_usage(body) }
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def list(api_key:, page_size: nil, page_token: nil, **)
|
|
17
19
|
client = Helpers::Client.new(api_key: api_key)
|
|
18
|
-
|
|
20
|
+
body = client.list_cached_contents(page_size: page_size, page_token: page_token)
|
|
21
|
+
{ result: body, usage: extract_usage(body) }
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def get(api_key:, name:, **)
|
|
22
25
|
client = Helpers::Client.new(api_key: api_key)
|
|
23
|
-
|
|
26
|
+
body = client.get_cached_content(name: name)
|
|
27
|
+
{ result: body, usage: extract_usage(body) }
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
def update(api_key:, name:, ttl: nil, expire_time: nil, **)
|
|
27
31
|
client = Helpers::Client.new(api_key: api_key)
|
|
28
|
-
|
|
32
|
+
body = client.update_cached_content(name: name, ttl: ttl, expire_time: expire_time)
|
|
33
|
+
{ result: body, usage: extract_usage(body) }
|
|
29
34
|
end
|
|
30
35
|
|
|
31
36
|
def delete(api_key:, name:, **)
|
|
32
37
|
client = Helpers::Client.new(api_key: api_key)
|
|
33
|
-
|
|
38
|
+
body = client.delete_cached_content(name: name)
|
|
39
|
+
{ result: body, usage: extract_usage(body) }
|
|
34
40
|
end
|
|
35
41
|
end
|
|
36
42
|
end
|
|
@@ -6,20 +6,23 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module Content
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def generate(api_key:, contents:, model: 'gemini-2.0-flash', generation_config: nil, safety_settings: nil,
|
|
11
12
|
system_instruction: nil, **)
|
|
12
13
|
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
body = client.generate_content(contents: contents, generation_config: generation_config,
|
|
15
|
+
safety_settings: safety_settings, system_instruction: system_instruction)
|
|
16
|
+
{ result: body, usage: extract_usage(body) }
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def stream_generate(api_key:, contents:, model: 'gemini-2.0-flash', generation_config: nil, safety_settings: nil,
|
|
18
20
|
system_instruction: nil, **)
|
|
19
21
|
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
body = client.stream_generate_content(contents: contents, generation_config: generation_config,
|
|
23
|
+
safety_settings: safety_settings,
|
|
24
|
+
system_instruction: system_instruction)
|
|
25
|
+
{ result: body, usage: extract_usage(body) }
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
@@ -6,15 +6,18 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module Embeddings
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def embed(api_key:, content:, model: 'gemini-embedding-exp', task_type: nil, title: nil, **)
|
|
11
12
|
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
12
|
-
|
|
13
|
+
body = client.embed_content(content: content, task_type: task_type, title: title)
|
|
14
|
+
{ result: body, usage: extract_usage(body) }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def batch_embed(api_key:, requests:, model: 'gemini-embedding-exp', **)
|
|
16
18
|
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
17
|
-
|
|
19
|
+
body = client.batch_embed_contents(requests: requests)
|
|
20
|
+
{ result: body, usage: extract_usage(body) }
|
|
18
21
|
end
|
|
19
22
|
end
|
|
20
23
|
end
|
|
@@ -6,25 +6,30 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module Files
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def upload(api_key:, file_path:, mime_type:, display_name: nil, **)
|
|
11
12
|
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
-
|
|
13
|
+
body = client.upload_file(file_path: file_path, mime_type: mime_type, display_name: display_name)
|
|
14
|
+
{ result: body, usage: extract_usage(body) }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def list(api_key:, page_size: nil, page_token: nil, **)
|
|
16
18
|
client = Helpers::Client.new(api_key: api_key)
|
|
17
|
-
|
|
19
|
+
body = client.list_files(page_size: page_size, page_token: page_token)
|
|
20
|
+
{ result: body, usage: extract_usage(body) }
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def get(api_key:, name:, **)
|
|
21
24
|
client = Helpers::Client.new(api_key: api_key)
|
|
22
|
-
|
|
25
|
+
body = client.get_file(name: name)
|
|
26
|
+
{ result: body, usage: extract_usage(body) }
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
def delete(api_key:, name:, **)
|
|
26
30
|
client = Helpers::Client.new(api_key: api_key)
|
|
27
|
-
|
|
31
|
+
body = client.delete_file(name: name)
|
|
32
|
+
{ result: body, usage: extract_usage(body) }
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
end
|
|
@@ -6,15 +6,18 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module Models
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def list(api_key:, **)
|
|
11
12
|
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
-
|
|
13
|
+
body = client.list_models
|
|
14
|
+
{ result: body, usage: extract_usage(body) }
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def get(api_key:, name:, **)
|
|
16
18
|
client = Helpers::Client.new(api_key: api_key)
|
|
17
|
-
|
|
19
|
+
body = client.get_model(name: name)
|
|
20
|
+
{ result: body, usage: extract_usage(body) }
|
|
18
21
|
end
|
|
19
22
|
end
|
|
20
23
|
end
|
|
@@ -6,10 +6,12 @@ module Legion
|
|
|
6
6
|
module Runners
|
|
7
7
|
module Tokens
|
|
8
8
|
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
include Helpers::Usage
|
|
9
10
|
|
|
10
11
|
def count(api_key:, contents:, model: 'gemini-2.0-flash', **)
|
|
11
12
|
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
12
|
-
|
|
13
|
+
body = client.count_tokens(contents: contents)
|
|
14
|
+
{ result: body, usage: extract_usage(body) }
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require 'legion/extensions/gemini/version'
|
|
4
4
|
require 'legion/extensions/gemini/helpers/client'
|
|
5
|
+
require 'legion/extensions/gemini/helpers/usage'
|
|
5
6
|
require 'legion/extensions/gemini/runners/content'
|
|
6
7
|
require 'legion/extensions/gemini/runners/embeddings'
|
|
7
8
|
require 'legion/extensions/gemini/runners/models'
|
|
8
9
|
require 'legion/extensions/gemini/runners/tokens'
|
|
9
10
|
require 'legion/extensions/gemini/runners/files'
|
|
10
11
|
require 'legion/extensions/gemini/runners/cached_contents'
|
|
12
|
+
require 'legion/extensions/gemini/identity'
|
|
11
13
|
|
|
12
14
|
module Legion
|
|
13
15
|
module Extensions
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-gemini
|
|
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
|
|
@@ -143,6 +143,8 @@ files:
|
|
|
143
143
|
- lex-gemini.gemspec
|
|
144
144
|
- lib/legion/extensions/gemini.rb
|
|
145
145
|
- lib/legion/extensions/gemini/helpers/client.rb
|
|
146
|
+
- lib/legion/extensions/gemini/helpers/usage.rb
|
|
147
|
+
- lib/legion/extensions/gemini/identity.rb
|
|
146
148
|
- lib/legion/extensions/gemini/runners/cached_contents.rb
|
|
147
149
|
- lib/legion/extensions/gemini/runners/content.rb
|
|
148
150
|
- lib/legion/extensions/gemini/runners/embeddings.rb
|