gemini-webapi 1.6.1__tar.gz → 1.8.0__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.
Files changed (37) hide show
  1. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.github/workflows/pypi-publish.yml +2 -2
  2. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/PKG-INFO +36 -8
  3. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/README.md +32 -4
  4. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/pyproject.toml +3 -3
  5. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/client.py +52 -21
  6. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/constants.py +25 -0
  7. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/types/image.py +4 -4
  8. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/get_access_token.py +4 -4
  9. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/rotate_1psidts.py +4 -4
  10. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/upload_file.py +4 -4
  11. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi.egg-info/PKG-INFO +36 -8
  12. gemini_webapi-1.8.0/src/gemini_webapi.egg-info/requires.txt +3 -0
  13. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/tests/test_client_features.py +13 -4
  14. gemini_webapi-1.6.1/src/gemini_webapi.egg-info/requires.txt +0 -3
  15. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.github/dependabot.yml +0 -0
  16. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.github/workflows/github-release.yml +0 -0
  17. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.gitignore +0 -0
  18. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.vscode/launch.json +0 -0
  19. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/.vscode/settings.json +0 -0
  20. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/LICENSE +0 -0
  21. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/assets/banner.png +0 -0
  22. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/assets/favicon.png +0 -0
  23. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/assets/logo.svg +0 -0
  24. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/setup.cfg +0 -0
  25. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/__init__.py +0 -0
  26. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/exceptions.py +0 -0
  27. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/types/__init__.py +0 -0
  28. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/types/candidate.py +0 -0
  29. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/types/modeloutput.py +0 -0
  30. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/__init__.py +0 -0
  31. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/load_browser_cookies.py +0 -0
  32. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi/utils/logger.py +0 -0
  33. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi.egg-info/SOURCES.txt +0 -0
  34. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi.egg-info/dependency_links.txt +0 -0
  35. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/src/gemini_webapi.egg-info/top_level.txt +0 -0
  36. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/tests/test_rotate_cookies.py +0 -0
  37. {gemini_webapi-1.6.1 → gemini_webapi-1.8.0}/tests/test_save_image.py +0 -0
@@ -36,7 +36,7 @@ jobs:
36
36
  - name: Build package
37
37
  run: python -m build
38
38
  - name: Archive production artifacts
39
- uses: actions/upload-artifact@v4.4.0
39
+ uses: actions/upload-artifact@v4.4.3
40
40
  with:
41
41
  name: dist
42
42
  path: dist
@@ -57,4 +57,4 @@ jobs:
57
57
  name: dist
58
58
  path: dist
59
59
  - name: Publish package distributions to PyPI
60
- uses: pypa/gh-action-pypi-publish@v1.10.1
60
+ uses: pypa/gh-action-pypi-publish@v1.12.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gemini-webapi
3
- Version: 1.6.1
3
+ Version: 1.8.0
4
4
  Summary: ✨ An elegant async Python wrapper for Google Gemini web app
5
5
  Author: UZQueen
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -676,9 +676,9 @@ Classifier: Programming Language :: Python :: 3.12
676
676
  Requires-Python: >=3.10
677
677
  Description-Content-Type: text/markdown
678
678
  License-File: LICENSE
679
- Requires-Dist: httpx>=0.25.2
680
- Requires-Dist: pydantic>=2.5.3
681
- Requires-Dist: loguru>=0.7.2
679
+ Requires-Dist: httpx~=0.28.0
680
+ Requires-Dist: pydantic~=2.10.2
681
+ Requires-Dist: loguru~=0.7.2
682
682
 
683
683
  <p align="center">
684
684
  <img src="https://raw.githubusercontent.com/HanaokaYuzu/Gemini-API/master/assets/banner.png" width="55%" alt="Gemini Banner" align="center">
