gemini-webapi 1.17.0__tar.gz → 1.17.2__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 (43) hide show
  1. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.github/workflows/github-release.yml +1 -1
  2. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.github/workflows/pypi-publish.yml +3 -3
  3. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/PKG-INFO +23 -6
  4. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/README.md +22 -5
  5. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/client.py +32 -19
  6. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/constants.py +31 -7
  7. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/image.py +27 -10
  8. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/decorators.py +4 -3
  9. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/parsing.py +2 -2
  10. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi.egg-info/PKG-INFO +23 -6
  11. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.github/dependabot.yml +0 -0
  12. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.gitignore +0 -0
  13. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.vscode/launch.json +0 -0
  14. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/.vscode/settings.json +0 -0
  15. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/LICENSE +0 -0
  16. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/assets/banner.png +0 -0
  17. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/assets/favicon.png +0 -0
  18. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/assets/logo.svg +0 -0
  19. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/assets/sample.pdf +0 -0
  20. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/pyproject.toml +0 -0
  21. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/setup.cfg +0 -0
  22. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/__init__.py +0 -0
  23. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/components/__init__.py +0 -0
  24. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/components/gem_mixin.py +0 -0
  25. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/exceptions.py +0 -0
  26. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/__init__.py +0 -0
  27. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/candidate.py +0 -0
  28. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/gem.py +0 -0
  29. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/grpc.py +0 -0
  30. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/types/modeloutput.py +0 -0
  31. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/__init__.py +0 -0
  32. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/get_access_token.py +0 -0
  33. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/load_browser_cookies.py +0 -0
  34. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/logger.py +0 -0
  35. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/rotate_1psidts.py +0 -0
  36. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi/utils/upload_file.py +0 -0
  37. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi.egg-info/SOURCES.txt +0 -0
  38. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi.egg-info/dependency_links.txt +0 -0
  39. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi.egg-info/requires.txt +0 -0
  40. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/src/gemini_webapi.egg-info/top_level.txt +0 -0
  41. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/tests/test_client_features.py +0 -0
  42. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/tests/test_gem_mixin.py +0 -0
  43. {gemini_webapi-1.17.0 → gemini_webapi-1.17.2}/tests/test_save_image.py +0 -0
@@ -11,7 +11,7 @@ jobs:
11
11
  permissions:
12
12
  contents: write
13
13
  steps:
14
- - uses: actions/checkout@v5
14
+ - uses: actions/checkout@v6
15
15
  - uses: ncipollo/release-action@v1
16
16
  with:
17
17
  body: ${{ github.event.head_commit.message }}
@@ -24,7 +24,7 @@ jobs:
24
24
  name: Build package
25
25
  runs-on: ubuntu-latest
26
26
  steps:
27
- - uses: actions/checkout@v5
27
+ - uses: actions/checkout@v6
28
28
  - name: Set up Python
29
29
  uses: actions/setup-python@v6
30
30
  with:
@@ -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.6.2
39
+ uses: actions/upload-artifact@v5.0.0
40
40
  with:
41
41
  name: dist
42
42
  path: dist
@@ -52,7 +52,7 @@ jobs:
52
52
  id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
53
53
  steps:
54
54
  - name: Retrieve built artifacts
55
- uses: actions/download-artifact@v5.0.0
55
+ uses: actions/download-artifact@v6.0.0
56
56
  with:
57
57
  name: dist
58
58
  path: dist
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-webapi
3
- Version: 1.17.0
3
+ Version: 1.17.2
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
@@ -783,11 +783,11 @@ pip install -U browser-cookie3
783
783
 
784
784
  ```yaml
785
785
  services:
786
- main:
787
- environment:
788
- GEMINI_COOKIE_PATH: /tmp/gemini_webapi
789
- volumes:
790
- - ./gemini_cookies:/tmp/gemini_webapi
786
+ main:
787
+ environment:
788
+ GEMINI_COOKIE_PATH: /tmp/gemini_webapi
789
+ volumes:
790
+ - ./gemini_cookies:/tmp/gemini_webapi
791
791
  ```
792
792
 
793
793
  > [!NOTE]
@@ -929,6 +929,23 @@ async def main():
929
929
  asyncio.run(main())
