llm-gemini 0.17__tar.gz → 0.18.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llm-gemini
3
- Version: 0.17
3
+ Version: 0.18.1
4
4
  Summary: LLM plugin to access Google's Gemini family of models
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -66,6 +66,7 @@ llm "A joke about a pelican and a walrus"
66
66
 
67
67
  Other models are:
68
68
 
69
+ - `gemini-2.5-flash-preview-04-17` - Gemini 2.5 Flash preview
69
70
  - `gemini-2.5-pro-exp-03-25` - free experimental release of Gemini 2.5 Pro
70
71
  - `gemini-2.5-pro-preview-03-25` - paid preview of Gemini 2.5 Pro
71
72
  - `gemma-3-27b-it` - [Gemma 3](https://blog.google/technology/developers/gemma-3/) 27B
@@ -43,6 +43,7 @@ llm "A joke about a pelican and a walrus"
43
43
 
44
44
  Other models are:
45
45
 
46
+ - `gemini-2.5-flash-preview-04-17` - Gemini 2.5 Flash preview
46
47
  - `gemini-2.5-pro-exp-03-25` - free experimental release of Gemini 2.5 Pro
47
48
  - `gemini-2.5-pro-preview-03-25` - paid preview of Gemini 2.5 Pro
48
49
  - `gemma-3-27b-it` - [Gemma 3](https://blog.google/technology/developers/gemma-3/) 27B
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llm-gemini
3
- Version: 0.17
3
+ Version: 0.18.1
4
4
  Summary: LLM plugin to access Google's Gemini family of models
5
5
  Author: Simon Willison
6
6
  License: Apache-2.0
@@ -66,6 +66,7 @@ llm "A joke about a pelican and a walrus"
66
66
 
67
67
  Other models are:
68
68
 
69
+ - `gemini-2.5-flash-preview-04-17` - Gemini 2.5 Flash preview
69
70
  - `gemini-2.5-pro-exp-03-25` - free experimental release of Gemini 2.5 Pro
70
71
  - `gemini-2.5-pro-preview-03-25` - paid preview of Gemini 2.5 Pro
71
72
  - `gemma-3-27b-it` - [Gemma 3](https://blog.google/technology/developers/gemma-3/) 27B
@@ -36,6 +36,23 @@ GOOGLE_SEARCH_MODELS = {
36
36
  "gemini-1.5-flash-002",
37
37
  "gemini-2.0-flash-exp",
38
38
  "gemini-2.0-flash",
39
+ "gemini-2.5-pro-preview-03-25",
40
+ "gemini-2.5-pro-exp-03-25",
41
+ }
42
+
43
+ # Older Google models used google_search_retrieval instead of google_search
44
+ GOOGLE_SEARCH_MODELS_USING_SEARCH_RETRIEVAL = {
45
+ "gemini-1.5-pro-latest",
46
+ "gemini-1.5-flash-latest",
47
+ "gemini-1.5-pro-001",
48
+ "gemini-1.5-flash-001",
49
+ "gemini-1.5-pro-002",
50
+ "gemini-1.5-flash-002",
51
+ "gemini-2.0-flash-exp",
52
+ }
53
+
54
+ THINKING_BUDGET_MODELS = {
55
+ "gemini-2.5-flash-preview-04-17",
39
56
  }
40
57
 
41
58
 
@@ -70,17 +87,22 @@ def register_models(register):
70
87
  "gemini-2.5-pro-exp-03-25",
71
88
  # 4th April 2025 (paid):
72
89
  "gemini-2.5-pro-preview-03-25",
90
+ # 17th April 2025:
91
+ "gemini-2.5-flash-preview-04-17",
73
92
  ]:
74
93
  can_google_search = model_id in GOOGLE_SEARCH_MODELS
94
+ can_thinking_budget = model_id in THINKING_BUDGET_MODELS
75
95
  register(
76
96
  GeminiPro(
77
97
  model_id,
78
98
  can_google_search=can_google_search,
99
+ can_thinking_budget=can_thinking_budget,
79
100
  can_schema="flash-thinking" not in model_id,
80
101
  ),
81
102
  AsyncGeminiPro(
82
103
  model_id,
83
104
  can_google_search=can_google_search,
105
+ can_thinking_budget=can_thinking_budget,
84
106
  can_schema="flash-thinking" not in model_id,
85
107
  ),
86
108
  )
@@ -208,12 +230,27 @@ class _SharedGemini:
208
230
  default=None,
209
231
  )
210
232
 
211
- def __init__(self, model_id, can_google_search=False, can_schema=False):
233
+ class OptionsWithThinkingBudget(OptionsWithGoogleSearch):
234
+ thinking_budget: Optional[int] = Field(
235
+ description="Indicates the thinking budget in tokens. Set to 0 to disable.",
236
+ default=None,
237
+ )
238
+
239
+ def __init__(
240
+ self,
241
+ model_id,
242
+ can_google_search=False,
243
+ can_thinking_budget=False,
244
+ can_schema=False,
245
+ ):
212
246
  self.model_id = model_id
213
247
  self.can_google_search = can_google_search
214
248
  self.supports_schema = can_schema
215
249
  if can_google_search:
216
250
  self.Options = self.OptionsWithGoogleSearch
251
+ self.can_thinking_budget = can_thinking_budget
252
+ if can_thinking_budget:
253
+ self.Options = self.OptionsWithThinkingBudget
217
254
 
218
255
  def build_messages(self, prompt, conversation):
219
256
  messages = []
@@ -262,14 +299,27 @@ class _SharedGemini:
262
299
  if prompt.options and prompt.options.code_execution:
263
300
  body["tools"] = [{"codeExecution": {}}]
264
301
  if prompt.options and self.can_google_search and prompt.options.google_search:
265
- body["tools"] = [{"google_search": {}}]
302
+ tool_name = (
303
+ "google_search_retrieval"
304
+ if self.model_id in GOOGLE_SEARCH_MODELS_USING_SEARCH_RETRIEVAL
305
+ else "google_search"
306
+ )
307
+ body["tools"] = [{tool_name: {}}]
266
308
  if prompt.system:
267
309
  body["systemInstruction"] = {"parts": [{"text": prompt.system}]}
268
310
 
311
+ generation_config = {}
312
+
269
313
  if prompt.schema:
270
- body["generationConfig"] = {
271
- "response_mime_type": "application/json",
272
- "response_schema": cleanup_schema(copy.deepcopy(prompt.schema)),
314
+ generation_config.update(
315
+ {
316
+ "response_mime_type": "application/json",
317
+ "response_schema": cleanup_schema(copy.deepcopy(prompt.schema)),
318
+ }
319
+ )
320
+ if self.can_thinking_budget and prompt.options.thinking_budget is not None:
321
+ generation_config["thinking_config"] = {
322
+ "thinking_budget": prompt.options.thinking_budget
273
323
  }
274
324
 
275
325
  config_map = {
@@ -279,16 +329,17 @@ class _SharedGemini:
279
329
  "top_k": "topK",
280
330
  }
281
331
  if prompt.options and prompt.options.json_object:
282
- body["generationConfig"] = {"response_mime_type": "application/json"}
332
+ generation_config["response_mime_type"] = "application/json"
283
333
 
284
334
  if any(
285
335
  getattr(prompt.options, key, None) is not None for key in config_map.keys()
286
336
  ):
287
- generation_config = {}
288
337
  for key, other_key in config_map.items():
289
338
  config_value = getattr(prompt.options, key, None)
290
339
  if config_value is not None:
291
340
  generation_config[other_key] = config_value
341
+
342
+ if generation_config:
292
343
  body["generationConfig"] = generation_config
293
344
 
294
345
  return body
@@ -458,9 +509,12 @@ def register_commands(cli):
458
509
  def models(key):
459
510
  "List of Gemini models pulled from their API"
460
511
  key = llm.get_key(key, "gemini", "LLM_GEMINI_KEY")
461
- response = httpx.get(
462
- f"https://generativelanguage.googleapis.com/v1beta/models?key={key}",
463
- )
512
+ if not key:
513
+ raise click.ClickException(
514
+ "You must set the LLM_GEMINI_KEY environment variable or use --key"
515
+ )
516
+ url = f"https://generativelanguage.googleapis.com/v1beta/models"
517
+ response = httpx.get(url, headers={"x-goog-api-key": key})
464
518
  response.raise_for_status()
465
519
  click.echo(json.dumps(response.json()["models"], indent=2))
466
520
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "llm-gemini"
3
- version = "0.17"
3
+ version = "0.18.1"
4
4
  description = "LLM plugin to access Google's Gemini family of models"
5
5
  readme = "README.md"
6
6
  authors = [{name = "Simon Willison"}]
@@ -1,4 +1,6 @@
1
+ from click.testing import CliRunner
1
2
  import llm
3
+ from llm.cli import cli
2
4
  import nest_asyncio
3
5
  import json
4
6
  import os
@@ -210,3 +212,22 @@ def test_cleanup_schema(schema, expected):
210
212
  # Use a deep copy so the original test data remains unchanged.
211
213
  result = cleanup_schema(schema)
212
214
  assert result == expected
215
+
216
+
217
+ @pytest.mark.vcr
218
+ def test_cli_gemini_models(tmpdir, monkeypatch):
219
+ user_dir = tmpdir / "llm.datasette.io"
220
+ user_dir.mkdir()
221
+ monkeypatch.setenv("LLM_USER_PATH", str(user_dir))
222
+ # With no key set should error nicely
223
+ runner = CliRunner()
224
+ result = runner.invoke(cli, ["gemini", "models"])
225
+ assert result.exit_code == 1
226
+ assert (
227
+ "Error: You must set the LLM_GEMINI_KEY environment variable or use --key\n"
228
+ == result.output
229
+ )
230
+ # Try again with --key
231
+ result2 = runner.invoke(cli, ["gemini", "models", "--key", GEMINI_API_KEY])
232
+ assert result2.exit_code == 0
233
+ assert "gemini-1.5-flash-latest" in result2.output
File without changes
File without changes