gemini-webapi 1.16.0__py3-none-any.whl → 1.17.1__py3-none-any.whl

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.
gemini_webapi/client.py CHANGED
@@ -10,31 +10,33 @@ from httpx import AsyncClient, ReadTimeout, Response
10
10
  from .components import GemMixin
11
11
  from .constants import Endpoint, ErrorCode, Headers, Model
12
12
  from .exceptions import (
13
- AuthError,
14
13
  APIError,
15
- ImageGenerationError,
16
- TimeoutError,
14
+ AuthError,
17
15
  GeminiError,
18
- UsageLimitExceeded,
16
+ ImageGenerationError,
19
17
  ModelInvalid,
20
18
  TemporarilyBlocked,
19
+ TimeoutError,
20
+ UsageLimitExceeded,
21
21
  )
22
22
  from .types import (
23
- WebImage,
24
- GeneratedImage,
25
23
  Candidate,
26
- ModelOutput,
27
24
  Gem,
25
+ GeneratedImage,
26
+ ModelOutput,
28
27
  RPCData,
28
+ WebImage,
29
29
  )
30
30
  from .utils import (
31
- upload_file,
31
+ extract_json_from_response,
32
+ get_access_token,
33
+ get_nested_value,
34
+ logger,
32
35
  parse_file_name,
33
36
  rotate_1psidts,
34
- get_access_token,
35
- running,
36
37
  rotate_tasks,
37
- logger,
38
+ running,
39
+ upload_file,
38
40
  )
39
41
 
40
42
 
@@ -42,7 +44,7 @@ class GeminiClient(GemMixin):
42
44
  """
43
45
  Async httpx client interface for gemini.google.com.
44
46
 
45
- `secure_1psid` must be provided unless the optional dependency `browser-cookie3` is installed and
47
+ `secure_1psid` must be provided unless the optional dependency `browser-cookie3` is installed, and
46
48
  you have logged in to google.com in your local browser.
47
49
 
48
50
  Parameters
@@ -203,6 +205,7 @@ class GeminiClient(GemMixin):
203
205
  if self.close_task:
204
206
  self.close_task.cancel()
205
207
  self.close_task = None
208
+
206
209
  self.close_task = asyncio.create_task(self.close(self.close_delay))
207
210
 
208
211
  async def start_auto_refresh(self) -> None:
@@ -211,18 +214,25 @@ class GeminiClient(GemMixin):
211
214
  """
212
215
 
213
216
  while True:
217
+ new_1psidts: str | None = None
214
218
  try:
215
219
  new_1psidts = await rotate_1psidts(self.cookies, self.proxy)
216
220
  except AuthError:
217
- if task := rotate_tasks.get(self.cookies["__Secure-1PSID"]):
221
+ if task := rotate_tasks.get(self.cookies.get("__Secure-1PSID", "")):
218
222
  task.cancel()
219
223
  logger.warning(
220
- "Failed to refresh cookies. Background auto refresh task canceled."
224
+ "AuthError: Failed to refresh cookies. Auto refresh task canceled."
221
225
  )
226
+ return
227
+ except Exception as exc:
228
+ logger.warning(f"Unexpected error while refreshing cookies: {exc}")
222
229
 
223
- logger.debug(f"Cookies refreshed. New __Secure-1PSIDTS: {new_1psidts}")
224
230
  if new_1psidts:
225
231
  self.cookies["__Secure-1PSIDTS"] = new_1psidts
232
+ if self.running:
233
+ self.client.cookies.set("__Secure-1PSIDTS", new_1psidts)
234
+ logger.debug("Cookies refreshed. New __Secure-1PSIDTS applied.")
235
+
226
236
  await asyncio.sleep(self.refresh_interval)
227
237
 
228
238
  @running(retry=2)
@@ -230,7 +240,7 @@ class GeminiClient(GemMixin):
230
240
  self,
231
241
  prompt: str,
232
242
  files: list[str | Path] | None = None,
233
- model: Model | str = Model.UNSPECIFIED,
243
+ model: Model | str | dict = Model.UNSPECIFIED,
234
244
  gem: Gem | str | None = None,
235
245
  chat: Optional["ChatSession"] = None,
236
246
  **kwargs,
@@ -244,9 +254,10 @@ class GeminiClient(GemMixin):
244
254
  Prompt provided by user.