930
930
  ```
931
931
 
932
+ You can also pass custom model header strings directly to access models which are not listed above.
933
+
934
+ ```python
935
+ # "model_name" and "model_header" keys must be present
936
+ custom_model = {
937
+ "model_name": "xxx",
938
+ "model_header": {
939
+ "x-goog-ext-525001261-jspb": "[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]"
940
+ },
941
+ }
942
+
943
+ response = await client.generate_content(
944
+ "What's your model version?",
945
+ model=custom_model
946
+ )
947
+ ```
948
+
932
949
  ### Apply system prompt with Gemini Gems
933
950
 
934
951
  System prompt can be applied to conversations via [Gemini Gems](https://gemini.google.com/gems/view). To use a gem, you can pass `gem` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. `gem` can be either a string of gem id or a `gemini_webapi.Gem` object. Only one gem can be applied to a single conversation.
@@ -99,11 +99,11 @@ pip install -U browser-cookie3
99
99
 
100
100
  ```yaml
101
101
  services:
102
- main:
103
- environment:
104
- GEMINI_COOKIE_PATH: /tmp/gemini_webapi
105
- volumes:
106
- - ./gemini_cookies:/tmp/gemini_webapi
102
+ main:
103
+ environment:
104
+ GEMINI_COOKIE_PATH: /tmp/gemini_webapi
105
+ volumes:
106
+ - ./gemini_cookies:/tmp/gemini_webapi
107
107
  ```
108
108
 
109
109
  > [!NOTE]
@@ -245,6 +245,23 @@ async def main():
245
245
  asyncio.run(main())
246
246
  ```
247
247
 
248
+ You can also pass custom model header strings directly to access models which are not listed above.
249
+
250
+ ```python
251
+ # "model_name" and "model_header" keys must be present
252
+ custom_model = {
253
+ "model_name": "xxx",
254
+ "model_header": {
255
+ "x-goog-ext-525001261-jspb": "[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]"
256
+ },
257
+ }
258
+
259
+ response = await client.generate_content(
260
+ "What's your model version?",
261
+ model=custom_model
262
+ )
263
+ ```
264
+
248
265
  ### Apply system prompt with Gemini Gems
249
266
 
