Skip to content

Commit b21ada6

Browse files
committed
Releae v0.11.0: add Gemini 3.1 model support and comprehensive Files API features
- Add `gemini-3.1-flash-lite-preview` and `gemini-3.1-flash-image-preview` entries to the model registry. - Implement generic `Gemini.predict/3` and `predict_long_running/3` endpoints for advanced model interactions (Imagen/Veo). - Add `thinking` boolean to `Gemini.Types.Response.Model` and `supports_thinking?/1` helper to identify reasoning capabilities. - Enhance `generateContent` response parsing with `candidates_tokens_details`, `grounding_metadata`, `logprobs_result`, and `url_context_metadata`. - Add structured parsing for `CitationMetadata` and `CitationSource`. - Implement `MultiSpeakerVoiceConfig` for advanced text-to-speech workflows. - Add `enable_enhanced_civic_answers` to `GenerationConfig`. - Enforce Gemini-only auth for Files API; add `api_key` override support. - Remove `mimeType` from file upload metadata (output-only field fix). - Deprecate `gemini-3-pro-preview` in favor of `gemini-3.1-pro-preview`.
1 parent c44bb3b commit b21ada6

26 files changed

Lines changed: 1006 additions & 203 deletions

CHANGELOG.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.11.0] - 2026-03-05
11+
12+
### Added
13+
14+
#### New Model Registry Entries
15+
- `gemini-3.1-flash-lite-preview` — Gemini 3.1 Flash-Lite Preview, frontier-class performance rivaling larger models at a fraction of the cost
16+
- `gemini-3.1-flash-image-preview` — Nano Banana 2, high-efficiency image generation and editing optimized for speed and high-volume use cases
17+
18+
#### Config Model Registry
19+
- `flash_3_1_lite_preview` and `flash_3_1_image_preview` keys added to universal models in `Gemini.Config`
20+
21+
#### Models API: `thinking` field
22+
- **`thinking` boolean field** added to `Gemini.Types.Response.Model` — reflects whether the model supports thinking/reasoning as returned by the API
23+
- **`Model.supports_thinking?/1`** helper function for querying thinking capability
24+
- `thinking` included in `Model.capabilities_summary/1` output
25+
- Parsed from API responses in both `Gemini.APIs.Models` and `Gemini.APIs.Coordinator`
26+
27+
#### generateContent/streamGenerateContent API completeness
28+
- **`candidates_tokens_details`** field added to `UsageMetadata` — parses `candidatesTokensDetails` modality token counts from API responses
29+
- **`grounding_metadata`** field added to `Candidate` — stores `groundingMetadata` (grounding chunks, supports, web search queries) from API responses
30+
- **`logprobs_result`** field added to `Candidate` — stores `logprobsResult` log probability data from API responses
31+
- **`url_context_metadata`** field added to `Candidate` — stores `urlContextMetadata` from API responses
32+
- **`CitationMetadata.from_api/1`** and **`CitationSource.from_api/1`** — proper structured parsing of citation data (previously stored as raw maps)
33+
- **`safety_settings`** wired into `build_generate_request` — pass `safety_settings: [SafetySetting.harassment(:block_none)]` to set per-request safety thresholds
34+
- **`enable_enhanced_civic_answers`** field added to `GenerationConfig` and supported in `put_generation_opt`
35+
- **`MultiSpeakerVoiceConfig`** and **`SpeakerVoiceConfig`** types added with `from_api/1` and `to_api/1`
36+
- **`multi_speaker_voice_config`** field added to `SpeechConfig` for multi-speaker voice synthesis
37+
38+
#### Models API: `predict` and `predictLongRunning` endpoints
39+
- **`Gemini.predict/3`** — generic `models.predict` endpoint (`POST /v1beta/{model=models/*}:predict`) for direct prediction requests
40+
- **`Gemini.predict_long_running/3`**`models.predictLongRunning` endpoint (`POST /v1beta/{model=models/*}:predictLongRunning`) returning an Operation for async processing
41+
- Both endpoints support Vertex AI (`projects/.../publishers/google/models/...:predict`) and Gemini API paths
42+
- For most use cases, prefer the higher-level `Gemini.APIs.Images` or `Gemini.APIs.Videos` modules which use these endpoints internally
43+
44+
### Changed
45+
- **Gemini 3 Pro deprecation**: `gemini-3-pro-preview` track changed from `:preview` to `:deprecated` — Google is discontinuing the model on March 9, 2026; migrate to `gemini-3.1-pro-preview`
46+
1047
## [0.10.0] - 2026-02-22
1148

