gemini-webapi 1.14.1__py3-none-any.whl → 1.14.3__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 +98 -47
- gemini_webapi/constants.py +12 -0
- gemini_webapi/types/__init__.py +1 -0
- gemini_webapi/types/grpc.py +34 -0
- gemini_webapi/types/image.py +6 -1
- {gemini_webapi-1.14.1.dist-info → gemini_webapi-1.14.3.dist-info}/METADATA +6 -2
- {gemini_webapi-1.14.1.dist-info → gemini_webapi-1.14.3.dist-info}/RECORD +10 -9
- {gemini_webapi-1.14.1.dist-info → gemini_webapi-1.14.3.dist-info}/WHEEL +0 -0
- {gemini_webapi-1.14.1.dist-info → gemini_webapi-1.14.3.dist-info}/licenses/LICENSE +0 -0
- {gemini_webapi-1.14.1.dist-info → gemini_webapi-1.14.3.dist-info}/top_level.txt +0 -0
gemini_webapi/client.py
CHANGED
|
@@ -7,9 +7,9 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Any, Optional
|
|
8
8
|
|
|
9
9
|
import orjson as json
|
|
10
|
-
from httpx import AsyncClient, ReadTimeout
|
|
10
|
+
from httpx import AsyncClient, ReadTimeout, Response
|
|
11
11
|
|
|
12
|
-
from .constants import Endpoint, ErrorCode, Headers, Model
|
|
12
|
+
from .constants import Endpoint, ErrorCode, Headers, Model, GRPC
|
|
13
13
|
from .exceptions import (
|
|
14
14
|
AuthError,
|
|
15
15
|
APIError,
|
|
@@ -20,7 +20,15 @@ from .exceptions import (
|
|
|
20
20
|
ModelInvalid,
|
|
21
21
|
TemporarilyBlocked,
|
|
22
22
|
)
|
|
23
|
-
from .types import
|
|
23
|
+
from .types import (
|
|
24
|
+
WebImage,
|
|
25
|
+
GeneratedImage,
|
|
26
|
+
Candidate,
|
|
27
|
+
ModelOutput,
|
|
28
|
+
Gem,
|
|
29
|
+
GemJar,
|
|
30
|
+
RPCData,
|
|
31
|
+
)
|
|
24
32
|
from .utils import (
|
|
25
33
|
upload_file,
|
|
26
34
|
parse_file_name,
|
|
@@ -301,66 +309,61 @@ class GeminiClient:
|
|
|
301
309
|
|
|
302
310
|
return self._gems
|
|
303
311
|
|
|
304
|
-
|
|
305
|
-
async def fetch_gems(self, **kwargs) -> GemJar:
|
|
312
|
+
async def fetch_gems(self, include_hidden: bool = False, **kwargs) -> GemJar:
|
|
306
313
|
"""
|
|
307
314
|
Get a list of available gems from gemini, including system predefined gems and user-created custom gems.
|
|
308
315
|
|
|
309
316
|
Note that network request will be sent every time this method is called.
|
|
310
317
|
Once the gems are fetched, they will be cached and accessible via `GeminiClient.gems` property.
|
|
311
318
|
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
include_hidden: `bool`, optional
|
|
322
|
+
There are some predefined gems that by default are not shown to users (and therefore may not work properly).
|
|
323
|
+
Set this parameter to `True` to include them in the fetched gem list.
|
|
324
|
+
|
|
312
325
|
Returns
|
|
313
326
|
-------
|
|
314
327
|
:class:`GemJar`
|
|
315
328
|
Refer to `gemini_webapi.types.GemJar`.
|
|
316
329
|
"""
|
|
317
330
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
)
|
|
334
|
-
except ReadTimeout:
|
|
335
|
-
raise TimeoutError(
|
|
336
|
-
"Fetch gems request timed out, please try again. If the problem persists, "
|
|
337
|
-
"consider setting a higher `timeout` value when initializing GeminiClient."
|
|
338
|
-
)
|
|
331
|
+
response = await self._batch_execute(
|
|
332
|
+
[
|
|
333
|
+
RPCData(
|
|
334
|
+
rpcid=GRPC.LIST_GEMS,
|
|
335
|
+
payload="[4]" if include_hidden else "[3]",
|
|
336
|
+
identifier="system",
|
|
337
|
+
),
|
|
338
|
+
RPCData(
|
|
339
|
+
rpcid=GRPC.LIST_GEMS,
|
|
340
|
+
payload="[2]",
|
|
341
|
+
identifier="custom",
|
|
342
|
+
),
|
|
343
|
+
],
|
|
344
|
+
**kwargs,
|
|
345
|
+
)
|
|
339
346
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
f"Failed to fetch gems. Request failed with status code {response.status_code}"
|
|
343
|
-
)
|
|
344
|
-
else:
|
|
345
|
-
try:
|
|
346
|
-
response_json = json.loads(response.text.split("\n")[2])
|
|
347
|
+
try:
|
|
348
|
+
response_json = json.loads(response.text.split("\n")[2])
|
|
347
349
|
|
|
348
|
-
|
|
350
|
+
predefined_gems, custom_gems = [], []
|
|
349
351
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
352
|
+
for part in response_json:
|
|
353
|
+
if part[-1] == "system":
|
|
354
|
+
predefined_gems = json.loads(part[2])[2]
|
|
355
|
+
elif part[-1] == "custom":
|
|
356
|
+
if custom_gems_container := json.loads(part[2]):
|
|
357
|
+
custom_gems = custom_gems_container[2]
|
|
356
358
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
if not predefined_gems and not custom_gems:
|
|
360
|
+
raise Exception
|
|
361
|
+
except Exception:
|
|
362
|
+
await self.close()
|
|
363
|
+
logger.debug(f"Invalid response: {response.text}")
|
|
364
|
+
raise APIError(
|
|
365
|
+
"Failed to fetch gems. Invalid response data received. Client will try to re-initialize on next request."
|
|
366
|
+
)
|
|
364
367
|
|
|
365
368
|
self._gems = GemJar(
|
|
366
369
|
itertools.chain(
|
|
@@ -664,6 +667,54 @@ class GeminiClient:
|
|
|
664
667
|
|
|
665
668
|
return ChatSession(geminiclient=self, **kwargs)
|
|
666
669
|
|
|
670
|
+
@running(retry=2)
|
|
671
|
+
async def _batch_execute(self, payloads: list[RPCData], **kwargs) -> Response:
|
|
672
|
+
"""
|
|
673
|
+
Execute a batch of requests to Gemini API.
|
|
674
|
+
|
|
675
|
+
Parameters
|
|
676
|
+
----------
|
|
677
|
+
payloads: `list[GRPC]`
|
|
678
|
+
List of `gemini_webapi.types.GRPC` objects to be executed.
|
|
679
|
+
kwargs: `dict`, optional
|
|
680
|
+
Additional arguments which will be passed to the post request.
|
|
681
|
+
Refer to `httpx.AsyncClient.request` for more information.
|
|
682
|
+
|
|
683
|
+
Returns
|
|
684
|
+
-------
|
|
685
|
+
:class:`httpx.Response`
|
|
686
|
+
Response object containing the result of the batch execution.
|
|
687
|
+
"""
|
|
688
|
+
|
|
689
|
+
try:
|
|
690
|
+
response = await self.client.post(
|
|
691
|
+
Endpoint.BATCH_EXEC,
|
|
692
|
+
data={
|
|
693
|
+
"at": self.access_token,
|
|
694
|
+
"f.req": json.dumps(
|
|
695
|
+
[[payload.serialize() for payload in payloads]]
|
|
696
|
+
).decode(),
|
|
697
|
+
},
|
|
698
|
+
**kwargs,
|
|
699
|
+
)
|
|
700
|
+
except ReadTimeout:
|
|
701
|
+
raise TimeoutError(
|
|
702
|
+
"Batch execute request timed out, please try again. If the problem persists, "
|
|
703
|
+
"consider setting a higher `timeout` value when initializing GeminiClient."
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
if response.status_code != 200:
|
|
707
|
+
logger.debug(
|
|
708
|
+
f"Batch execution failed with status code {response.status_code}. "
|
|
709
|
+
f"RPC: {', '.join({payload.rpcid.name for payload in payloads})}; "
|
|
710
|
+
f"Invalid response: {response.text}"
|
|
711
|
+
)
|
|
712
|
+
raise APIError(
|
|
713
|
+
f"Batch execution failed with status code {response.status_code}"
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
return response
|
|
717
|
+
|
|
667
718
|
|
|
668
719
|
class ChatSession:
|
|
669
720
|
"""
|
gemini_webapi/constants.py
CHANGED
|
@@ -10,6 +10,18 @@ class Endpoint(StrEnum):
|
|
|
10
10
|
BATCH_EXEC = "https://gemini.google.com/_/BardChatUi/data/batchexecute"
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
class GRPC(StrEnum):
|
|
14
|
+
"""
|
|
15
|
+
Google RPC ids used in Gemini API.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Chat methods
|
|
19
|
+
READ_CHAT = "hNvQHb"
|
|
20
|
+
|
|
21
|
+
# Gem methods
|
|
22
|
+
LIST_GEMS = "CNgdBe"
|
|
23
|
+
|
|
24
|
+
|
|
13
25
|
class Headers(Enum):
|
|
14
26
|
GEMINI = {
|
|
15
27
|
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
gemini_webapi/types/__init__.py
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from ..constants import GRPC
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RPCData(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
Helper class containing necessary data for Google RPC calls.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
rpcid : GRPC
|
|
13
|
+
Google RPC ID.
|
|
14
|
+
payload : str
|
|
15
|
+
Payload for the RPC call.
|
|
16
|
+
identifier : str, optional
|
|
17
|
+
Identifier/order for the RPC call, defaults to "generic".
|
|
18
|
+
Makes sense if there are multiple RPC calls in a batch, where this identifier
|
|
19
|
+
can be used to distinguish between responses.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
rpcid: GRPC
|
|
23
|
+
payload: str
|
|
24
|
+
identifier: str = "generic"
|
|
25
|
+
|
|
26
|
+
def __repr__(self):
|
|
27
|
+
return f"GRPC(rpcid='{self.rpcid}', payload='{self.payload}', identifier='{self.identifier}')"
|
|
28
|
+
|
|
29
|
+
def serialize(self) -> list:
|
|
30
|
+
"""
|
|
31
|
+
Serializes object into formatted payload ready for RPC call.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
return [self.rpcid, self.payload, None, self.identifier]
|
gemini_webapi/types/image.py
CHANGED
|
@@ -137,7 +137,7 @@ class GeneratedImage(Image):
|
|
|
137
137
|
return v
|
|
138
138
|
|
|
139
139
|
# @override
|
|
140
|
-
async def save(self, **kwargs) -> str | None:
|
|
140
|
+
async def save(self, full_size=True, **kwargs) -> str | None:
|
|
141
141
|
"""
|
|
142
142
|
Save the image to disk.
|
|
143
143
|
|
|
@@ -146,6 +146,8 @@ class GeneratedImage(Image):
|
|
|
146
146
|
filename: `str`, optional
|
|
147
147
|
Filename to save the image, generated images are always in .png format, but file extension will not be included in the URL.
|
|
148
148
|
And since the URL ends with a long hash, by default will use timestamp + end of the hash as the filename.
|
|
149
|
+
full_size: `bool`, optional
|
|
150
|
+
If True, will modify the default preview (512*512) URL to get the full size image.
|
|
149
151
|
kwargs: `dict`, optional
|
|
150
152
|
Other arguments to pass to `Image.save`.
|
|
151
153
|
|
|
@@ -155,6 +157,9 @@ class GeneratedImage(Image):
|
|
|
155
157
|
Absolute path of the saved image if successfully saved.
|
|
156
158
|
"""
|
|
157
159
|
|
|
160
|
+
if full_size:
|
|
161
|
+
self.url += "=s2048"
|
|
162
|
+
|
|
158
163
|
return await super().save(
|
|
159
164
|
filename=kwargs.pop("filename", None)
|
|
160
165
|
or f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{self.url[-10:]}.png",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemini-webapi
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.3
|
|
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
|
|
@@ -931,10 +931,14 @@ asyncio.run(main())
|
|
|
931
931
|
|
|
932
932
|
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.
|
|
933
933
|
|
|
934
|
+
> [!TIP]
|
|
935
|
+
>
|
|
936
|
+
> There are some system predefined gems that by default are not shown to users (and therefore may not work properly). Use `client.fetch_gems(include_hidden=True)` to include them in the fetch result.
|
|
937
|
+
|
|
934
938
|
```python
|
|
935
939
|
async def main():
|
|
936
940
|
# Fetch all gems for the current account, including both predefined and user-created ones
|
|
937
|
-
await client.fetch_gems()
|
|
941
|
+
await client.fetch_gems(include_hidden=False)
|
|
938
942
|
|
|
939
943
|
# Once fetched, gems will be cached in `GeminiClient.gems`
|
|
940
944
|
gems = client.gems
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
gemini_webapi/__init__.py,sha256=7ELCiUoI10ea3daeJxnv0UwqLVKpM7rxsgOZsPMstO8,150
|
|
2
|
-
gemini_webapi/client.py,sha256=
|
|
3
|
-
gemini_webapi/constants.py,sha256=
|
|
2
|
+
gemini_webapi/client.py,sha256=XrPG5LtJDYpEOMyjL8zxRO9ExFwp9EO5UpsNxhouSvk,31820
|
|
3
|
+
gemini_webapi/constants.py,sha256=M2fbQSSVhF0ROv_9CTR4n8sDOujJmLRxwA6ARQ0Uf7k,2519
|
|
4
4
|
gemini_webapi/exceptions.py,sha256=qkXrIpr0L7LtGbq3VcTO8D1xZ50pJtt0dDRp5I3uDSg,1038
|
|
5
|
-
gemini_webapi/types/__init__.py,sha256=
|
|
5
|
+
gemini_webapi/types/__init__.py,sha256=1DU4JEw2KHQJbtOgOuvoEXtzQCMwAkyo0koSFVdT9JY,192
|
|
6
6
|
gemini_webapi/types/candidate.py,sha256=67BhY75toE5mVuB21cmHcTFtw332V_KmCjr3-9VTbJo,1477
|
|
7
7
|
gemini_webapi/types/gem.py,sha256=3Ppjq9V22Zp4Lb9a9ZnDviDKQpfSQf8UZxqOEjeEWd4,4070
|
|
8
|
-
gemini_webapi/types/
|
|
8
|
+
gemini_webapi/types/grpc.py,sha256=S64h1oeC7ZJC50kmS_C2CQ7WVTanhJ4kqTFx5ZYayXI,917
|
|
9
|
+
gemini_webapi/types/image.py,sha256=xCPaMzstIM5C09ZkJ0545vVPAsrF3y89XnRJrEWGz5k,5436
|
|
9
10
|
gemini_webapi/types/modeloutput.py,sha256=h07kQOkL5r-oPLvZ59uVtO1eP4FGy5ZpzuYQzAeQdr8,1196
|
|
10
11
|
gemini_webapi/utils/__init__.py,sha256=cJ9HQYxr8l0CsY61TFlho-5DdPxCaEOpRfjxPX-eils,320
|
|
11
12
|
gemini_webapi/utils/get_access_token.py,sha256=eNn1omFO41wWXco1eM-KXR2CEi0Tb-chlph7H-PCNjg,6137
|
|
@@ -13,8 +14,8 @@ gemini_webapi/utils/load_browser_cookies.py,sha256=A5n_VsB7Rm8ck5lpy856UNJEhv30l
|
|
|
13
14
|
gemini_webapi/utils/logger.py,sha256=0VcxhVLhHBRDQutNCpapP1y_MhPoQ2ud1uIFLqxC3Z8,958
|
|
14
15
|
gemini_webapi/utils/rotate_1psidts.py,sha256=NyQ9OYPLBOcvpc8bodvEYDIVFrsYN0kdfc831lPEctM,1680
|
|
15
16
|
gemini_webapi/utils/upload_file.py,sha256=SJOMr6kryK_ClrKmqI96fqZBNFOMPsyAvFINAGAU3rk,1468
|
|
16
|
-
gemini_webapi-1.14.
|
|
17
|
-
gemini_webapi-1.14.
|
|
18
|
-
gemini_webapi-1.14.
|
|
19
|
-
gemini_webapi-1.14.
|
|
20
|
-
gemini_webapi-1.14.
|
|
17
|
+
gemini_webapi-1.14.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
18
|
+
gemini_webapi-1.14.3.dist-info/METADATA,sha256=GNbHaHKIyQD0t3_MhkaxjHufYV7B5S1X5YUDG9-8e5o,59157
|
|
19
|
+
gemini_webapi-1.14.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
gemini_webapi-1.14.3.dist-info/top_level.txt,sha256=dtWtug_ZrmnUqCYuu8NmGzTgWglHeNzhHU_hXmqZGWE,14
|
|
21
|
+
gemini_webapi-1.14.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|