245
255
  files: `list[str | Path]`, optional
246
256
  List of file paths to be attached.
247
- model: `Model` | `str`, optional
257
+ model: `Model | str | dict`, optional
248
258
  Specify the model to use for generation.
249
- 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).
250
261
  gem: `Gem | str`, optional
251
262
  Specify a gem to use as system prompt for the chat session.
252
263
  Pass either a `gemini_webapi.types.Gem` object or a gem id string.
@@ -277,8 +288,15 @@ class GeminiClient(GemMixin):
277
288
 
278
289
  assert prompt, "Prompt cannot be empty."
279
290
 
280
- if not isinstance(model, Model):
291
+ if isinstance(model, str):
281
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
+ )
282
300
 
283
301
  if isinstance(gem, Gem):
284
302
  gem_id = gem.id
@@ -335,18 +353,24 @@ class GeminiClient(GemMixin):
335
353
  f"Failed to generate contents. Request failed with status code {response.status_code}"
336
354
  )
337
355
  else:
356
+ response_json: list[Any] = []
357
+ body: list[Any] = []
358
+ body_index = 0
359
+
338
360
  try:
339
- response_json = json.loads(response.text.split("\n")[2])
361
+ response_json = extract_json_from_response(response.text)
340
362
 
341
- body = None
342
- body_index = 0
343
363
  for part_index, part in enumerate(response_json):
344
364
  try:
345
- main_part = json.loads(part[2])
346
- if main_part[4]:
347
- body_index, body = part_index, main_part
365
+ part_body = get_nested_value(part, [2])
366
+ if not part_body:
367
+ continue
368
+
369
+ part_json = json.loads(part_body)
370
+ if get_nested_value(part_json, [4]):
371
+ body_index, body = part_index, part_json
348
372
  break
349
- except (IndexError, TypeError, ValueError):
373
+ except json.JSONDecodeError:
350
374
  continue
351
375
 
352
376
  if not body:
@@ -355,7 +379,8 @@ class GeminiClient(GemMixin):
355
379
  await self.close()
356
380
 
357
381
  try:
358
- match ErrorCode(response_json[0][5][2][0][1][0]):
382
+ error_code = get_nested_value(response_json, [0, 5, 2, 0, 1, 0], -1)
383
+ match ErrorCode(error_code):
359
384
  case ErrorCode.USAGE_LIMIT_EXCEEDED:
360
385
  raise UsageLimitExceeded(
361
386
  f"Failed to generate contents. Usage limit of {model.model_name} model has exceeded. Please try switching to another model."
@@ -385,47 +410,58 @@ class GeminiClient(GemMixin):
385
410
  )
386
411
 
387
412
  try:
388
- candidates = []
389
- for candidate_index, candidate in enumerate(body[4]):
390
- text = candidate[1][0]
413
+ candidate_list: list[Any] = get_nested_value(body, [4], [])
414
+ output_candidates: list[Candidate] = []
415
+
416
+ for candidate_index, candidate in enumerate(candidate_list):
417
+ rcid = get_nested_value(candidate, [0])
418
+ if not rcid:
419
+ continue # Skip candidate if it has no rcid
420
+
421
+ # Text output and thoughts
422
+ text = get_nested_value(candidate, [1, 0], "")
391
423
  if re.match(
392
424
  r"^http://googleusercontent\.com/card_content/\d+", text
393
425
  ):
394
- text = candidate[22] and candidate[22][0] or text
426
+ text = get_nested_value(candidate, [22, 0]) or text
395
427
 
396
- try:
397
- thoughts = candidate[37][0][0]
398
- except (TypeError, IndexError):
399
- thoughts = None
400
-
401
- web_images = (
402
- candidate[12]
403
- and candidate[12][1]
404
- and [
428
+ thoughts = get_nested_value(candidate, [37, 0, 0])
429
+
430
+ # Web images
431
+ web_images = []
432
+ for web_img_data in get_nested_value(candidate, [12, 1], []):
433
+ url = get_nested_value(web_img_data, [0, 0, 0])
434
+ if not url:
435
+ continue
436
+
437
+ web_images.append(
405
438
  WebImage(
406
- url=web_image[0][0][0],
407
- title=web_image[7][0],
408
- alt=web_image[0][4],
439
+ url=url,
440
+ title=get_nested_value(web_img_data, [7, 0], ""),
441
+ alt=get_nested_value(web_img_data, [0, 4], ""),
409
442
  proxy=self.proxy,
410
443
  )
411
- for web_image in candidate[12][1]
412
- ]
413
- or []
414
- )
444
+ )
415
445
 