1249
### Added
@@ -1998,7 +2035,8 @@ config :gemini_ex,
19982035
- Minimal latency overhead
19992036
- Concurrent request processing
20002037

2001-
[Unreleased]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.10.0...HEAD
2038+
[Unreleased]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.11.0...HEAD
2039+
[0.11.0]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.10.0...v0.11.0
20022040
[0.10.0]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.9.1...v0.10.0
20032041
[0.9.1]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.9.0...v0.9.1
20042042
[0.9.0]: https://github.com/nshkrdotcom/gemini_ex/compare/v0.8.8...v0.9.0

README.md

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ A comprehensive Elixir client for Google's Gemini AI API with dual authenticatio
2929
- **Batches API**: Submit large numbers of requests with 50% cost savings (NEW in v0.7.0!)
3030
- **Operations API**: Track long-running operations like video generation (NEW in v0.7.0!)
3131
- **Tunings (Fine-Tuning)**: Create, monitor, and manage tuned models (NEW in v0.8.x!)
32-
- **Model Registry**: Centralized model capabilities, modality tracking, and registry-backed Live API model resolution (NEW in v0.10.0!)
32+
- **Model Registry**: Centralized model capabilities, modality tracking, and registry-backed Live API model resolution (Enhanced in v0.11.0!)
3333
- **Image & Video Generation**: Imagen/Veo APIs for text-to-image, editing, upscaling, and video generation with Veo 3.1 support
3434
- **Embeddings with MRL**: Text embeddings with Matryoshka Representation Learning, normalization, and distance metrics
3535
- **Async Batch Embeddings**: Production-scale embedding generation with 50% cost savings
@@ -59,7 +59,7 @@ Add `gemini` to your list of dependencies in `mix.exs`:
5959
```elixir
6060
def deps do
6161
[
62-
{:gemini_ex, "~> 0.10.0"}
62+
{:gemini_ex, "~> 0.11.0"}
6363
]
6464
end
6565
```
@@ -431,7 +431,7 @@ alias Gemini.Types.Content
431431
- `gemini-2.5-pro`
432432
- `gemini-2.0-flash-001`
433433
- `gemini-2.0-flash-lite-001`
434-
- `gemini-3-pro-preview`
434+
- `gemini-3.1-pro-preview`
435435
- `gemini-3-flash-preview`
436436

437437
You can list, get, update TTL, and delete caches via the top-level `Gemini.*cache*` helpers or `Gemini.APIs.ContextCache.*`. Vertex AI names are auto-expanded when `auth: :vertex_ai` or configured credentials are present.
@@ -442,25 +442,24 @@ Upload and manage files for use with Gemini models. Perfect for multimodal conte
442442

443443
```elixir
444444
alias Gemini.APIs.Files
445-
alias Gemini.Types.File
446445

447-
# Upload a file
448-
{:ok, file} = Files.upload("path/to/image.png")
446+
# Upload a file (Gemini Developer API only)
447+
{:ok, file} = Files.upload("path/to/image.png", auth: :gemini)
449448

450-
# Wait for processing (videos/large files)
451-
{:ok, ready} = Files.wait_for_processing(file.name)
449+
# Use the File struct directly in content generation
450+
{:ok, response} = Gemini.generate([file, "What's in this image?"])
452451

453-
# Use in content generation
454-
{:ok, response} = Gemini.generate([
455-
"What's in this image?",
456-
%{file_uri: ready.uri, mime_type: ready.mime_type}
457-
])
452+
# Wait for processing only when the file is still processing
453+
{:ok, video} = Files.upload("path/to/video.mp4", auth: :gemini)
454+
{:ok, ready_video} = Files.wait_for_processing(video.name, auth: :gemini)
455+
{:ok, video_response} = Gemini.generate([ready_video, "Describe this video clip"])
458456

459457
# List all files
460-
{:ok, files} = Files.list_all()
458+
{:ok, files} = Files.list_all(auth: :gemini)
461459

462460
# Clean up
463-
:ok = Files.delete(file.name)
461+
:ok = Files.delete(file.name, auth: :gemini)
462+
:ok = Files.delete(video.name, auth: :gemini)
464463
```
465464