250
267
  System prompt can be applied to conversations via [Gemini Gems](https://gemini.google.com/gems/view). To use a gem, you can pass `gem` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. `gem` can be either a string of gem id or a `gemini_webapi.Gem` object. Only one gem can be applied to a single conversation.
@@ -68,7 +68,7 @@ class GeminiClient(GemMixin):
68
68
  __slots__ = [
69
69
  "cookies",
70
70
  "proxy",
71
- "running",
71
+ "_running",
72
72
  "client",
73
73
  "access_token",
74
74
  "timeout",
@@ -91,7 +91,7 @@ class GeminiClient(GemMixin):
91
91
  super().__init__()
92
92
  self.cookies = {}
93
93
  self.proxy = proxy
94
- self.running: bool = False
94
+ self._running: bool = False
95
95
  self.client: AsyncClient | None = None
96
96
  self.access_token: str | None = None
97
97
  self.timeout: float = 300
@@ -152,7 +152,7 @@ class GeminiClient(GemMixin):
152
152
  )
153
153
  self.access_token = access_token
154
154
  self.cookies = valid_cookies
155
- self.running = True
155
+ self._running = True
156
156
 
157
157
  self.timeout = timeout
158
158
  self.auto_close = auto_close
@@ -188,7 +188,7 @@ class GeminiClient(GemMixin):
188
188
  if delay:
189
189
  await asyncio.sleep(delay)
190
190
 
191
- self.running = False
191
+ self._running = False
192
192
 
193
193
  if self.close_task:
194
194
  self.close_task.cancel()
@@ -229,7 +229,7 @@ class GeminiClient(GemMixin):
229
229
 
230
230
  if new_1psidts:
231
231
  self.cookies["__Secure-1PSIDTS"] = new_1psidts
232
- if self.running:
232
+ if self._running:
233
233
  self.client.cookies.set("__Secure-1PSIDTS", new_1psidts)
234
234
  logger.debug("Cookies refreshed. New __Secure-1PSIDTS applied.")
235
235
 
@@ -240,7 +240,7 @@ class GeminiClient(GemMixin):
240
240
  self,
241
241
  prompt: str,
242
242
  files: list[str | Path] | None = None,
243
- model: Model | str = Model.UNSPECIFIED,
243
+ model: Model | str | dict = Model.UNSPECIFIED,
244
244
  gem: Gem | str | None = None,
245
245
  chat: Optional["ChatSession"] = None,
246
246
  **kwargs,
@@ -254,9 +254,10 @@ class GeminiClient(GemMixin):
254
254
  Prompt provided by user.
255
255
  files: `list[str | Path]`, optional
256
256
  List of file paths to be attached.
257
- model: `Model` | `str`, optional
257
+ model: `Model | str | dict`, optional
258
258
  Specify the model to use for generation.
259
- Pass either a `gemini_webapi.constants.Model` enum or a model name string.
259
+ Pass either a `gemini_webapi.constants.Model` enum or a model name string to use predefined models.
260
+ Pass a dictionary to use custom model header strings ("model_name" and "model_header" keys must be provided).
260
261
  gem: `Gem | str`, optional
261
262
  Specify a gem to use as system prompt for the chat session.
262
263
  Pass either a `gemini_webapi.types.Gem` object or a gem id string.
@@ -287,8 +288,15 @@ class GeminiClient(GemMixin):
287
288
 
288
289
  assert prompt, "Prompt cannot be empty."
289
290
 
290
- if not isinstance(model, Model):
291
+ if isinstance(model, str):
291
292
  model = Model.from_name(model)
293
+ elif isinstance(model, dict):
294
+ model = Model.from_dict(model)
295
+ elif not isinstance(model, Model):
296
+ raise TypeError(
297
+ f"'model' must be a `gemini_webapi.constants.Model` instance, "
298
+ f"string, or dictionary; got `{type(model).__name__}`"
299
+ )
292
300
 
293
301
  if isinstance(gem, Gem):
294
302
  gem_id = gem.id
@@ -362,7 +370,7 @@ class GeminiClient(GemMixin):
362
370
  if get_nested_value(part_json, [4]):
363
371
  body_index, body = part_index, part_json
364
372
  break
365
- except (TypeError, ValueError):
373
+ except json.JSONDecodeError:
366
374
  continue
367
375
 
368
376
  if not body:
@@ -372,7 +380,7 @@ class GeminiClient(GemMixin):
372
380
 
373
381
  try:
374
382
  error_code = get_nested_value(response_json, [0, 5, 2, 0, 1, 0], -1)
375
- match ErrorCode(error_code):
383
+ match error_code:
376
384
  case ErrorCode.USAGE_LIMIT_EXCEEDED:
377
385
  raise UsageLimitExceeded(
378
386
  f"Failed to generate contents. Usage limit of {model.model_name} model has exceeded. Please try switching to another model."
@@ -443,13 +451,17 @@ class GeminiClient(GemMixin):
443
451
  if img_part_index < body_index:
444
452
  continue
445
453
  try:
446
- img_part = json.loads(part[2])
454
+ img_part_body = get_nested_value(part, [2])
455
+ if not img_part_body:
456
+ continue
457
+
458
+ img_part_json = json.loads(img_part_body)
447
459
  if get_nested_value(
448
- img_part, [4, candidate_index, 12, 7, 0]
460
+ img_part_json, [4, candidate_index, 12, 7, 0]
449
461
  ):
450
- img_body = img_part
462
+ img_body = img_part_json
451
463
  break
452
- except (IndexError, TypeError, ValueError):
464
+ except json.JSONDecodeError:
453
465
  continue
454
466
 
455
467
  if not img_body:
@@ -614,9 +626,10 @@ class ChatSession:
614
626
  Reply id, if provided together with metadata, will override the second value in it.
615
627
  rcid: `str`, optional
616
628
  Reply candidate id, if provided together with metadata, will override the third value in it.
617
- model: `Model` | `str`, optional
629
+ model: `Model | str | dict`, optional
618
630
  Specify the model to use for generation.
619
- Pass either a `gemini_webapi.constants.Model` enum or a model name string.
631
+ Pass either a `gemini_webapi.constants.Model` enum or a model name string to use predefined models.
632
+ Pass a dictionary to use custom model header strings ("model_name" and "model_header" keys must be provided).
620
633
  gem: `Gem | str`, optional
621
634
  Specify a gem to use as system prompt for the chat session.
622
635
  Pass either a `gemini_webapi.types.Gem` object or a gem id string.
@@ -637,13 +650,13 @@ class ChatSession:
637
650
  cid: str | None = None, # chat id
638
651
  rid: str | None = None, # reply id
639
652
  rcid: str | None = None, # reply candidate id
640
- model: Model | str = Model.UNSPECIFIED,
653
+ model: Model | str | dict = Model.UNSPECIFIED,
641
654
  gem: Gem | str | None = None,
642
655
  ):
643
656
  self.__metadata: list[str | None] = [None, None, None]
644
657
  self.geminiclient: GeminiClient = geminiclient
645
658
  self.last_output: ModelOutput | None = None
646
- self.model: Model | str = model
659
+ self.model: Model | str | dict = model
647
660
  self.gem: Gem | str | None = gem
648
661
 
649
662
  if metadata:
@@ -45,17 +45,23 @@ class Model(Enum):
45
45
  UNSPECIFIED = ("unspecified", {}, False)
46
46
  G_3_0_PRO = (
47
47
  "gemini-3.0-pro",
48
- {"x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,null,[4]]'},
49
- False,
50
- )
51
- G_2_5_FLASH = (
52
- "gemini-2.5-flash",
53
- {"x-goog-ext-525001261-jspb": '[1,null,null,null,"9ec249fc9ad08861",null,null,0,[4]]'},
48
+ {
49
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]'
50
+ },
54
51
  False,
55
52
  )
56
53
  G_2_5_PRO = (
57
54
  "gemini-2.5-pro",
58
- {"x-goog-ext-525001261-jspb": '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]'},
55
+ {
56
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]'
57
+ },
58
+ False,
59
+ )
60
+ G_2_5_FLASH = (
61
+ "gemini-2.5-flash",
62
+ {
63
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"9ec249fc9ad08861",null,null,0,[4]]'
64
+ },
59
65
  False,
