lex-gemini 0.1.1
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 +7 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +54 -0
- data/CHANGELOG.md +11 -0
- data/CLAUDE.md +65 -0
- data/Dockerfile +6 -0
- data/Gemfile +12 -0
- data/LICENSE +21 -0
- data/README.md +105 -0
- data/lex-gemini.gemspec +30 -0
- data/lib/legion/extensions/gemini/helpers/client.rb +176 -0
- data/lib/legion/extensions/gemini/runners/cached_contents.rb +39 -0
- data/lib/legion/extensions/gemini/runners/content.rb +28 -0
- data/lib/legion/extensions/gemini/runners/embeddings.rb +23 -0
- data/lib/legion/extensions/gemini/runners/files.rb +33 -0
- data/lib/legion/extensions/gemini/runners/models.rb +23 -0
- data/lib/legion/extensions/gemini/runners/tokens.rb +18 -0
- data/lib/legion/extensions/gemini/version.rb +9 -0
- data/lib/legion/extensions/gemini.rb +18 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3fc603fd61048eacb62856e1d69d9f3a91bea5bc0af2fd6fa302021a6f54b2f1
|
|
4
|
+
data.tar.gz: 2019858b1f1587e40b4d71aaa13d0cdf8b06a3202e3d0313690da4add0bf24c3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f72c04be03d39391f3afeb17fee0440840c75852b9fb6bb6ca17625ba86aa5f1a2a87e2dfe72fe94342621af70d6dfa168f64e78e935f5885ef4bb34c9dbc887
|
|
7
|
+
data.tar.gz: c68e1688f7d9f07a34e17fa1d8fb553769d8bb98bf441e21069bf32cea38bc7b49d1baaea5318eaeac9d0bbd1a060d37c676daf1c39a1c61405a0ccb6461b85a
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
ci:
|
|
9
|
+
uses: LegionIO/.github/.github/workflows/ci.yml@main
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
needs: ci
|
|
13
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
14
|
+
uses: LegionIO/.github/.github/workflows/release.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
rubygems-api-key: ${{ secrets.RUBYGEMS_API_KEY }}
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.4
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Layout/LineLength:
|
|
7
|
+
Max: 160
|
|
8
|
+
|
|
9
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
10
|
+
EnforcedStyle: space
|
|
11
|
+
|
|
12
|
+
Layout/HashAlignment:
|
|
13
|
+
EnforcedHashRocketStyle: table
|
|
14
|
+
EnforcedColonStyle: table
|
|
15
|
+
|
|
16
|
+
Metrics/MethodLength:
|
|
17
|
+
Max: 50
|
|
18
|
+
|
|
19
|
+
Metrics/ClassLength:
|
|
20
|
+
Max: 1500
|
|
21
|
+
|
|
22
|
+
Metrics/ModuleLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/BlockLength:
|
|
26
|
+
Max: 40
|
|
27
|
+
Exclude:
|
|
28
|
+
- 'spec/**/*'
|
|
29
|
+
|
|
30
|
+
Metrics/AbcSize:
|
|
31
|
+
Max: 60
|
|
32
|
+
|
|
33
|
+
Metrics/CyclomaticComplexity:
|
|
34
|
+
Max: 15
|
|
35
|
+
|
|
36
|
+
Metrics/PerceivedComplexity:
|
|
37
|
+
Max: 17
|
|
38
|
+
|
|
39
|
+
Style/Documentation:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
Style/SymbolArray:
|
|
43
|
+
Enabled: true
|
|
44
|
+
|
|
45
|
+
Style/FrozenStringLiteralComment:
|
|
46
|
+
Enabled: true
|
|
47
|
+
EnforcedStyle: always
|
|
48
|
+
|
|
49
|
+
Metrics/ParameterLists:
|
|
50
|
+
Max: 10
|
|
51
|
+
CountKeywordArgs: false
|
|
52
|
+
|
|
53
|
+
Naming/FileName:
|
|
54
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# lex-gemini: Google Gemini API Integration for LegionIO
|
|
2
|
+
|
|
3
|
+
**Repository Level 3 Documentation**
|
|
4
|
+
- **Parent**: `/Users/miverso2/rubymine/legion/extensions-ai/CLAUDE.md`
|
|
5
|
+
- **Grandparent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Legion Extension that connects LegionIO to Google Gemini API. Generate content, create embeddings, manage files, count tokens, and cache content.
|
|
10
|
+
|
|
11
|
+
**GitHub**: https://github.com/LegionIO/lex-gemini
|
|
12
|
+
**License**: MIT
|
|
13
|
+
**Version**: 0.1.0
|
|
14
|
+
**Specs**: 36 examples
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Legion::Extensions::Gemini
|
|
20
|
+
├── Runners/
|
|
21
|
+
│ ├── Content # generate(api_key:, contents:, model:, ...), stream_generate(...)
|
|
22
|
+
│ ├── Embeddings # embed(api_key:, content:, model:, ...), batch_embed(...)
|
|
23
|
+
│ ├── Models # list(api_key:, ...), get(api_key:, name:, ...)
|
|
24
|
+
│ ├── Tokens # count(api_key:, contents:, model:, ...)
|
|
25
|
+
│ ├── Files # upload(api_key:, file_path:, mime_type:, ...), list, get, delete
|
|
26
|
+
│ └── CachedContents # create(api_key:, model:, contents:, ...), list, get, update, delete
|
|
27
|
+
└── Helpers/
|
|
28
|
+
└── Client # Faraday-based HTTP client (class, instantiated per-request)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Unlike lex-claude and lex-openai, `Helpers::Client` is a **class** instantiated per-request. Each runner creates `Helpers::Client.new(api_key:, model:)` inline. This means no module-level `extend` is used in runners; instead, runners call `Helpers::Client.new(...)` directly.
|
|
32
|
+
|
|
33
|
+
`include Legion::Extensions::Helpers::Lex` guard: uses `if defined?(Legion::Extensions::Helpers::Lex)` (note: slightly different guard pattern from lex-claude/lex-openai which use `const_defined?`).
|
|
34
|
+
|
|
35
|
+
## Key Design Decisions
|
|
36
|
+
|
|
37
|
+
- `Helpers::Client` is a class to allow per-request model selection without global state.
|
|
38
|
+
- Authentication uses query parameter `?key=<api_key>` set on the Faraday connection params, so all requests automatically include it.
|
|
39
|
+
- 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:, status: }` on failure (unlike lex-claude/lex-openai which always return `{ result:, status: }`).
|
|
41
|
+
- `gemini-2.0-flash` is the default model for Content, Tokens, and `Helpers::Client` initialization.
|
|
42
|
+
- `gemini-embedding-exp` is the default model for Embeddings runners.
|
|
43
|
+
|
|
44
|
+
## API Base URL
|
|
45
|
+
|
|
46
|
+
`https://generativelanguage.googleapis.com` — path prefix `v1beta` is prepended per-request in `Helpers::Client`.
|
|
47
|
+
|
|
48
|
+
## Dependencies
|
|
49
|
+
|
|
50
|
+
| Gem | Purpose |
|
|
51
|
+
|-----|---------|
|
|
52
|
+
| `faraday` >= 2.0 | HTTP client for Gemini REST API |
|
|
53
|
+
| `faraday-multipart` | File uploads (optional — falls back to raw binary upload if not available) |
|
|
54
|
+
|
|
55
|
+
## Testing
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bundle install
|
|
59
|
+
bundle exec rspec # 36 examples
|
|
60
|
+
bundle exec rubocop
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
**Maintained By**: Matthew Iverson (@Esity)
|
data/Dockerfile
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Esity
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# lex-gemini
|
|
2
|
+
|
|
3
|
+
Google Gemini API integration for [LegionIO](https://github.com/LegionIO/LegionIO). Generate content, create embeddings, manage files, count tokens, and cache content using Google's Gemini models.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install lex-gemini
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or add to your Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'lex-gemini'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Functions
|
|
18
|
+
|
|
19
|
+
### Content
|
|
20
|
+
- `generate` - Generate text content using Gemini models
|
|
21
|
+
- `stream_generate` - Stream generated content
|
|
22
|
+
|
|
23
|
+
### Embeddings
|
|
24
|
+
- `embed` - Generate embedding vectors from text
|
|
25
|
+
- `batch_embed` - Generate multiple embeddings in one request
|
|
26
|
+
|
|
27
|
+
### Models
|
|
28
|
+
- `list` - List available Gemini models
|
|
29
|
+
- `get` - Get details about a specific model
|
|
30
|
+
|
|
31
|
+
### Tokens
|
|
32
|
+
- `count` - Count tokens in content
|
|
33
|
+
|
|
34
|
+
### Files
|
|
35
|
+
- `upload` - Upload a file for use with Gemini
|
|
36
|
+
- `list` - List uploaded files
|
|
37
|
+
- `get` - Get file metadata
|
|
38
|
+
- `delete` - Delete an uploaded file
|
|
39
|
+
|
|
40
|
+
### Cached Contents
|
|
41
|
+
- `create` - Create cached content for repeated use
|
|
42
|
+
- `list` - List cached contents
|
|
43
|
+
- `get` - Get cached content details
|
|
44
|
+
- `update` - Update cached content expiration
|
|
45
|
+
- `delete` - Delete cached content
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Set your API key in your LegionIO settings:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"gemini": {
|
|
54
|
+
"api_key": "AIza..."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Standalone Usage
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
require 'legion/extensions/gemini/helpers/client'
|
|
63
|
+
|
|
64
|
+
client = Legion::Extensions::Gemini::Helpers::Client.new(
|
|
65
|
+
api_key: ENV['GEMINI_API_KEY'],
|
|
66
|
+
model: 'gemini-2.0-flash'
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Generate content
|
|
70
|
+
result = client.generate_content(
|
|
71
|
+
contents: [{ parts: [{ text: 'Explain quantum entanglement in one sentence.' }] }]
|
|
72
|
+
)
|
|
73
|
+
puts result.dig('candidates', 0, 'content', 'parts', 0, 'text')
|
|
74
|
+
|
|
75
|
+
# Generate embeddings
|
|
76
|
+
embedding_client = Legion::Extensions::Gemini::Helpers::Client.new(
|
|
77
|
+
api_key: ENV['GEMINI_API_KEY'],
|
|
78
|
+
model: 'gemini-embedding-exp'
|
|
79
|
+
)
|
|
80
|
+
embedding = embedding_client.embed_content(
|
|
81
|
+
content: { parts: [{ text: 'Hello world' }] }
|
|
82
|
+
)
|
|
83
|
+
puts embedding['embedding']['values'].length
|
|
84
|
+
|
|
85
|
+
# Count tokens
|
|
86
|
+
tokens = client.count_tokens(
|
|
87
|
+
contents: [{ parts: [{ text: 'How many tokens?' }] }]
|
|
88
|
+
)
|
|
89
|
+
puts tokens['totalTokens']
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Dependencies
|
|
93
|
+
|
|
94
|
+
- `faraday` >= 2.0 - HTTP client
|
|
95
|
+
- `faraday-multipart` (optional) - required only for multipart file uploads; raw binary upload used as fallback
|
|
96
|
+
|
|
97
|
+
## Requirements
|
|
98
|
+
|
|
99
|
+
- Ruby >= 3.4
|
|
100
|
+
- [LegionIO](https://github.com/LegionIO/LegionIO) framework (optional for standalone usage)
|
|
101
|
+
- Google Gemini API key ([Get one here](https://ai.google.dev/))
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
MIT
|
data/lex-gemini.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/legion/extensions/gemini/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lex-gemini'
|
|
7
|
+
spec.version = Legion::Extensions::Gemini::VERSION
|
|
8
|
+
spec.authors = ['Esity']
|
|
9
|
+
spec.email = ['matthewdiverson@gmail.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'LEX Gemini'
|
|
12
|
+
spec.description = 'Connects LegionIO to Google Gemini API'
|
|
13
|
+
spec.homepage = 'https://github.com/LegionIO/lex-gemini'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = '>= 3.4'
|
|
16
|
+
|
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-gemini'
|
|
19
|
+
spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-gemini'
|
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-gemini'
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-gemini/issues'
|
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
23
|
+
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ['lib']
|
|
28
|
+
|
|
29
|
+
spec.add_dependency 'faraday', '>= 2.0'
|
|
30
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Extensions
|
|
8
|
+
module Gemini
|
|
9
|
+
module Helpers
|
|
10
|
+
class Client
|
|
11
|
+
BASE_URL = 'https://generativelanguage.googleapis.com'
|
|
12
|
+
API_VERSION = 'v1beta'
|
|
13
|
+
|
|
14
|
+
attr_reader :api_key, :model, :connection
|
|
15
|
+
|
|
16
|
+
def initialize(api_key:, model: 'gemini-2.0-flash', base_url: BASE_URL)
|
|
17
|
+
@api_key = api_key
|
|
18
|
+
@model = model
|
|
19
|
+
@connection = Faraday.new(url: base_url) do |conn|
|
|
20
|
+
conn.request :json
|
|
21
|
+
conn.response :json
|
|
22
|
+
conn.params['key'] = api_key
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def generate_content(contents:, generation_config: nil, safety_settings: nil, system_instruction: nil, model: @model)
|
|
27
|
+
body = { contents: contents }
|
|
28
|
+
body[:generationConfig] = generation_config if generation_config
|
|
29
|
+
body[:safetySettings] = safety_settings if safety_settings
|
|
30
|
+
body[:systemInstruction] = system_instruction if system_instruction
|
|
31
|
+
post("#{API_VERSION}/models/#{model}:generateContent", body)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def stream_generate_content(contents:, generation_config: nil, safety_settings: nil, system_instruction: nil, model: @model)
|
|
35
|
+
body = { contents: contents }
|
|
36
|
+
body[:generationConfig] = generation_config if generation_config
|
|
37
|
+
body[:safetySettings] = safety_settings if safety_settings
|
|
38
|
+
body[:systemInstruction] = system_instruction if system_instruction
|
|
39
|
+
post("#{API_VERSION}/models/#{model}:streamGenerateContent", body)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def embed_content(content:, task_type: nil, title: nil, model: @model)
|
|
43
|
+
body = { content: content }
|
|
44
|
+
body[:taskType] = task_type if task_type
|
|
45
|
+
body[:title] = title if title
|
|
46
|
+
post("#{API_VERSION}/models/#{model}:embedContent", body)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def batch_embed_contents(requests:, model: @model)
|
|
50
|
+
post("#{API_VERSION}/models/#{model}:batchEmbedContents", { requests: requests })
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def list_models
|
|
54
|
+
get("#{API_VERSION}/models")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def get_model(name:)
|
|
58
|
+
get("#{API_VERSION}/models/#{name}")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def count_tokens(contents:, model: @model)
|
|
62
|
+
post("#{API_VERSION}/models/#{model}:countTokens", { contents: contents })
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def upload_file(file_path:, mime_type:, display_name: nil)
|
|
66
|
+
metadata = { file: { mimeType: mime_type } }
|
|
67
|
+
metadata[:file][:displayName] = display_name if display_name
|
|
68
|
+
|
|
69
|
+
upload_conn = Faraday.new(url: BASE_URL) do |conn|
|
|
70
|
+
conn.params['key'] = api_key
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
payload = Faraday::Multipart::FilePart.new(file_path, mime_type) if defined?(Faraday::Multipart)
|
|
74
|
+
|
|
75
|
+
if payload
|
|
76
|
+
upload_conn.request :multipart
|
|
77
|
+
upload_conn.post("/upload/#{API_VERSION}/files") do |req|
|
|
78
|
+
req.body = { metadata: metadata.to_json, file: payload }
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
upload_conn.post("/upload/#{API_VERSION}/files") do |req|
|
|
82
|
+
req.headers['Content-Type'] = mime_type
|
|
83
|
+
req.headers['X-Goog-Upload-Protocol'] = 'raw'
|
|
84
|
+
req.body = File.binread(file_path)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def list_files(page_size: nil, page_token: nil)
|
|
90
|
+
params = {}
|
|
91
|
+
params[:pageSize] = page_size if page_size
|
|
92
|
+
params[:pageToken] = page_token if page_token
|
|
93
|
+
get("#{API_VERSION}/files", params)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def get_file(name:)
|
|
97
|
+
get("#{API_VERSION}/#{name}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def delete_file(name:)
|
|
101
|
+
delete("#{API_VERSION}/#{name}")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def create_cached_content(model:, contents:, ttl: nil, expire_time: nil, display_name: nil, system_instruction: nil)
|
|
105
|
+
body = { model: "models/#{model}", contents: contents }
|
|
106
|
+
body[:ttl] = ttl if ttl
|
|
107
|
+
body[:expireTime] = expire_time if expire_time
|
|
108
|
+
body[:displayName] = display_name if display_name
|
|
109
|
+
body[:systemInstruction] = system_instruction if system_instruction
|
|
110
|
+
post("#{API_VERSION}/cachedContents", body)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def list_cached_contents(page_size: nil, page_token: nil)
|
|
114
|
+
params = {}
|
|
115
|
+
params[:pageSize] = page_size if page_size
|
|
116
|
+
params[:pageToken] = page_token if page_token
|
|
117
|
+
get("#{API_VERSION}/cachedContents", params)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def get_cached_content(name:)
|
|
121
|
+
get("#{API_VERSION}/#{name}")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def update_cached_content(name:, ttl: nil, expire_time: nil)
|
|
125
|
+
body = {}
|
|
126
|
+
update_masks = []
|
|
127
|
+
if ttl
|
|
128
|
+
body[:ttl] = ttl
|
|
129
|
+
update_masks << 'ttl'
|
|
130
|
+
end
|
|
131
|
+
if expire_time
|
|
132
|
+
body[:expireTime] = expire_time
|
|
133
|
+
update_masks << 'expireTime'
|
|
134
|
+
end
|
|
135
|
+
patch("#{API_VERSION}/#{name}", body, updateMask: update_masks.join(','))
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def delete_cached_content(name:)
|
|
139
|
+
delete("#{API_VERSION}/#{name}")
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def get(path, params = {})
|
|
145
|
+
response = connection.get(path, params)
|
|
146
|
+
handle_response(response)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def post(path, body)
|
|
150
|
+
response = connection.post(path, body)
|
|
151
|
+
handle_response(response)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def patch(path, body, params = {})
|
|
155
|
+
response = connection.patch(path) do |req|
|
|
156
|
+
req.params = connection.params.merge(params)
|
|
157
|
+
req.body = body
|
|
158
|
+
end
|
|
159
|
+
handle_response(response)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def delete(path)
|
|
163
|
+
response = connection.delete(path)
|
|
164
|
+
handle_response(response)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def handle_response(response)
|
|
168
|
+
return response.body if response.success?
|
|
169
|
+
|
|
170
|
+
{ error: response.body, status: response.status }
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module CachedContents
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def create(api_key:, model:, contents:, ttl: nil, expire_time: nil, display_name: nil, system_instruction: nil, **)
|
|
11
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
+
{ result: client.create_cached_content(model: model, contents: contents, ttl: ttl, expire_time: expire_time,
|
|
13
|
+
display_name: display_name, system_instruction: system_instruction) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def list(api_key:, page_size: nil, page_token: nil, **)
|
|
17
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
18
|
+
{ result: client.list_cached_contents(page_size: page_size, page_token: page_token) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def get(api_key:, name:, **)
|
|
22
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
23
|
+
{ result: client.get_cached_content(name: name) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def update(api_key:, name:, ttl: nil, expire_time: nil, **)
|
|
27
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
28
|
+
{ result: client.update_cached_content(name: name, ttl: ttl, expire_time: expire_time) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def delete(api_key:, name:, **)
|
|
32
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
33
|
+
{ result: client.delete_cached_content(name: name) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module Content
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def generate(api_key:, contents:, model: 'gemini-2.0-flash', generation_config: nil, safety_settings: nil,
|
|
11
|
+
system_instruction: nil, **)
|
|
12
|
+
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
13
|
+
{ result: client.generate_content(contents: contents, generation_config: generation_config,
|
|
14
|
+
safety_settings: safety_settings, system_instruction: system_instruction) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stream_generate(api_key:, contents:, model: 'gemini-2.0-flash', generation_config: nil, safety_settings: nil,
|
|
18
|
+
system_instruction: nil, **)
|
|
19
|
+
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
20
|
+
{ result: client.stream_generate_content(contents: contents, generation_config: generation_config,
|
|
21
|
+
safety_settings: safety_settings,
|
|
22
|
+
system_instruction: system_instruction) }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module Embeddings
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def embed(api_key:, content:, model: 'gemini-embedding-exp', task_type: nil, title: nil, **)
|
|
11
|
+
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
12
|
+
{ result: client.embed_content(content: content, task_type: task_type, title: title) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def batch_embed(api_key:, requests:, model: 'gemini-embedding-exp', **)
|
|
16
|
+
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
17
|
+
{ result: client.batch_embed_contents(requests: requests) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module Files
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def upload(api_key:, file_path:, mime_type:, display_name: nil, **)
|
|
11
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
+
{ result: client.upload_file(file_path: file_path, mime_type: mime_type, display_name: display_name) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def list(api_key:, page_size: nil, page_token: nil, **)
|
|
16
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
17
|
+
{ result: client.list_files(page_size: page_size, page_token: page_token) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def get(api_key:, name:, **)
|
|
21
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
22
|
+
{ result: client.get_file(name: name) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def delete(api_key:, name:, **)
|
|
26
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
27
|
+
{ result: client.delete_file(name: name) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module Models
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def list(api_key:, **)
|
|
11
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
12
|
+
{ result: client.list_models }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def get(api_key:, name:, **)
|
|
16
|
+
client = Helpers::Client.new(api_key: api_key)
|
|
17
|
+
{ result: client.get_model(name: name) }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Extensions
|
|
5
|
+
module Gemini
|
|
6
|
+
module Runners
|
|
7
|
+
module Tokens
|
|
8
|
+
include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
|
|
9
|
+
|
|
10
|
+
def count(api_key:, contents:, model: 'gemini-2.0-flash', **)
|
|
11
|
+
client = Helpers::Client.new(api_key: api_key, model: model)
|
|
12
|
+
{ result: client.count_tokens(contents: contents) }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'legion/extensions/gemini/version'
|
|
4
|
+
require 'legion/extensions/gemini/helpers/client'
|
|
5
|
+
require 'legion/extensions/gemini/runners/content'
|
|
6
|
+
require 'legion/extensions/gemini/runners/embeddings'
|
|
7
|
+
require 'legion/extensions/gemini/runners/models'
|
|
8
|
+
require 'legion/extensions/gemini/runners/tokens'
|
|
9
|
+
require 'legion/extensions/gemini/runners/files'
|
|
10
|
+
require 'legion/extensions/gemini/runners/cached_contents'
|
|
11
|
+
|
|
12
|
+
module Legion
|
|
13
|
+
module Extensions
|
|
14
|
+
module Gemini
|
|
15
|
+
extend Legion::Extensions::Core if defined?(Legion::Extensions::Core)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lex-gemini
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Esity
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: faraday
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
description: Connects LegionIO to Google Gemini API
|
|
27
|
+
email:
|
|
28
|
+
- matthewdiverson@gmail.com
|
|
29
|
+
executables: []
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- ".github/workflows/ci.yml"
|
|
34
|
+
- ".gitignore"
|
|
35
|
+
- ".rspec"
|
|
36
|
+
- ".rubocop.yml"
|
|
37
|
+
- CHANGELOG.md
|
|
38
|
+
- CLAUDE.md
|
|
39
|
+
- Dockerfile
|
|
40
|
+
- Gemfile
|
|
41
|
+
- LICENSE
|
|
42
|
+
- README.md
|
|
43
|
+
- lex-gemini.gemspec
|
|
44
|
+
- lib/legion/extensions/gemini.rb
|
|
45
|
+
- lib/legion/extensions/gemini/helpers/client.rb
|
|
46
|
+
- lib/legion/extensions/gemini/runners/cached_contents.rb
|
|
47
|
+
- lib/legion/extensions/gemini/runners/content.rb
|
|
48
|
+
- lib/legion/extensions/gemini/runners/embeddings.rb
|
|
49
|
+
- lib/legion/extensions/gemini/runners/files.rb
|
|
50
|
+
- lib/legion/extensions/gemini/runners/models.rb
|
|
51
|
+
- lib/legion/extensions/gemini/runners/tokens.rb
|
|
52
|
+
- lib/legion/extensions/gemini/version.rb
|
|
53
|
+
homepage: https://github.com/LegionIO/lex-gemini
|
|
54
|
+
licenses:
|
|
55
|
+
- MIT
|
|
56
|
+
metadata:
|
|
57
|
+
homepage_uri: https://github.com/LegionIO/lex-gemini
|
|
58
|
+
source_code_uri: https://github.com/LegionIO/lex-gemini
|
|
59
|
+
documentation_uri: https://github.com/LegionIO/lex-gemini
|
|
60
|
+
changelog_uri: https://github.com/LegionIO/lex-gemini
|
|
61
|
+
bug_tracker_uri: https://github.com/LegionIO/lex-gemini/issues
|
|
62
|
+
rubygems_mfa_required: 'true'
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '3.4'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.6.9
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: LEX Gemini
|
|
80
|
+
test_files: []
|