466465
**Key Features:**
@@ -469,6 +468,8 @@ alias Gemini.Types.File
469468
- Automatic MIME type detection
470469
- 48-hour file expiration
471470

471+
**Note:** The Files API is available only on the Gemini Developer API. It is not supported on Vertex AI.
472+
472473
See [Files API Guide](guides/files.md) for complete documentation.
473474

474475
## File Search Stores (New in v0.8.x!)
@@ -631,18 +632,14 @@ Register existing GCS files with the Gemini API without uploading. Ideal for lar
631632
```elixir
632633
alias Gemini.APIs.Files
633634

634-
# GCS credentials with read access to the bucket
635-
credentials = %{
636-
"type" => "service_account",
637-
"client_email" => "...",
638-
"private_key" => "...",
639-
# ... other service account fields
640-
}
635+
# Fetch an OAuth token with read access to the bucket
636+
{:ok, token} = Goth.fetch(MyApp.Goth)
641637

642638
# Register GCS files
643639
{:ok, response} = Files.register_files(
644640
["gs://my-bucket/documents/report.pdf", "gs://my-bucket/images/photo.jpg"],
645-
credentials: credentials
641+
credentials: %{token: token.token},
642+
auth: :gemini
646643
)
647644

648645
# Use registered files in generation
@@ -652,12 +649,12 @@ end)
652649

653650
file = hd(response.files)
654651
{:ok, response} = Gemini.generate([
655-
"Summarize this document",
656-
%{file_uri: file.uri, mime_type: file.mime_type}
652+
file,
653+
"Summarize this document"
657654
])
658655
```
659656

660-
**Note:** This feature is only available in the Gemini Developer API, not Vertex AI. The credentials must have read access to the GCS bucket.
657+
**Note:** This feature is only available in the Gemini Developer API, not Vertex AI. Pass either `%{token: "..."}` or a `Goth.Token`-style struct, and ensure the token can read the referenced GCS objects.
661658

662659
## Model Armor (Vertex AI only)
663660

@@ -1510,7 +1507,7 @@ config :gemini_ex,
15101507
Or specify per-request:
15111508

15121509
```elixir
1513-
Gemini.generate("Hello", model: "gemini-3-pro-preview")
1510+
Gemini.generate("Hello", model: "gemini-3.1-pro-preview")
15141511
Gemini.embed_content("Text", model: "gemini-embedding-001")
15151512
```
15161513

@@ -1622,7 +1619,7 @@ All generation config options are fully supported across all API entry points:
16221619
- `gemini-flash-lite-latest` (default; fastest + most cost-efficient)
16231620
- `gemini-2.5-flash` (balanced price/performance for high-volume workloads)
16241621
- `gemini-3-flash-preview` (fast Gemini 3 with full thinking levels + built-in tools)
1625-
- `gemini-3-pro-preview` (most capable multimodal reasoning)
1622+
- `gemini-3.1-pro-preview` (most capable multimodal reasoning and agentic coding)
16261623

16271624
### Multimodal Content (New in v0.2.2!)
16281625