60
66
  )
61
67
 
@@ -69,10 +75,28 @@ class Model(Enum):
69
75
  for model in cls:
70
76
  if model.model_name == name:
71
77
  return model
78
+
72
79
  raise ValueError(
73
80
  f"Unknown model name: {name}. Available models: {', '.join([model.model_name for model in cls])}"
74
81
  )
75
82
 
83
+ @classmethod
84
+ def from_dict(cls, model_dict: dict):
85
+ if "model_name" not in model_dict or "model_header" not in model_dict:
86
+ raise ValueError(
87
+ "When passing a custom model as a dictionary, 'model_name' and 'model_header' keys must be provided."
88
+ )
89
+
90
+ if not isinstance(model_dict["model_header"], dict):
91
+ raise ValueError(
92
+ "When passing a custom model as a dictionary, 'model_header' must be a dictionary containing valid header strings."
93
+ )
94
+
95
+ custom_model = cls.UNSPECIFIED
96
+ custom_model.model_name = model_dict["model_name"]
97
+ custom_model.model_header = model_dict["model_header"]
98
+ return custom_model
99
+
76
100
 
77
101
  class ErrorCode(IntEnum):
78
102
  """
@@ -71,9 +71,10 @@ class Image(BaseModel):
71
71
  """
72
72
 
73
73
  filename = filename or self.url.split("/")[-1].split("?")[0]
74
- try:
75
- filename = re.search(r"^(.*\.\w+)", filename).group()
76
- except AttributeError:
74
+ match = re.search(r"^(.*\.\w+)", filename)
75
+ if match:
76
+ filename = match.group()
77
+ else:
77
78
  if verbose:
78
79
  logger.warning(f"Invalid filename: {filename}")
79
80
  if skip_invalid_filename:
@@ -137,19 +138,33 @@ class GeneratedImage(Image):
137
138
  return v
138
139
 
139
140
  # @override