446
+ # Generated images
416
447
  generated_images = []
417
- if candidate[12] and candidate[12][7] and candidate[12][7][0]:
448
+ if get_nested_value(candidate, [12, 7, 0]):
418
449
  img_body = None
419
450
  for img_part_index, part in enumerate(response_json):
420
451
  if img_part_index < body_index:
421
452
  continue
422
-
423
453
  try:
424
- img_part = json.loads(part[2])
425
- if img_part[4][candidate_index][12][7][0]:
426
- img_body = img_part
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)
459
+ if get_nested_value(
460
+ img_part_json, [4, candidate_index, 12, 7, 0]
461
+ ):
462
+ img_body = img_part_json
427
463
  break
428
- except (IndexError, TypeError, ValueError):
464
+ except json.JSONDecodeError:
429
465
  continue
430
466
 
431
467
  if not img_body:
@@ -434,57 +470,73 @@ class GeminiClient(GemMixin):
434
470
  "If the error persists and is caused by the package, please report it on GitHub."
435
471
  )
436
472
 
437
- img_candidate = img_body[4][candidate_index]
438
-
439
- text = re.sub(
440
- r"http://googleusercontent\.com/image_generation_content/\d+",
441
- "",
442
- img_candidate[1][0],
443
- ).rstrip()
444
-
445
- generated_images = [
446
- GeneratedImage(
447
- url=generated_image[0][3][3],
448
- title=(
449
- f"[Generated Image {generated_image[3][6]}]"
450
- if generated_image[3][6]
451
- else "[Generated Image]"
452
- ),
453
- alt=(
454
- generated_image[3][5][image_index]
455
- if generated_image[3][5]
456
- and len(generated_image[3][5]) > image_index
457
- else (
458
- generated_image[3][5][0]
459
- if generated_image[3][5]
460
- else ""
461
- )
462
- ),
463
- proxy=self.proxy,
464
- cookies=self.cookies,
473
+ img_candidate = get_nested_value(
474
+ img_body, [4, candidate_index], []
475
+ )
476
+
477
+ if finished_text := get_nested_value(
478
+ img_candidate, [1, 0]
479
+ ): # Only overwrite if new text is returned after image generation
480
+ text = re.sub(
481
+ r"http://googleusercontent\.com/image_generation_content/\d+",
482
+ "",
483
+ finished_text,
484
+ ).rstrip()
485
+
486
+ for img_index, gen_img_data in enumerate(
487
+ get_nested_value(img_candidate, [12, 7, 0], [])
488
+ ):
489
+ url = get_nested_value(gen_img_data, [0, 3, 3])
490
+ if not url:
491
+ continue
492
+
493
+ img_num = get_nested_value(gen_img_data, [3, 6])
494
+ title = (
495
+ f"[Generated Image {img_num}]"
496
+ if img_num
497
+ else "[Generated Image]"
498
+ )
499
+
500
+ alt_list = get_nested_value(gen_img_data, [3, 5], [])
501
+ alt = (
502
+ get_nested_value(alt_list, [img_index])
503
+ or get_nested_value(alt_list, [0])
504
+ or ""
465
505
  )
466
- for image_index, generated_image in enumerate(
467
- img_candidate[12][7][0]
506
+
507
+ generated_images.append(
508
+ GeneratedImage(
509
+ url=url,
510
+ title=title,
511
+ alt=alt,
512
+ proxy=self.proxy,
513
+ cookies=self.cookies,
514
+ )
468
515
  )
469
- ]
470
516
 
471
- candidates.append(
517
+ output_candidates.append(
472
518
  Candidate(
473
- rcid=candidate[0],
519
+ rcid=rcid,
474
520
  text=text,
475
521
  thoughts=thoughts,
476
522
  web_images=web_images,
477
523
  generated_images=generated_images,
478
524
  )
479
525
  )
480
- if not candidates:
526
+
527
+ if not output_candidates:
481
528
  raise GeminiError(
482
529
  "Failed to generate contents. No output data found in response."
483
530
  )
484
531
 
485
- output = ModelOutput(metadata=body[1], candidates=candidates)
486
- except (TypeError, IndexError):
487
- logger.debug(f"Invalid response: {response.text}")
532
+ output = ModelOutput(
533
+ metadata=get_nested_value(body, [1], []),
534
+ candidates=output_candidates,
535
+ )
536
+ except (TypeError, IndexError) as e:
537
+ logger.debug(
538
+ f"{type(e).__name__}: {e}; Invalid response structure: {response.text}"
539
+ )
488
540
  raise APIError(
489
541
  "Failed to parse response body. Data structure is invalid."
490
542
  )
@@ -574,9 +626,10 @@ class ChatSession:
574
626
  Reply id, if provided together with metadata, will override the second value in it.
575
627
  rcid: `str`, optional
576
628
  Reply candidate id, if provided together with metadata, will override the third value in it.
577
- model: `Model` | `str`, optional
629
+ model: `Model | str | dict`, optional
578
630
  Specify the model to use for generation.
579
- 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).
580
633
  gem: `Gem | str`, optional
581
634
  Specify a gem to use as system prompt for the chat session.
582
635
  Pass either a `gemini_webapi.types.Gem` object or a gem id string.
@@ -597,13 +650,13 @@ class ChatSession:
597
650
  cid: str | None = None, # chat id
598
651
  rid: str | None = None, # reply id
599
652
  rcid: str | None = None, # reply candidate id
600
- model: Model | str = Model.UNSPECIFIED,
653
+ model: Model | str | dict = Model.UNSPECIFIED,
601
654
  gem: Gem | str | None = None,
602
655
  ):
603
656
  self.__metadata: list[str | None] = [None, None, None]
604
657
  self.geminiclient: GeminiClient = geminiclient
605
658
  self.last_output: ModelOutput | None = None
606
- self.model: Model | str = model
659
+ self.model: Model | str | dict = model
607
660
  self.gem: Gem | str | None = gem
608
661
 
609
662
  if metadata:
@@ -43,26 +43,27 @@ class Headers(Enum):
43
43
 
44
44
  class Model(Enum):
45
45
  UNSPECIFIED = ("unspecified", {}, False)
46
- G_2_5_FLASH = (
47
- "gemini-2.5-flash",
48
- {"x-goog-ext-525001261-jspb": '[1,null,null,null,"71c2d248d3b102ff",null,null,0,[4]]'},
46
+ G_3_0_PRO = (
47
+ "gemini-3.0-pro",
48
+ {
49
+ "x-goog-ext-525001261-jspb": '[1,null,null,null,"9d8ca3786ebdfbea",null,null,0,[4]]'
50
+ },
49
51
  False,
50
52
  )
51
53
  G_2_5_PRO = (
52
54
  "gemini-2.5-pro",
53
- {"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
+ },
54
58
  False,
55
59
  )
56
- G_2_0_FLASH = (
57
- "gemini-2.0-flash",
58
- {"x-goog-ext-525001261-jspb": '[1,null,null,null,"f299729663a2343f"]'},
59
- False,
60
- ) # Deprecated
61
- G_2_0_FLASH_THINKING = (
62
- "gemini-2.0-flash-thinking",
63
- {"x-goog-ext-525001261-jspb": '[null,null,null,null,"7ca48d02d802f20a"]'},
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
+ },
64
65
  False,
65
- ) # Deprecated
66
+ )
66
67
 