@@ -732,6 +732,7 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
732
732
  - [Retrieve images in response](#retrieve-images-in-response)
733
733
  - [Generate images with ImageFx](#generate-images-with-imagefx)
734
734
  - [Save images to local files](#save-images-to-local-files)
735
+ - [Specify language model version](#specify-language-model-version)
735
736
  - [Generate contents with Gemini extensions](#generate-contents-with-gemini-extensions)
736
737
  - [Check and switch to other reply candidates](#check-and-switch-to-other-reply-candidates)
737
738
  - [Control log level](#control-log-level)
@@ -774,9 +775,9 @@ pip install -U browser-cookie3
774
775
 
775
776
  ```yaml
776
777
  services:
777
- main:
778
- volumes:
779
- - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
778
+ main:
779
+ volumes:
780
+ - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
780
781
  ```
781
782
 
782
783
  > [!NOTE]
@@ -804,7 +805,7 @@ Secure_1PSIDTS = "COOKIE VALUE HERE"
804
805
 
805
806
  async def main():
806
807
  # If browser-cookie3 is installed, simply use `client = GeminiClient()`
807
- client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxies=None)
808
+ client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxy=None)
808
809
  await client.init(timeout=30, auto_close=False, close_delay=300, auto_refresh=True)
809
810
 
810
811
  asyncio.run(main())
@@ -937,6 +938,33 @@ async def main():
937
938
  asyncio.run(main())
938
939
  ```
939
940
 
941
+ ### Specify language model version
942
+
943
+ You can choose a specified language model version by passing `model` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. The default value is `unspecified`.
944
+
945
+ Currently available models (as of Dec 21, 2024):
946
+
947
+ - `unspecified` - Default model (Gemini 1.5 Flash)
948
+ - `gemini-1.5-flash` - Gemini 1.5 Flash
949
+ - `gemini-2.0-flash-exp` - Gemini 2.0 Flash Experimental
950
+
951
+ ```python
952
+ from gemini_webapi.constants import Model
953
+
954
+ async def main():
955
+ response1 = await client.generate_content(
956
+ "What's you language model version? Reply version number only.",
957
+ model="gemini-1.5-flash",
958
+ )
959
+ print(f"Model version (gemini-1.5-flash): {response1.text}")
960
+
961
+ chat = client.start_chat(model=Model.G_2_0_FLASH_EXP)
962
+ response2 = await chat.send_message("What's you language model version? Reply version number only.")
963
+ print(f"Model version ({Model.G_2_0_FLASH_EXP.model_name}): {response2.text}")
964
+
965
+ asyncio.run(main())
966
+ ```
967
+
940
968
  ### Generate contents with Gemini extensions
941
969
 
942
970
  > [!IMPORTANT]
@@ -50,6 +50,7 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
50
50
  - [Retrieve images in response](#retrieve-images-in-response)
51
51
  - [Generate images with ImageFx](#generate-images-with-imagefx)
52
52
  - [Save images to local files](#save-images-to-local-files)
53
+ - [Specify language model version](#specify-language-model-version)
53
54
  - [Generate contents with Gemini extensions](#generate-contents-with-gemini-extensions)
54
55
  - [Check and switch to other reply candidates](#check-and-switch-to-other-reply-candidates)
55
56
  - [Control log level](#control-log-level)
@@ -92,9 +93,9 @@ pip install -U browser-cookie3
92
93
 
93
94
  ```yaml
94
95
  services:
95
- main:
96
- volumes:
97
- - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
96
+ main:
97
+ volumes:
98
+ - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
98
99
  ```
99
100
 
100
101
  > [!NOTE]
@@ -122,7 +123,7 @@ Secure_1PSIDTS = "COOKIE VALUE HERE"
122
123
 
123
124
  async def main():
124
125
  # If browser-cookie3 is installed, simply use `client = GeminiClient()`
125
- client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxies=None)
126
+ client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxy=None)
126
127
  await client.init(timeout=30, auto_close=False, close_delay=300, auto_refresh=True)
127
128
 
128
129
  asyncio.run(main())
@@ -255,6 +256,33 @@ async def main():
255
256
  asyncio.run(main())
256
257
  ```
257
258
 
259
+ ### Specify language model version
260
+
261
+ You can choose a specified language model version by passing `model` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. The default value is `unspecified`.
262
+
263
+ Currently available models (as of Dec 21, 2024):
264
+
265
+ - `unspecified` - Default model (Gemini 1.5 Flash)
266
+ - `gemini-1.5-flash` - Gemini 1.5 Flash
267
+ - `gemini-2.0-flash-exp` - Gemini 2.0 Flash Experimental
268
+
269
+ ```python
270
+ from gemini_webapi.constants import Model
271
+
272
+ async def main():
273
+ response1 = await client.generate_content(
274
+ "What's you language model version? Reply version number only.",
275
+ model="gemini-1.5-flash",
276
+ )
277
+ print(f"Model version (gemini-1.5-flash): {response1.text}")
278
+
279
+ chat = client.start_chat(model=Model.G_2_0_FLASH_EXP)
280
+ response2 = await chat.send_message("What's you language model version? Reply version number only.")
281
+ print(f"Model version ({Model.G_2_0_FLASH_EXP.model_name}): {response2.text}")
282
+
283
+ asyncio.run(main())
284
+ ```
285
+
258
286
  ### Generate contents with Gemini extensions
259
287
 
260
288
  > [!IMPORTANT]
@@ -20,9 +20,9 @@ classifiers = [
20
20
  ]
21
21
  requires-python = ">=3.10"
22
22
  dependencies = [
23
- "httpx>=0.25.2",
24
- "pydantic>=2.5.3",
25
- "loguru>=0.7.2",
23
+ "httpx~=0.28.0",
24
+ "pydantic~=2.10.2",
25
+ "loguru~=0.7.2",
26
26
  ]
27
27
  dynamic = ["version"]
28
28
 
@@ -8,7 +8,7 @@ from typing import Any, Optional
8
8
 
9
9
  from httpx import AsyncClient, ReadTimeout
10
10
 
11
- from .constants import Endpoint, Headers
11
+ from .constants import Endpoint, Headers, Model
12
12
  from .exceptions import AuthError, APIError, TimeoutError, GeminiError
13
13
  from .types import WebImage, GeneratedImage, Candidate, ModelOutput
14
14
  from .utils import (
@@ -77,8 +77,11 @@ class GeminiClient:
77
77
  __Secure-1PSID cookie value.
78
78
  secure_1psidts: `str`, optional
79
79
  __Secure-1PSIDTS cookie value, some google accounts don't require this value, provide only if it's in the cookie list.
80
- proxies: `dict`, optional
81
- Dict of proxies.
80
+ proxy: `str`, optional
81
+ Proxy URL.
82
+ kwargs: `dict`, optional
83
+ Additional arguments which will be passed to the http client.
84
+ Refer to `httpx.AsyncClient` for more information.
82
85
 
83
86
  Raises
84
87
  ------
@@ -88,7 +91,7 @@ class GeminiClient:
88
91
 
89
92
  __slots__ = [
90
93
  "cookies",
91
- "proxies",
94
+ "proxy",
92
95
  "running",
93
96
  "client",
94
97
  "access_token",
@@ -98,16 +101,18 @@ class GeminiClient:
98
101
  "close_task",
99
102
  "auto_refresh",
100
103
  "refresh_interval",
104
+ "kwargs",
101
105
  ]
102
106
 
103
107
  def __init__(
104
108
  self,
105
109
  secure_1psid: str | None = None,
106
110
  secure_1psidts: str | None = None,
107
- proxies: dict | None = None,
111
+ proxy: str | None = None,
112
+ **kwargs,
108
113
  ):
109
114
  self.cookies = {}
110
- self.proxies = proxies
115
+ self.proxy = proxy
111
116
  self.running: bool = False
112
117
  self.client: AsyncClient | None = None
113
118
  self.access_token: str | None = None
@@ -117,6 +122,7 @@ class GeminiClient:
117
122
  self.close_task: Task | None = None
118
123
  self.auto_refresh: bool = True
119
124
  self.refresh_interval: float = 540
125
+ self.kwargs = kwargs
120
126
 
121
127
  # Validate cookies
122
128
  if secure_1psid:
@@ -164,15 +170,16 @@ class GeminiClient:
164
170
 
165
171
  try:
166
172
  access_token, valid_cookies = await get_access_token(
167
- base_cookies=self.cookies, proxies=self.proxies, verbose=verbose
173
+ base_cookies=self.cookies, proxy=self.proxy, verbose=verbose
168
174
  )
169
175
 
170
176
  self.client = AsyncClient(
171
177
  timeout=timeout,
172
- proxies=self.proxies,
178
+ proxy=self.proxy,
173
179
  follow_redirects=True,
174
180
  headers=Headers.GEMINI.value,
175
181
  cookies=valid_cookies,
182
+ **self.kwargs,
176
183
  )
177
184
  self.access_token = access_token
178
185
  self.cookies = valid_cookies
@@ -238,7 +245,7 @@ class GeminiClient:
238
245
 
239
246
  while True:
240
247
  try:
241
- new_1psidts = await rotate_1psidts(self.cookies, self.proxies)
248
+ new_1psidts = await rotate_1psidts(self.cookies, self.proxy)
242
249
  except AuthError:
243
250
  if task := rotate_tasks.get(self.cookies["__Secure-1PSID"]):
244
251
  task.cancel()
@@ -256,7 +263,9 @@ class GeminiClient:
256
263
  self,
257
264
  prompt: str,
258
265
  images: list[bytes | str | Path] | None = None,
266
+ model: Model | str = Model.UNSPECIFIED,
259
267
  chat: Optional["ChatSession"] = None,
268
+ **kwargs,
260
269
  ) -> ModelOutput:
261
270
  """
262
271
  Generates contents with prompt.
@@ -267,8 +276,14 @@ class GeminiClient:
267
276
  Prompt provided by user.
268
277
  images: `list[bytes | str | Path]`, optional
269
278
  List of image file paths or file data in bytes.
279
+ model: `Model` | `str`, optional
280
+ Specify the model to use for generation.
281
+ Pass either a `gemini_webapi.constants.Model` enum or a model name string.
270
282
  chat: `ChatSession`, optional
271
283
  Chat data to retrieve conversation history. If None, will automatically generate a new chat id when sending post request.
284
+ kwargs: `dict`, optional
285
+ Additional arguments which will be passed to the post request.
286
+ Refer to `httpx.AsyncClient.request` for more information.
272
287
 
273
288
  Returns
274
289
  -------
@@ -291,12 +306,16 @@ class GeminiClient:
291
306
 
292
307
  assert prompt, "Prompt cannot be empty."
293
308
 
309
+ if not isinstance(model, Model):
310
+ model = Model.from_name(model)
311
+
294
312
  if self.auto_close:
295
313
  await self.reset_close_task()
296
314
 
297
315
  try:
298
316
  response = await self.client.post(
299
317
  Endpoint.GENERATE.value,
318
+ headers=model.model_header,
300
319
  data={
301
320
  "at": self.access_token,
302
321
  "f.req": json.dumps(
@@ -311,12 +330,8 @@ class GeminiClient:
311
330
  None,
312
331
  [
313
332
  [
314
- [
315
- await upload_file(
316
- image, self.proxies
317
- ),
318
- 1,
319
- ]
333
+ [await upload_file(image, self.proxy)],
334
+ "filename.jpg",
320
335
  ]
321
336
  for image in images
322
337
  ],
@@ -329,6 +344,7 @@ class GeminiClient:
329
344
  ]
330
345
  ),
331
346
  },
347
+ **kwargs,
332
348
  )
333
349
  except ReadTimeout:
334
350
  raise TimeoutError(
@@ -377,7 +393,7 @@ class GeminiClient:
377
393
  url=image[0][0][0],
378
394
  title=image[7][0],
379
395
  alt=image[0][4],
380
- proxies=self.proxies,
396
+ proxy=self.proxy,
381
397
  )
382
398
  for image in candidate[12][1]
383
399
  ]
@@ -395,7 +411,7 @@ class GeminiClient:
395
411
  alt=len(image[3][5]) > i
396
412
  and image[3][5][i]
397
413
  or image[3][5][0],
398
- proxies=self.proxies,
414
+ proxy=self.proxy,
399
415
  cookies=self.cookies,
400
416
  )
401
417
  for i, image in enumerate(
@@ -435,12 +451,13 @@ class GeminiClient:
435
451
  Parameters
436
452
  ----------
437
453
  kwargs: `dict`, optional
438
- Other arguments to pass to `ChatSession.__init__`.
454
+ Additional arguments which will be passed to the chat session.
455
+ Refer to `gemini_webapi.ChatSession` for more information.
439
456
 
440
457
  Returns
441
458
  -------
442
459
  :class:`ChatSession`
443
- Empty chat object for retrieving conversation history.
460
+ Empty chat session object for retrieving conversation history.
444
461
  """
445
462
 
446
463
  return ChatSession(geminiclient=self, **kwargs)
@@ -462,9 +479,17 @@ class ChatSession:
462
479
  Reply id, if provided together with metadata, will override the second value in it.
463
480
  rcid: `str`, optional
464
481
  Reply candidate id, if provided together with metadata, will override the third value in it.
482
+ model: `Model` | `str`, optional
483
+ Specify the model to use for generation.
484
+ Pass either a `gemini_webapi.constants.Model` enum or a model name string.
465
485
  """
466
486
 
467
- __slots__ = ["__metadata", "geminiclient", "last_output"]
487
+ __slots__ = [
488
+ "__metadata",
489
+ "geminiclient",
490
+ "last_output",
491
+ "model",
492
+ ]
468
493
 
469
494
  def __init__(
470
495
  self,
@@ -473,10 +498,12 @@ class ChatSession:
473
498
  cid: str | None = None, # chat id
474
499
  rid: str | None = None, # reply id
475
500
  rcid: str | None = None, # reply candidate id
501
+ model: Model | str = Model.UNSPECIFIED,
476
502
  ):
477
503
  self.__metadata: list[str | None] = [None, None, None]
478
504
  self.geminiclient: GeminiClient = geminiclient
479
505
  self.last_output: ModelOutput | None = None
506
+ self.model = model
480
507
 
481
508
  if metadata:
482
509
  self.metadata = metadata
@@ -503,6 +530,7 @@ class ChatSession:
503
530
  self,
504
531
  prompt: str,
505
532
  images: list[bytes | str | Path] | None = None,
533
+ **kwargs,
506
534
  ) -> ModelOutput:
507
535
  """
508
536
  Generates contents with prompt.
@@ -514,6 +542,9 @@ class ChatSession:
514
542
  Prompt provided by user.
515
543
  images: `list[bytes | str | Path]`, optional
516
544
  List of image file paths or file data in bytes.
545
+ kwargs: `dict`, optional
546
+ Additional arguments which will be passed to the post request.
547
+ Refer to `httpx.AsyncClient.request` for more information.
517
548
 
518
549
  Returns
519
550
  -------
@@ -535,7 +566,7 @@ class ChatSession:
535
566
  """
536
567
 
537
568
  return await self.geminiclient.generate_content(
538
- prompt=prompt, images=images, chat=self
569
+ prompt=prompt, images=images, model=self.model, chat=self, **kwargs
539
570
  )
540
571
 
541
572
  def choose_candidate(self, index: int) -> ModelOutput:
@@ -21,3 +21,28 @@ class Headers(Enum):
21
21
  "Content-Type": "application/json",
22
22
  }
23
23
  UPLOAD = {"Push-ID": "feeds/mcudyrk2a4khkz"}
24
+
25
+
26
+ class Model(Enum):
27
+ UNSPECIFIED = ("unspecified", {})
28
+ G_1_5_FLASH = (
29
+ "gemini-1.5-flash",
30
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"7daceb7ef88130f5"]'},
31
+ )
32
+ G_2_0_FLASH_EXP = (
33
+ "gemini-2.0-flash-exp",
34
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"948b866104ccf484"]'},
35
+ )
36
+
37
+ def __init__(self, name, header):
38
+ self.model_name = name
39
+ self.model_header = header
40
+
41
+ @classmethod
42
+ def from_name(cls, name: str):
43
+ for model in cls:
44
+ if model.model_name == name:
45
+ return model
46
+ raise ValueError(
47
+ f"Unknown model name: {name}. Available models: {', '.join([model.model_name for model in cls])}"
48
+ )
@@ -20,14 +20,14 @@ class Image(BaseModel):
20
20
  Title of the image, by default is "[Image]".
21
21
  alt: `str`, optional
22
22
  Optional description of the image.
23
- proxies: `dict`, optional
24
- Dict of proxies used when saving image.
23
+ proxy: `str`, optional
24
+ Proxy used when saving image.
25
25
  """
26
26
 
27
27
  url: str
28
28
  title: str = "[Image]"
29
29
  alt: str = ""
30
- proxies: dict | None = None
30
+ proxy: str | None = None
31
31
 
32
32
  def __str__(self):
33
33
  return f"{self.title}({self.url}) - {self.alt}"
@@ -80,7 +80,7 @@ class Image(BaseModel):
80
80
  return None
81
81
 
82
82
  async with AsyncClient(
83
- follow_redirects=True, cookies=cookies, proxies=self.proxies
83
+ follow_redirects=True, cookies=cookies, proxy=self.proxy
84
84
  ) as client:
85
85
  response = await client.get(self.url)
86
86
  if response.status_code == 200:
@@ -12,7 +12,7 @@ from .logger import logger
12
12
 
13
13
 
14
14
  async def get_access_token(
15
- base_cookies: dict, proxies: dict | None = None, verbose: bool = False
15
+ base_cookies: dict, proxy: str | None = None, verbose: bool = False
16
16
  ) -> tuple[str, dict]:
17
17
  """
18
18
  Send a get request to gemini.google.com for each group of available cookies and return
@@ -27,8 +27,8 @@ async def get_access_token(
27
27
  ----------
28
28
  base_cookies : `dict`
29
29
  Base cookies to be used in the request.
30
- proxies: `dict`, optional
31
- Dict of proxies.
30
+ proxy: `str`, optional
31
+ Proxy URL.
32
32
  verbose: `bool`, optional
33
33
  If `True`, will print more infomation in logs.
34
34
 
@@ -47,7 +47,7 @@ async def get_access_token(
47
47
 
48
48
  async def send_request(cookies: dict) -> tuple[Response | None, dict]:
49
49
  async with AsyncClient(
50
- proxies=proxies,
50
+ proxy=proxy,
51
51
  headers=Headers.GEMINI.value,
52
52
  cookies=cookies,
53
53
  follow_redirects=True,
@@ -8,7 +8,7 @@ from ..constants import Endpoint, Headers
8
8
  from ..exceptions import AuthError
9
9
 
10
10
 
11
- async def rotate_1psidts(cookies: dict, proxies: dict | None = None) -> str:
11
+ async def rotate_1psidts(cookies: dict, proxy: str | None = None) -> str:
12
12
  """
13
13
  Refresh the __Secure-1PSIDTS cookie and store the refreshed cookie value in cache file.
14
14
 
@@ -16,8 +16,8 @@ async def rotate_1psidts(cookies: dict, proxies: dict | None = None) -> str:
16
16
  ----------
17
17
  cookies : `dict`
18
18
  Cookies to be used in the request.
19
- proxies: `dict`, optional
20
- Dict of proxies.
19
+ proxy: `str`, optional
20
+ Proxy URL.
21
21
 
22
22
  Returns
23
23
  -------
@@ -39,7 +39,7 @@ async def rotate_1psidts(cookies: dict, proxies: dict | None = None) -> str:
39
39
 
40
40
  # Check if the cache file was modified in the last minute to avoid 429 Too Many Requests
41
41
  if not (path.is_file() and time.time() - os.path.getmtime(path) <= 60):
42
- async with AsyncClient(proxies=proxies) as client:
42
+ async with AsyncClient(proxy=proxy) as client:
43
43
  response = await client.post(
44
44
  url=Endpoint.ROTATE_COOKIES.value,
45
45
  headers=Headers.ROTATE_COOKIES.value,
@@ -7,7 +7,7 @@ from ..constants import Endpoint, Headers
7
7
 
8
8
 
9
9
  @validate_call
10
- async def upload_file(file: bytes | str | Path, proxies: dict | None = None) -> str:
10
+ async def upload_file(file: bytes | str | Path, proxy: str | None = None) -> str:
11
11
  """
12
12
  Upload a file to Google's server and return its identifier.
13
13
 
@@ -15,8 +15,8 @@ async def upload_file(file: bytes | str | Path, proxies: dict | None = None) ->
15
15
  ----------
16
16
  file : `bytes` | `str` | `Path`
17
17
  File data in bytes, or path to the file to be uploaded.
18
- proxies: `dict`, optional
19
- Dict of proxies.
18
+ proxy: `str`, optional
19
+ Proxy URL.
20
20
 
21
21
  Returns
22
22
  -------
@@ -34,7 +34,7 @@ async def upload_file(file: bytes | str | Path, proxies: dict | None = None) ->
34
34
  with open(file, "rb") as f:
35
35
  file = f.read()
36
36
 
37
- async with AsyncClient(proxies=proxies) as client:
37
+ async with AsyncClient(proxy=proxy) as client:
38
38
  response = await client.post(
39
39
  url=Endpoint.UPLOAD.value,
40
40
  headers=Headers.UPLOAD.value,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gemini-webapi
3
- Version: 1.6.1
3
+ Version: 1.8.0
4
4
  Summary: ✨ An elegant async Python wrapper for Google Gemini web app
5
5
  Author: UZQueen
6
6
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -676,9 +676,9 @@ Classifier: Programming Language :: Python :: 3.12
676
676
  Requires-Python: >=3.10
677
677
  Description-Content-Type: text/markdown
678
678
  License-File: LICENSE
679
- Requires-Dist: httpx>=0.25.2
680
- Requires-Dist: pydantic>=2.5.3
681
- Requires-Dist: loguru>=0.7.2
679
+ Requires-Dist: httpx~=0.28.0
680
+ Requires-Dist: pydantic~=2.10.2
681
+ Requires-Dist: loguru~=0.7.2
682
682
 
683
683
  <p align="center">
684
684
  <img src="https://raw.githubusercontent.com/HanaokaYuzu/Gemini-API/master/assets/banner.png" width="55%" alt="Gemini Banner" align="center">
@@ -732,6 +732,7 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
732
732
  - [Retrieve images in response](#retrieve-images-in-response)
733
733
  - [Generate images with ImageFx](#generate-images-with-imagefx)
734
734
  - [Save images to local files](#save-images-to-local-files)
735
+ - [Specify language model version](#specify-language-model-version)
735
736
  - [Generate contents with Gemini extensions](#generate-contents-with-gemini-extensions)
736
737
  - [Check and switch to other reply candidates](#check-and-switch-to-other-reply-candidates)
737
738
  - [Control log level](#control-log-level)
@@ -774,9 +775,9 @@ pip install -U browser-cookie3
774
775
 
775
776
  ```yaml
776
777
  services:
777
- main:
778
- volumes:
779
- - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
778
+ main:
779
+ volumes:
780
+ - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
780
781
  ```
781
782
 
782
783
  > [!NOTE]
@@ -804,7 +805,7 @@ Secure_1PSIDTS = "COOKIE VALUE HERE"
804
805
 
805
806
  async def main():
806
807
  # If browser-cookie3 is installed, simply use `client = GeminiClient()`
807
- client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxies=None)
808
+ client = GeminiClient(Secure_1PSID, Secure_1PSIDTS, proxy=None)
808
809
  await client.init(timeout=30, auto_close=False, close_delay=300, auto_refresh=True)
809
810
 
810
811
  asyncio.run(main())
@@ -937,6 +938,33 @@ async def main():
937
938
  asyncio.run(main())
938
939
  ```
939
940
 
941
+ ### Specify language model version
942
+
943
+ You can choose a specified language model version by passing `model` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. The default value is `unspecified`.
944
+
945
+ Currently available models (as of Dec 21, 2024):
946
+
947
+ - `unspecified` - Default model (Gemini 1.5 Flash)
948
+ - `gemini-1.5-flash` - Gemini 1.5 Flash
949
+ - `gemini-2.0-flash-exp` - Gemini 2.0 Flash Experimental
950
+
951
+ ```python
952
+ from gemini_webapi.constants import Model
953
+
954
+ async def main():
955
+ response1 = await client.generate_content(
956
+ "What's you language model version? Reply version number only.",
957
+ model="gemini-1.5-flash",
958
+ )
959
+ print(f"Model version (gemini-1.5-flash): {response1.text}")
960
+
961
+ chat = client.start_chat(model=Model.G_2_0_FLASH_EXP)
962
+ response2 = await chat.send_message("What's you language model version? Reply version number only.")
963
+ print(f"Model version ({Model.G_2_0_FLASH_EXP.model_name}): {response2.text}")
964
+
965
+ asyncio.run(main())
966
+ ```
967
+
940
968
  ### Generate contents with Gemini extensions
941
969
 
942
970
  > [!IMPORTANT]
@@ -0,0 +1,3 @@
1
+ httpx~=0.28.0
2
+ pydantic~=2.10.2
3
+ loguru~=0.7.2
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from loguru import logger
7
7
 
8
8
  from gemini_webapi import GeminiClient, AuthError, set_log_level
9
+ from gemini_webapi.constants import Model
9
10
 
10
11
  logging.getLogger("asyncio").setLevel(logging.ERROR)
11
12
  set_log_level("DEBUG")
@@ -27,10 +28,20 @@ class TestGeminiClient(unittest.IsolatedAsyncioTestCase):
27
28
  response = await self.geminiclient.generate_content("Hello World!")
28
29
  self.assertTrue(response.text)
29
30
 
31
+ @logger.catch(reraise=True)
32
+ async def test_switch_model(self):
33
+ for model in Model:
34
+ response = await self.geminiclient.generate_content(
35
+ "What's you language model version? Reply version number only.",
36
+ model=model,
37
+ )
38
+ logger.debug(f"Model version ({model.model_name}): {response.text}")
39
+
30
40
  @logger.catch(reraise=True)
31
41
  async def test_upload_image(self):
32
42
  response = await self.geminiclient.generate_content(
33
- "Describe these images", images=[Path("assets/banner.png"), "assets/favicon.png"]
43
+ "Describe these images",
44
+ images=[Path("assets/banner.png"), "assets/favicon.png"],
34
45
  )
35
46
  logger.debug(response.text)
36
47
 
@@ -86,9 +97,7 @@ class TestGeminiClient(unittest.IsolatedAsyncioTestCase):
86
97
 
87
98
  @logger.catch(reraise=True)
88
99
  async def test_card_content(self):
89
- response = await self.geminiclient.generate_content(
90
- "How is today's weather?"
91
- )
100
+ response = await self.geminiclient.generate_content("How is today's weather?")
92
101
  logger.debug(response.text)
93
102
 
94
103
  @logger.catch(reraise=True)
@@ -1,3 +0,0 @@
1
- httpx>=0.25.2
2
- pydantic>=2.5.3
3
- loguru>=0.7.2
File without changes
File without changes
File without changes