140
- async def save(self, full_size=True, **kwargs) -> str | None:
141
+ async def save(
142
+ self,
143
+ path: str = "temp",
144
+ filename: str | None = None,
145
+ cookies: dict | None = None,
146
+ verbose: bool = False,
147
+ skip_invalid_filename: bool = False,
148
+ full_size: bool = True,
149
+ ) -> str | None:
141
150
  """
142
151
  Save the image to disk.
143
152
 
144
153
  Parameters
145
154
  ----------
155
+ path: `str`, optional
156
+ Path to save the image, by default will save to "./temp".
146
157
  filename: `str`, optional
147
158
  Filename to save the image, generated images are always in .png format, but file extension will not be included in the URL.
148
159
  And since the URL ends with a long hash, by default will use timestamp + end of the hash as the filename.
160
+ cookies: `dict`, optional
161
+ Cookies used for requesting the content of the image. If not provided, will use the cookies from the GeneratedImage instance.
162
+ verbose : `bool`, optional
163
+ If True, will print the path of the saved file or warning for invalid file name, by default False.
164
+ skip_invalid_filename: `bool`, optional
165
+ If True, will only save the image if the file name and extension are valid, by default False.
149
166
  full_size: `bool`, optional
150
- If True, will modify the default preview (512*512) URL to get the full size image.
151
- kwargs: `dict`, optional
152
- Other arguments to pass to `Image.save`.
167
+ If True, will modify the default preview (512*512) URL to get the full size image, by default True.
153
168
 
154
169
  Returns
155
170
  -------
@@ -161,8 +176,10 @@ class GeneratedImage(Image):
161
176
  self.url += "=s2048"
162
177
 
163
178
  return await super().save(
164
- filename=kwargs.pop("filename", None)
179
+ path=path,
180
+ filename=filename
165
181
  or f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{self.url[-10:]}.png",
166
- cookies=self.cookies,
167
- **kwargs,
182
+ cookies=cookies or self.cookies,
183
+ verbose=verbose,
184
+ skip_invalid_filename=skip_invalid_filename,
168
185
  )
@@ -1,10 +1,11 @@
1
1
  import asyncio
2
2
  import functools
3
+ from collections.abc import Callable
3
4
 
4
5
  from ..exceptions import APIError, ImageGenerationError
5
6
 
6
7
 
7
- def running(retry: int = 0) -> callable:
8
+ def running(retry: int = 0) -> Callable:
8
9
  """
9
10
  Decorator to check if GeminiClient is running before making a request.
10
11
 
@@ -18,7 +19,7 @@ def running(retry: int = 0) -> callable:
18
19
  @functools.wraps(func)
19
20
  async def wrapper(client, *args, retry=retry, **kwargs):
20
21
  try:
21
- if not client.running:
22
+ if not client._running:
22
23
  await client.init(
23
24
  timeout=client.timeout,
24
25
  auto_close=client.auto_close,
@@ -27,7 +28,7 @@ def running(retry: int = 0) -> callable:
27
28
  refresh_interval=client.refresh_interval,
28
29
  verbose=False,
29
30
  )
30
- if client.running:
31
+ if client._running:
31
32
  return await func(client, *args, **kwargs)
32
33
 
33
34
  # Should not reach here
@@ -30,8 +30,8 @@ def get_nested_value(data: list, path: list[int], default: Any = None) -> Any:
30
30
  current_repr = f"{current_repr[:197]}..."
31
31
 
32
32
  logger.debug(
33
- f"{type(e).__name__}: parsing failed at path {path} (index {i}, key '{key}') "
34
- f"while attempting to get value from `{current_repr}`"
33
+ f"Safe navigation: path {path} ended at index {i} (key '{key}'), "
34
+ f"returning default. <Debug: {type(e).__name__}; Context: {current_repr}>"
35
35
  )
36
36
  return default
37
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-webapi
3
- Version: 1.17.0
3
+ Version: 1.17.2
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
@@ -783,11 +783,11 @@ pip install -U browser-cookie3
783
783
 
784
784
  ```yaml
785
785
  services:
786
- main:
787
- environment:
788
- GEMINI_COOKIE_PATH: /tmp/gemini_webapi
789
- volumes:
790
- - ./gemini_cookies:/tmp/gemini_webapi
786
+ main:
787
+ environment:
788
+ GEMINI_COOKIE_PATH: /tmp/gemini_webapi
789
+ volumes:
790
+ - ./gemini_cookies:/tmp/gemini_webapi
791
791
  ```
792
792
 
793
793
  > [!NOTE]
@@ -929,6 +929,23 @@ async def main():
929
929
  asyncio.run(main())
930
930
  ```
931
931
 
932
+ You can also pass custom model header strings directly to access models which are not listed above.
933
+
934
+ ```python
935
+ # "model_name" and "model_header" keys must be present
936
+ custom_model = {
937
+ "model_name": "xxx",
938
+ "model_header": {
939
+ "x-goog-ext-525001261-jspb": "[1,null,null,null,'e6fa609c3fa255c0',null,null,null,[4]]"
940
+ },
941
+ }
942
+
943
+ response = await client.generate_content(
944
+ "What's your model version?",
945
+ model=custom_model
946
+ )
947
+ ```
948
+
932
949
  ### Apply system prompt with Gemini Gems
933
950
 
934
951
  System prompt can be applied to conversations via [Gemini Gems](https://gemini.google.com/gems/view). To use a gem, you can pass `gem` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. `gem` can be either a string of gem id or a `gemini_webapi.Gem` object. Only one gem can be applied to a single conversation.
File without changes
File without changes