examples/15_files_api.exs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# Files API Example
2+
# Run with: mix run examples/15_files_api.exs
3+
#
4+
# Demonstrates:
5+
# - Uploading a file with the Gemini Files API
6+
# - Using the File struct directly in generation
7+
# - Getting metadata and listing files
8+
# - Cleaning up uploaded files
9+
10+
defmodule FilesAPIExample do
11+
alias Gemini.APIs.Files
12+
13+
@fixture_path Path.expand("../test/fixtures/test_document.txt", __DIR__)
14+
15+
def run do
16+
print_header("FILES API")
17+
18+
if gemini_api_key_available?() do
19+
demo_files_api()
20+
print_footer()
21+
else
22+
IO.puts("[SKIP] Files API examples require GEMINI_API_KEY.")
23+
IO.puts("[SKIP] Vertex AI credentials are not enough for Gemini Files uploads.")
24+
IO.puts("")
25+
end
26+
end
27+
28+
defp demo_files_api do
29+
print_section("1. Upload, Generate, Inspect, Delete")
30+
31+
uploaded_file =
32+
case Files.upload(@fixture_path, auth: :gemini) do
33+
{:ok, file} ->
34+
IO.puts("UPLOADED:")
35+
IO.puts(" name: #{file.name}")
36+
IO.puts(" mime_type: #{file.mime_type}")
37+
IO.puts("")
38+
file
39+
40+
{:error, error} ->
41+
IO.puts("[ERROR] Upload failed: #{inspect(error)}")
42+
System.halt(1)
43+
end
44+
45+
try do
46+
demo_generate(uploaded_file)
47+
demo_get(uploaded_file)
48+
demo_list(uploaded_file)
49+
after
50+
case Files.delete(uploaded_file.name, auth: :gemini) do
51+
:ok ->
52+
IO.puts("CLEANUP:")
53+
IO.puts(" deleted #{uploaded_file.name}")
54+
IO.puts("")
55+
56+
{:error, error} ->
57+
IO.puts("[ERROR] Cleanup failed: #{inspect(error)}")
58+
IO.puts("")
59+
end
60+
end
61+
end
62+
63+
defp demo_generate(file) do
64+
IO.puts("GENERATE:")
65+
66+
case Gemini.generate([file, "\n\n", "Summarize this file in one sentence."]) do
67+
{:ok, response} ->
68+
{:ok, text} = Gemini.extract_text(response)
69+
IO.puts(" #{text}")
70+
IO.puts("")
71+
72+
{:error, error} ->
73+
IO.puts(" [ERROR] #{inspect(error)}")
74+
IO.puts("")
75+
end
76+
end
77+
78+
defp demo_get(file) do
79+
IO.puts("GET:")
80+
81+
case Files.get(file.name, auth: :gemini) do
82+
{:ok, fetched} ->
83+
IO.puts(" state: #{inspect(fetched.state)}")
84+
IO.puts(" uri: #{fetched.uri}")
85+
IO.puts("")
86+
87+
{:error, error} ->
88+
IO.puts(" [ERROR] #{inspect(error)}")
89+
IO.puts("")
90+
end
91+
end
92+
93+
defp demo_list(file) do
94+
IO.puts("LIST:")
95+
96+
case Files.list(page_size: 5, auth: :gemini) do
97+
{:ok, page} ->
98+
present? = Enum.any?(page.files, &(&1.name == file.name))
99+
IO.puts(" returned #{length(page.files)} files")
100+
IO.puts(" uploaded file present?: #{present?}")
101+
IO.puts("")
102+
103+
{:error, error} ->
104+
IO.puts(" [ERROR] #{inspect(error)}")
105+
IO.puts("")
106+
end
107+
end
108+
109+
defp gemini_api_key_available? do
110+
case System.get_env("GEMINI_API_KEY") do
111+
value when is_binary(value) and value != "" -> true
112+
_ -> false
113+
end
114+
end
115+
116+
defp print_header(title) do
117+
IO.puts("")
118+
IO.puts(String.duplicate("=", 70))
119+
IO.puts(" #{title}")
120+
IO.puts(String.duplicate("=", 70))
121+
IO.puts("")
122+
end
123+
124+
defp print_section(title) do
125+
IO.puts(String.duplicate("-", 70))
126+
IO.puts(title)
127+
IO.puts(String.duplicate("-", 70))
128+
IO.puts("")
129+
end
130+
131+
defp print_footer do
132+
IO.puts(String.duplicate("=", 70))
133+
IO.puts(" EXAMPLE COMPLETE")
134+
IO.puts(String.duplicate("=", 70))
135+
IO.puts("")
136+
end
137+
end
138+
139+
FilesAPIExample.run()

