llm_conductor 1.8.2 → 1.8.3
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/README.md +16 -1
- data/docs/README.md +2 -2
- data/docs/custom-parameters.md +44 -2
- data/examples/gemini_thinking_usage.rb +48 -0
- data/lib/llm_conductor/clients/concerns/gemini_generation_config.rb +110 -0
- data/lib/llm_conductor/clients/gemini_client.rb +2 -25
- data/lib/llm_conductor/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 669ae889bc5b65b4455617425a5c3628a66ed21741eca0930c78652f4b8bfbad
|
|
4
|
+
data.tar.gz: 396219406bf2db7236c223cc36200dc1be2739d6a87ab6cc50ccdc75be7482ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e71aaa12abdf4c1bf1d750f4e28b41508d5e0b999d8373858e5ad0a16d8ea7f24c059553e59b5b38377b262bd5e6963f0a424b745d9f58e064d4135d4c05779
|
|
7
|
+
data.tar.gz: 2ab40251e03a5b8eb01bb6d7cfdbe0bac4b1137768b4c42b991da8649ec146698d17e012d076aa102643e37ddaa94951552e8e0c8f80b68ac14fd7875f7e8cdd
|
data/README.md
CHANGED
|
@@ -108,7 +108,7 @@ end
|
|
|
108
108
|
|----------|-------------|--------|---------------|
|
|
109
109
|
| OpenAI (GPT) | ✅ `gpt-*` | ✅ | 🔜 |
|
|
110
110
|
| Anthropic (Claude) | ✅ `claude-*` | ✅ | 🔜 |
|
|
111
|
-
| Google (Gemini) | ✅ `gemini-*` | ✅ |
|
|
111
|
+
| Google (Gemini) | ✅ `gemini-*` | ✅ | ✅ |
|
|
112
112
|
| Groq | ✅ `llama/mixtral` | ❌ | 🔜 |
|
|
113
113
|
| Ollama | ✅ (default) | ❌ | ✅ |
|
|
114
114
|
| OpenRouter | 🔧 Manual | ✅ | 🔜 |
|
|
@@ -150,6 +150,20 @@ response = LlmConductor.generate(
|
|
|
150
150
|
)
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
+
### Fast Gemini Calls (Minimal Thinking)
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
response = LlmConductor.generate(
|
|
157
|
+
model: 'gemini-3.5-flash-lite',
|
|
158
|
+
prompt: 'Extract the company name from: ...',
|
|
159
|
+
vendor: :gemini,
|
|
160
|
+
params: { thinking_level: 'minimal' }
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Sent to Gemini as `generationConfig.thinkingConfig.thinkingLevel`. See
|
|
165
|
+
[Custom Parameters](docs/custom-parameters.md#gemini-thinking-configuration).
|
|
166
|
+
|
|
153
167
|
### Vision Analysis
|
|
154
168
|
|
|
155
169
|
```ruby
|
|
@@ -328,6 +342,7 @@ Check the [examples/](examples/) directory for comprehensive examples:
|
|
|
328
342
|
|
|
329
343
|
- `simple_usage.rb` - Basic text generation
|
|
330
344
|
- `ollama_params_usage.rb` - Custom parameters with Ollama
|
|
345
|
+
- `gemini_thinking_usage.rb` - Gemini thinking configuration (`thinkingConfig`)
|
|
331
346
|
- `gpt_vision_usage.rb` - Vision with OpenAI
|
|
332
347
|
- `claude_vision_usage.rb` - Vision with Anthropic
|
|
333
348
|
- `gemini_vision_usage.rb` - Vision with Gemini
|
data/docs/README.md
CHANGED
|
@@ -11,8 +11,8 @@ Learn how to fine-tune LLM generation with parameters like `temperature`, `top_p
|
|
|
11
11
|
- Provider-specific parameters
|
|
12
12
|
- Best practices and use cases
|
|
13
13
|
|
|
14
|
-
**Currently supported**: Ollama
|
|
15
|
-
**Coming soon**: OpenAI, Anthropic,
|
|
14
|
+
**Currently supported**: Ollama, Gemini (including Gemini 3.x `thinkingConfig`)
|
|
15
|
+
**Coming soon**: OpenAI, Anthropic, Groq, OpenRouter, Z.ai
|
|
16
16
|
|
|
17
17
|
### [Vision Support](vision-support.md)
|
|
18
18
|
Complete guide to using vision/multimodal capabilities. Includes:
|
data/docs/custom-parameters.md
CHANGED
|
@@ -149,6 +149,13 @@ Below are common parameters supported by Google Gemini via `generationConfig`. F
|
|
|
149
149
|
| `max_output_tokens` | `maxOutputTokens` | Integer | Alias for max_tokens |
|
|
150
150
|
| `candidate_count` | `candidateCount` | Integer | Number of candidates to return |
|
|
151
151
|
| `stop_sequences` | `stopSequences` | Array | Stop sequences that end generation |
|
|
152
|
+
| `thinking_config` | `thinkingConfig` | Hash | Gemini 3.x thinking controls (see below) |
|
|
153
|
+
| `thinking_level` | `thinkingConfig.thinkingLevel` | String | Thinking depth: `'minimal'`, `'low'`, `'high'` |
|
|
154
|
+
| `thinking_budget` | `thinkingConfig.thinkingBudget` | Integer | Token budget for thinking (`0` disables it) |
|
|
155
|
+
| `include_thoughts` | `thinkingConfig.includeThoughts` | Boolean | Return the thought summary alongside the answer |
|
|
156
|
+
|
|
157
|
+
Unsupported keys are skipped and logged as a warning through
|
|
158
|
+
`LlmConductor.configuration.logger` — they are never dropped silently.
|
|
152
159
|
|
|
153
160
|
### Gemini Usage Examples
|
|
154
161
|
|
|
@@ -170,6 +177,36 @@ response = LlmConductor.generate(
|
|
|
170
177
|
)
|
|
171
178
|
```
|
|
172
179
|
|
|
180
|
+
### Gemini Thinking Configuration
|
|
181
|
+
|
|
182
|
+
Gemini 3.x models think before answering. Cutting that budget down is the single
|
|
183
|
+
biggest latency lever available on the fast models, so the thinking controls are
|
|
184
|
+
exposed through the same `params` hash:
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
# Fastest path — minimal thinking (flat convenience key)
|
|
188
|
+
response = LlmConductor.generate(
|
|
189
|
+
model: 'gemini-3.5-flash-lite',
|
|
190
|
+
prompt: 'Extract the company name from: ...',
|
|
191
|
+
vendor: :gemini,
|
|
192
|
+
params: { thinking_level: 'minimal', temperature: 0.0 }
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Nested form — mirrors the API shape, accepts snake_case or camelCase
|
|
196
|
+
response = LlmConductor.generate(
|
|
197
|
+
model: 'gemini-3.5-flash-lite',
|
|
198
|
+
prompt: 'Extract the company name from: ...',
|
|
199
|
+
vendor: :gemini,
|
|
200
|
+
params: { thinking_config: { thinking_level: 'minimal', thinking_budget: 0 } }
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Both forms are merged into the request as:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{ "generationConfig": { "thinkingConfig": { "thinkingLevel": "minimal" } } }
|
|
208
|
+
```
|
|
209
|
+
|
|
173
210
|
## Ollama Parameters Reference
|
|
174
211
|
|
|
175
212
|
Below are common parameters supported by Ollama. For a complete list, see the [Ollama documentation](https://github.com/ollama/ollama/blob/main/docs/modelfile.md#valid-parameters-and-values).
|
|
@@ -348,7 +385,8 @@ Always refer to your provider's documentation for supported parameters.
|
|
|
348
385
|
|
|
349
386
|
Currently, custom parameters are fully supported for:
|
|
350
387
|
- ✅ **Ollama**
|
|
351
|
-
- ✅ **Google (Gemini)** — maps snake_case Ruby keys to camelCase `generationConfig
|
|
388
|
+
- ✅ **Google (Gemini)** — maps snake_case Ruby keys to camelCase `generationConfig`,
|
|
389
|
+
including `thinkingConfig` for Gemini 3.x thinking controls
|
|
352
390
|
|
|
353
391
|
Coming soon:
|
|
354
392
|
- 🔜 OpenAI (GPT)
|
|
@@ -364,6 +402,8 @@ Coming soon:
|
|
|
364
402
|
1. Check parameter spelling (case-sensitive)
|
|
365
403
|
2. Verify your provider supports the parameter
|
|
366
404
|
3. Check parameter value types (integer vs float vs string)
|
|
405
|
+
4. Set `config.logger` — Gemini logs a warning for every param it cannot map,
|
|
406
|
+
e.g. `Skipping generationConfig param :reasoning_effort: no Gemini API mapping.`
|
|
367
407
|
|
|
368
408
|
### Unexpected Output
|
|
369
409
|
|
|
@@ -379,7 +419,9 @@ Coming soon:
|
|
|
379
419
|
|
|
380
420
|
## Examples
|
|
381
421
|
|
|
382
|
-
See the complete example
|
|
422
|
+
See the complete example files:
|
|
423
|
+
- [examples/ollama_params_usage.rb](../examples/ollama_params_usage.rb)
|
|
424
|
+
- [examples/gemini_thinking_usage.rb](../examples/gemini_thinking_usage.rb)
|
|
383
425
|
|
|
384
426
|
## Resources
|
|
385
427
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Gemini thinking configuration (generationConfig.thinkingConfig).
|
|
5
|
+
#
|
|
6
|
+
# Gemini 3.x models think before answering. Trimming that budget is the biggest
|
|
7
|
+
# latency lever on the fast models, so llm_conductor exposes it through params:
|
|
8
|
+
#
|
|
9
|
+
# params: { thinking_level: 'minimal' } # flat convenience key
|
|
10
|
+
# params: { thinking_config: { thinking_level: 'minimal' } } # nested, mirrors the API
|
|
11
|
+
#
|
|
12
|
+
# Both reach the API as generationConfig.thinkingConfig.thinkingLevel.
|
|
13
|
+
#
|
|
14
|
+
# GEMINI_API_KEY=... ruby examples/gemini_thinking_usage.rb
|
|
15
|
+
# GEMINI_API_KEY=... MODEL=gemini-3.5-flash-lite ruby examples/gemini_thinking_usage.rb
|
|
16
|
+
|
|
17
|
+
require_relative '../lib/llm_conductor'
|
|
18
|
+
require 'logger'
|
|
19
|
+
|
|
20
|
+
MODEL = ENV['MODEL'] || 'gemini-2.5-flash'
|
|
21
|
+
PROMPT = 'Name the capital of France. Answer with one word.'
|
|
22
|
+
|
|
23
|
+
LlmConductor.configure do |config|
|
|
24
|
+
config.gemini(api_key: ENV.fetch('GEMINI_API_KEY'))
|
|
25
|
+
# With a logger configured, any param the client cannot map is reported
|
|
26
|
+
# instead of being dropped silently.
|
|
27
|
+
config.logger = Logger.new($stdout)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
CASES = {
|
|
31
|
+
'no thinking config' => {},
|
|
32
|
+
'flat thinking_level' => { thinking_level: 'minimal' },
|
|
33
|
+
'nested thinking_config' => { thinking_config: { thinking_level: 'minimal' } },
|
|
34
|
+
'thinking_budget: 0' => { thinking_config: { thinking_budget: 0 } },
|
|
35
|
+
'unmapped param (expect a warning)' => { reasoning_effort: 'high' }
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
CASES.each do |label, params|
|
|
39
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
40
|
+
response = LlmConductor.generate(model: MODEL, prompt: PROMPT, vendor: :gemini, params:)
|
|
41
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
|
|
42
|
+
|
|
43
|
+
if response.success?
|
|
44
|
+
puts "[#{label}] #{elapsed.round(2)}s — #{response.output.to_s.strip}"
|
|
45
|
+
else
|
|
46
|
+
puts "[#{label}] FAILED — #{response.metadata[:error]}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LlmConductor
|
|
4
|
+
module Clients
|
|
5
|
+
module Concerns
|
|
6
|
+
# Builds Gemini's generationConfig payload from the generic snake_case +params+
|
|
7
|
+
# hash accepted by every client.
|
|
8
|
+
#
|
|
9
|
+
# The Gemini REST API expects camelCase keys inside generationConfig, and nests
|
|
10
|
+
# the Gemini 3.x thinking controls one level deeper, under
|
|
11
|
+
# generationConfig.thinkingConfig. Callers keep passing snake_case Ruby keys —
|
|
12
|
+
# the translation lives here.
|
|
13
|
+
#
|
|
14
|
+
# Keys without a mapping are skipped and reported through the configured logger
|
|
15
|
+
# instead of disappearing silently. Skipping rather than raising keeps existing
|
|
16
|
+
# callers that pass provider-agnostic params working unchanged.
|
|
17
|
+
module GeminiGenerationConfig
|
|
18
|
+
# Gemini REST API uses camelCase keys in generationConfig.
|
|
19
|
+
PARAM_KEY_MAP = {
|
|
20
|
+
temperature: :temperature,
|
|
21
|
+
top_p: :topP,
|
|
22
|
+
top_k: :topK,
|
|
23
|
+
max_tokens: :maxOutputTokens,
|
|
24
|
+
max_output_tokens: :maxOutputTokens,
|
|
25
|
+
candidate_count: :candidateCount,
|
|
26
|
+
stop_sequences: :stopSequences
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# Keys nested under generationConfig.thinkingConfig (Gemini 3.x thinking
|
|
30
|
+
# controls). Accepted flat in params or inside a thinking_config hash.
|
|
31
|
+
THINKING_KEY_MAP = {
|
|
32
|
+
thinking_level: :thinkingLevel,
|
|
33
|
+
thinking_budget: :thinkingBudget,
|
|
34
|
+
include_thoughts: :includeThoughts
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Param keys carrying a nested thinkingConfig hash.
|
|
38
|
+
THINKING_CONFIG_KEYS = %i[thinking_config thinkingConfig].freeze
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Build Gemini generationConfig from the params hash.
|
|
43
|
+
#
|
|
44
|
+
# Thinking controls may be passed flat (thinking_level: 'minimal') or nested
|
|
45
|
+
# (thinking_config: { thinking_level: 'minimal' }); either way they end up in
|
|
46
|
+
# generationConfig.thinkingConfig, and both forms together are merged.
|
|
47
|
+
#
|
|
48
|
+
# @return [Hash, nil] generationConfig hash, or nil when nothing maps
|
|
49
|
+
def build_generation_config
|
|
50
|
+
return unless params.is_a?(Hash) && params.any?
|
|
51
|
+
|
|
52
|
+
gen_cfg = {}
|
|
53
|
+
thinking_cfg = {}
|
|
54
|
+
params.each { |key, value| assign_generation_param(key, value, gen_cfg, thinking_cfg) }
|
|
55
|
+
gen_cfg[:thinkingConfig] = thinking_cfg if thinking_cfg.any?
|
|
56
|
+
gen_cfg.any? ? gen_cfg : nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Route a single param into generationConfig or its nested thinkingConfig.
|
|
60
|
+
def assign_generation_param(key, value, gen_cfg, thinking_cfg)
|
|
61
|
+
if THINKING_CONFIG_KEYS.include?(key.to_sym)
|
|
62
|
+
thinking_cfg.merge!(normalize_thinking_config(value))
|
|
63
|
+
elsif (thinking_key = thinking_config_key_for(key))
|
|
64
|
+
thinking_cfg[thinking_key] = value
|
|
65
|
+
elsif (mapped = PARAM_KEY_MAP[key.to_sym])
|
|
66
|
+
gen_cfg[mapped] = value
|
|
67
|
+
else
|
|
68
|
+
warn_unmapped_param(key)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Normalize a nested thinking_config hash to the API's camelCase keys.
|
|
73
|
+
# @param value [Hash] thinking config as given by the caller
|
|
74
|
+
# @return [Hash] camelCase thinkingConfig fragment
|
|
75
|
+
def normalize_thinking_config(value)
|
|
76
|
+
unless value.is_a?(Hash)
|
|
77
|
+
warn_unmapped_param(:thinking_config, reason: "expected a Hash, got #{value.class}")
|
|
78
|
+
return {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
value.each_with_object({}) do |(key, val), cfg|
|
|
82
|
+
mapped = thinking_config_key_for(key)
|
|
83
|
+
if mapped
|
|
84
|
+
cfg[mapped] = val
|
|
85
|
+
else
|
|
86
|
+
warn_unmapped_param(key, scope: 'thinkingConfig')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Resolve a thinkingConfig key, accepting snake_case or the camelCase API name.
|
|
92
|
+
# @return [Symbol, nil] camelCase API key, or nil when unknown
|
|
93
|
+
def thinking_config_key_for(key)
|
|
94
|
+
sym_key = key.to_sym
|
|
95
|
+
THINKING_KEY_MAP[sym_key] || (sym_key if THINKING_KEY_MAP.value?(sym_key))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Report a param the client cannot map. Non-fatal by design: unmapped keys are
|
|
99
|
+
# skipped, but never without a trace — configure LlmConductor.configuration.logger
|
|
100
|
+
# to surface them.
|
|
101
|
+
def warn_unmapped_param(key, scope: 'generationConfig', reason: 'no Gemini API mapping')
|
|
102
|
+
configuration.logger&.warn(
|
|
103
|
+
"[LlmConductor::Clients::GeminiClient] Skipping #{scope} param #{key.inspect}: #{reason}. " \
|
|
104
|
+
'See docs/custom-parameters.md for the supported keys.'
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -5,6 +5,7 @@ require 'base64'
|
|
|
5
5
|
require 'net/http'
|
|
6
6
|
require 'uri'
|
|
7
7
|
require_relative 'concerns/vision_support'
|
|
8
|
+
require_relative 'concerns/gemini_generation_config'
|
|
8
9
|
require_relative '../patches/gemini_vertex_api_key'
|
|
9
10
|
|
|
10
11
|
module LlmConductor
|
|
@@ -13,20 +14,10 @@ module LlmConductor
|
|
|
13
14
|
# Supports both text-only and multimodal (vision) requests
|
|
14
15
|
class GeminiClient < BaseClient
|
|
15
16
|
include Concerns::VisionSupport
|
|
17
|
+
include Concerns::GeminiGenerationConfig
|
|
16
18
|
|
|
17
19
|
private
|
|
18
20
|
|
|
19
|
-
# Gemini REST API uses camelCase keys in generationConfig.
|
|
20
|
-
PARAM_KEY_MAP = {
|
|
21
|
-
temperature: :temperature,
|
|
22
|
-
top_p: :topP,
|
|
23
|
-
top_k: :topK,
|
|
24
|
-
max_tokens: :maxOutputTokens,
|
|
25
|
-
max_output_tokens: :maxOutputTokens,
|
|
26
|
-
candidate_count: :candidateCount,
|
|
27
|
-
stop_sequences: :stopSequences
|
|
28
|
-
}.freeze
|
|
29
|
-
|
|
30
21
|
def generate_content(prompt)
|
|
31
22
|
content = format_content(prompt)
|
|
32
23
|
parts = build_parts_for_gemini(content)
|
|
@@ -45,20 +36,6 @@ module LlmConductor
|
|
|
45
36
|
response.dig('candidates', 0, 'content', 'parts', 0, 'text')
|
|
46
37
|
end
|
|
47
38
|
|
|
48
|
-
# Build Gemini generationConfig from params hash.
|
|
49
|
-
# Maps snake_case Ruby keys to camelCase Gemini API keys.
|
|
50
|
-
# @return [Hash, nil] generationConfig hash or nil if no mapped params
|
|
51
|
-
def build_generation_config
|
|
52
|
-
return unless params.is_a?(Hash) && params.any?
|
|
53
|
-
|
|
54
|
-
gen_cfg = {}
|
|
55
|
-
params.each do |key, value|
|
|
56
|
-
mapped = PARAM_KEY_MAP[key.to_sym]
|
|
57
|
-
gen_cfg[mapped] = value if mapped
|
|
58
|
-
end
|
|
59
|
-
gen_cfg.any? ? gen_cfg : nil
|
|
60
|
-
end
|
|
61
|
-
|
|
62
39
|
# Build parts array for Gemini API from formatted content
|
|
63
40
|
# Converts VisionSupport format to Gemini's specific format
|
|
64
41
|
# @param content [String, Array] Formatted content from VisionSupport
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: llm_conductor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.
|
|
4
|
+
version: 1.8.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Zheng
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-08-25 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -172,6 +172,7 @@ files:
|
|
|
172
172
|
- docs/vision-support.md
|
|
173
173
|
- examples/claude_vision_usage.rb
|
|
174
174
|
- examples/data_builder_usage.rb
|
|
175
|
+
- examples/gemini_thinking_usage.rb
|
|
175
176
|
- examples/gemini_usage.rb
|
|
176
177
|
- examples/gemini_vision_usage.rb
|
|
177
178
|
- examples/gpt_vision_usage.rb
|
|
@@ -187,6 +188,7 @@ files:
|
|
|
187
188
|
- lib/llm_conductor/client_factory.rb
|
|
188
189
|
- lib/llm_conductor/clients/anthropic_client.rb
|
|
189
190
|
- lib/llm_conductor/clients/base_client.rb
|
|
191
|
+
- lib/llm_conductor/clients/concerns/gemini_generation_config.rb
|
|
190
192
|
- lib/llm_conductor/clients/concerns/vision_support.rb
|
|
191
193
|
- lib/llm_conductor/clients/gemini_client.rb
|
|
192
194
|
- lib/llm_conductor/clients/gpt_client.rb
|