67
68
  def __init__(self, name, header, advanced_only):
68
69
  self.model_name = name
@@ -74,10 +75,28 @@ class Model(Enum):
74
75
  for model in cls:
75
76
  if model.model_name == name:
76
77
  return model
78
+
77
79
  raise ValueError(
78
80
  f"Unknown model name: {name}. Available models: {', '.join([model.model_name for model in cls])}"
79
81
  )
80
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
+
81
100
 
82
101
  class ErrorCode(IntEnum):
83
102
  """
@@ -3,11 +3,12 @@
3
3
  from asyncio import Task
4
4
 
5
5
  from .decorators import running
6
- from .upload_file import upload_file, parse_file_name
7
- from .rotate_1psidts import rotate_1psidts
8
6
  from .get_access_token import get_access_token
9
7
  from .load_browser_cookies import load_browser_cookies
10
8
  from .logger import logger, set_log_level
9
+ from .parsing import extract_json_from_response, get_nested_value
10
+ from .rotate_1psidts import rotate_1psidts
11
+ from .upload_file import upload_file, parse_file_name
11
12
 
12
13
 
13
14
  rotate_tasks: dict[str, Task] = {}
@@ -0,0 +1,79 @@
1
+ from typing import Any
2
+
3
+ import orjson as json
4
+
5
+ from .logger import logger
6
+
7
+
8
+ def get_nested_value(data: list, path: list[int], default: Any = None) -> Any:
9
+ """
10
+ Safely get a value from a nested list by a sequence of indices.
11
+
12
+ Parameters
13
+ ----------
14
+ data: `list`
15
+ The nested list to traverse.
16
+ path: `list[int]`
17
+ A list of indices representing the path to the desired value.
18
+ default: `Any`, optional
19
+ The default value to return if the path is not found.
20
+ """
21
+
22
+ current = data
23
+
24
+ for i, key in enumerate(path):
25
+ try:
26
+ current = current[key]
27
+ except (IndexError, TypeError, KeyError) as e:
28
+ current_repr = repr(current)
29
+ if len(current_repr) > 200:
30
+ current_repr = f"{current_repr[:197]}..."
31
+
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}`"
35
+ )
36
+ return default
37
+
38
+ if current is None and default is not None:
39
+ return default
40
+
41
+ return current
42
+
43
+
44
+ def extract_json_from_response(text: str) -> list:
45
+ """
46
+ Clean and extract the JSON content from a Google API response.
47
+
48
+ Parameters
49
+ ----------
50
+ text: `str`
51
+ The raw response text from a Google API.
52
+
53
+ Returns
54
+ -------
55
+ `list`
56
+ The extracted JSON array or object (should be an array).
57
+
58
+ Raises
59
+ ------
60
+ `TypeError`
61
+ If the input is not a string.
62
+ `ValueError`
63
+ If no JSON object is found or the response is empty.
64
+ """
65
+
66
+ if not isinstance(text, str):
67
+ raise TypeError(
68
+ f"Input text is expected to be a string, got {type(text).__name__} instead."
69
+ )
70
+
71
+ # Find the first line which is valid JSON
72
+ for line in text.splitlines():
73
+ try:
74
+ return json.loads(line.strip())
75
+ except json.JSONDecodeError:
76
+ continue
77
+
78
+ # If no JSON is found, raise ValueError
79
+ raise ValueError("Could not find a valid JSON object or array in the response.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemini-webapi
3
- Version: 1.16.0
3
+ Version: 1.17.1
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]
@@ -905,16 +905,12 @@ asyncio.run(main())
905
905
 
906
906
  You can specify which language model to use by passing `model` argument to `GeminiClient.generate_content` or `GeminiClient.start_chat`. The default value is `unspecified`.
907
907
 
908
- Currently available models (as of June 12, 2025):
908
+ Currently available models (as of November 20, 2025):
909
909
 
910
910
  - `unspecified` - Default model
911
+ - `gemini-3.0-pro` - Gemini 3.0 Pro
912
+ - `gemini-2.5-pro` - Gemini 2.5 Pro
911
913
  - `gemini-2.5-flash` - Gemini 2.5 Flash
912
- - `gemini-2.5-pro` - Gemini 2.5 Pro (daily usage limit imposed)
913
-
914
- Deprecated models (yet still working):
915
-
916
- - `gemini-2.0-flash` - Gemini 2.0 Flash
917
- - `gemini-2.0-flash-thinking` - Gemini 2.0 Flash Thinking
918
914
 
919
915
  ```python
920
916
  from gemini_webapi.constants import Model
@@ -933,6 +929,23 @@ async def main():
933
929
  asyncio.run(main())
934
930
  ```
935
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
+
936
949
  ### Apply system prompt with Gemini Gems
937
950
 
938
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.
@@ -1,6 +1,6 @@
1
1
  gemini_webapi/__init__.py,sha256=7ELCiUoI10ea3daeJxnv0UwqLVKpM7rxsgOZsPMstO8,150
2
- gemini_webapi/client.py,sha256=YlgBiIQWkxCjPaqMQ1Zs-MvRz6ww0U3uqdS__azqvwA,26827
3
- gemini_webapi/constants.py,sha256=ZzyZRqwJsqjvDdj3GYIdk0SpQVbKa1Vd5NwEsvIz6pI,2685
2
+ gemini_webapi/client.py,sha256=zcsJHaZAi_lOIOtwQ-6PqS2S6-8m_Jt3rZRK7cH0EBQ,29288
3
+ gemini_webapi/constants.py,sha256=tm9y8oKuD3QhKXUbVXZKroYCoPpqgBJdSTwoeue43Ss,3295
4
4
  gemini_webapi/exceptions.py,sha256=qkXrIpr0L7LtGbq3VcTO8D1xZ50pJtt0dDRp5I3uDSg,1038
5
5
  gemini_webapi/components/__init__.py,sha256=wolxuAJJ32-jmHOKgpsesexP7hXea1JMo5vI52wysTI,48
6
6
  gemini_webapi/components/gem_mixin.py,sha256=WPJkYDS4yQpLMBNQ94LQo5w59RgkllWaSiHsFG1k5GU,8795
@@ -10,15 +10,16 @@ gemini_webapi/types/gem.py,sha256=3Ppjq9V22Zp4Lb9a9ZnDviDKQpfSQf8UZxqOEjeEWd4,40
10
10
  gemini_webapi/types/grpc.py,sha256=S64h1oeC7ZJC50kmS_C2CQ7WVTanhJ4kqTFx5ZYayXI,917
11
11
  gemini_webapi/types/image.py,sha256=NhW1QY2Agzb6UbrIjZLnqxqukabDSGbmoCsVguzko10,5395
12
12
  gemini_webapi/types/modeloutput.py,sha256=h07kQOkL5r-oPLvZ59uVtO1eP4FGy5ZpzuYQzAeQdr8,1196
13
- gemini_webapi/utils/__init__.py,sha256=RIU1MBqYNjih0XMMt7wCxAA-X9KVF53nDbAjaxaGTo8,352
13
+ gemini_webapi/utils/__init__.py,sha256=k8hV2zn6tD_BEpd1Xya6ED0deijsmzb1e9XxdFhJzIE,418
14
14
  gemini_webapi/utils/decorators.py,sha256=AuY6sU1_6_ZqeL92dTAi3eRPQ7zubB5VuBZEiz16aBM,1741
15
15
  gemini_webapi/utils/get_access_token.py,sha256=VjrHW8VMN3LPs6zCdXQtraWygurOjTY0SJZhe49aQwc,7265
16
16
  gemini_webapi/utils/load_browser_cookies.py,sha256=OHCfe27DpV_rloIDgW9Xpeb0mkfzbYONNiholw0ElXU,1791
17
17
  gemini_webapi/utils/logger.py,sha256=0VcxhVLhHBRDQutNCpapP1y_MhPoQ2ud1uIFLqxC3Z8,958
18
+ gemini_webapi/utils/parsing.py,sha256=z-t0bDbXVIa0-3_ZmTK19PvL6zBM5vuy56z2jv1Yu1I,2105
18
19
  gemini_webapi/utils/rotate_1psidts.py,sha256=XjEeQnZS3ZI6wOl0Zb5CvsbIrg0BVVNas7cE6f3x_XE,1802
19
20
  gemini_webapi/utils/upload_file.py,sha256=SJOMr6kryK_ClrKmqI96fqZBNFOMPsyAvFINAGAU3rk,1468
20
- gemini_webapi-1.16.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
21
- gemini_webapi-1.16.0.dist-info/METADATA,sha256=vRnA79JpF9eMcgjzCqbpok_UGTvw5EPYKRO1bFtjZ5Q,61426
22
- gemini_webapi-1.16.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- gemini_webapi-1.16.0.dist-info/top_level.txt,sha256=dtWtug_ZrmnUqCYuu8NmGzTgWglHeNzhHU_hXmqZGWE,14
24
- gemini_webapi-1.16.0.dist-info/RECORD,,
21
+ gemini_webapi-1.17.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
22
+ gemini_webapi-1.17.1.dist-info/METADATA,sha256=OlCVaM8XIgS_w6zxowLrxLxc5ksPf-3W27oodzH2_MM,61763
23
+ gemini_webapi-1.17.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ gemini_webapi-1.17.1.dist-info/top_level.txt,sha256=dtWtug_ZrmnUqCYuu8NmGzTgWglHeNzhHU_hXmqZGWE,14
25
+ gemini_webapi-1.17.1.dist-info/RECORD,,