examples/README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ mix run examples/01_basic_generation.exs
5050
| 09 | `09_safety_settings.exs` | Content safety filters, harm categories, thresholds |
5151
| 10 | `10_system_instructions.exs` | Persona setup, formatting rules, domain experts |
5252
| 11 | `11_live_text_chat.exs` | Live API multi-turn text conversations |
53-
| 12 | `12_live_audio_streaming.exs` | Live API audio input/output streaming |
54-
| 13 | `13_live_session_resumption.exs` | Session resume across disconnections |
55-
| 14 | `14_live_function_calling.exs` | Tool/function calling with telemetry |
53+
| 12 | `12_live_audio_streaming.exs` | Live API audio input/output streaming |
54+
| 13 | `13_live_session_resumption.exs` | Session resume across disconnections |
55+
| 14 | `14_live_function_calling.exs` | Tool/function calling with telemetry |
56+
| 15 | `15_files_api.exs` | Gemini Files upload/get/list/delete workflow with generation |
5657

5758
## Example Details
5859

@@ -190,8 +191,8 @@ SafetySetting.permissive() # Block only high risk
190191
Gemini.generate(prompt, safety_settings: safety_settings)
191192
```
192193

193-
### 10 - System Instructions
194-
Control model behavior and persona:
194+
### 10 - System Instructions
195+
Control model behavior and persona:
195196
- Create custom personas
196197
- Enforce response formatting
197198
- Define domain expertise
@@ -204,10 +205,22 @@ You are a helpful coding assistant. Always:
204205
- Suggest best practices
205206
"""
206207

207-
Gemini.generate(prompt, system_instruction: system_instruction)
208-
```
209-
210-
## Output Format
208+
Gemini.generate(prompt, system_instruction: system_instruction)
209+
```
210+
211+
### 15 - Files API
212+
Upload a file with Gemini, use the returned `Gemini.Types.File` directly in generation, inspect it, and delete it:
213+
214+
```elixir
215+
{:ok, file} = Gemini.APIs.Files.upload("test/fixtures/test_document.txt", auth: :gemini)
216+
{:ok, response} = Gemini.generate([file, "Summarize this file in one sentence."])
217+
{:ok, fetched} = Gemini.APIs.Files.get(file.name, auth: :gemini)
218+
:ok = Gemini.APIs.Files.delete(file.name, auth: :gemini)
219+
```
220+
221+
This example skips itself unless `GEMINI_API_KEY` is present, because the Files API is Gemini-only.
222+
223+
## Output Format
211224

212225
All examples follow a consistent output format:
213226
- Clear section headers with `===` or `---`

guides/file_search_stores.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ Stores are created asynchronously. Always wait for activation before adding docu
189189
If you've already uploaded a file using the Files API:
190190

191191
```elixir
192-
# Upload a file first
193-
{:ok, file} = Gemini.upload_file("/path/to/document.pdf")
192+
# Upload a file first with the Gemini Files API
193+
{:ok, file} = Gemini.APIs.Files.upload("/path/to/document.pdf", auth: :gemini)
194194

195195
# Import it into the store
196196
{:ok, doc} = FileSearchStores.import_file(
@@ -200,7 +200,7 @@ If you've already uploaded a file using the Files API:
200200
)
201201

202202
# Wait for processing
203-
{:ok, ready_doc} = FileSearchStores.wait_for_document(doc.name)
203+
{:ok, ready_doc} = FileSearchStores.wait_for_document(doc.name, auth: :vertex_ai)
204204
IO.puts("Document ready with #{ready_doc.chunk_count} chunks")
205205
```
206206

0 commit comments

Comments
 (0)