gemini-webapi 1.14.4__py3-none-any.whl → 1.15.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 +7 -7
- gemini_webapi/components/gem_mixin.py +140 -3
- gemini_webapi/constants.py +4 -2
- {gemini_webapi-1.14.4.dist-info → gemini_webapi-1.15.1.dist-info}/METADATA +74 -1
- {gemini_webapi-1.14.4.dist-info → gemini_webapi-1.15.1.dist-info}/RECORD +8 -8
- {gemini_webapi-1.14.4.dist-info → gemini_webapi-1.15.1.dist-info}/WHEEL +0 -0
- {gemini_webapi-1.14.4.dist-info → gemini_webapi-1.15.1.dist-info}/licenses/LICENSE +0 -0
- {gemini_webapi-1.14.4.dist-info → gemini_webapi-1.15.1.dist-info}/top_level.txt +0 -0
gemini_webapi/client.py
CHANGED
|
@@ -292,7 +292,9 @@ class GeminiClient(GemMixin):
|
|
|
292
292
|
model = Model.from_name(model)
|
|
293
293
|
|
|
294
294
|
if isinstance(gem, Gem):
|
|
295
|
-
|
|
295
|
+
gem_id = gem.id
|
|
296
|
+
else:
|
|
297
|
+
gem_id = gem
|
|
296
298
|
|
|
297
299
|
if self.auto_close:
|
|
298
300
|
await self.reset_close_task()
|
|
@@ -325,7 +327,7 @@ class GeminiClient(GemMixin):
|
|
|
325
327
|
None,
|
|
326
328
|
chat and chat.metadata,
|
|
327
329
|
]
|
|
328
|
-
+ (
|
|
330
|
+
+ (gem_id and [None] * 16 + [gem_id] or [])
|
|
329
331
|
).decode(),
|
|
330
332
|
]
|
|
331
333
|
).decode(),
|
|
@@ -551,12 +553,10 @@ class GeminiClient(GemMixin):
|
|
|
551
553
|
"consider setting a higher `timeout` value when initializing GeminiClient."
|
|
552
554
|
)
|
|
553
555
|
|
|
556
|
+
# ? Seems like batch execution will immediately invalidate the current access token,
|
|
557
|
+
# ? causing the next request to fail with 401 Unauthorized.
|
|
554
558
|
if response.status_code != 200:
|
|
555
|
-
|
|
556
|
-
f"Batch execution failed with status code {response.status_code}. "
|
|
557
|
-
f"RPC: {', '.join({payload.rpcid.name for payload in payloads})}; "
|
|
558
|
-
f"Invalid response: {response.text}"
|
|
559
|
-
)
|
|
559
|
+
await self.close()
|
|
560
560
|
raise APIError(
|
|
561
561
|
f"Batch execution failed with status code {response.status_code}"
|
|
562
562
|
)
|
|
@@ -131,10 +131,145 @@ class GemMixin:
|
|
|
131
131
|
|
|
132
132
|
return self._gems
|
|
133
133
|
|
|
134
|
+
@running(retry=2)
|
|
135
|
+
async def create_gem(self, name: str, prompt: str, description: str = "") -> Gem:
|
|
136
|
+
"""
|
|
137
|
+
Create a new custom gem.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
name: `str`
|
|
142
|
+
Name of the custom gem.
|
|
143
|
+
prompt: `str`
|
|
144
|
+
System instructions for the custom gem.
|
|
145
|
+
description: `str`, optional
|
|
146
|
+
Description of the custom gem (has no effect on the model's behavior).
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
:class:`Gem`
|
|
151
|
+
The created gem.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
response = await self._batch_execute(
|
|
155
|
+
[
|
|
156
|
+
RPCData(
|
|
157
|
+
rpcid=GRPC.CREATE_GEM,
|
|
158
|
+
payload=json.dumps(
|
|
159
|
+
[
|
|
160
|
+
[
|
|
161
|
+
name,
|
|
162
|
+
description,
|
|
163
|
+
prompt,
|
|
164
|
+
None,
|
|
165
|
+
None,
|
|
166
|
+
None,
|
|
167
|
+
None,
|
|
168
|
+
None,
|
|
169
|
+
0,
|
|
170
|
+
None,
|
|
171
|
+
1,
|
|
172
|
+
None,
|
|
173
|
+
None,
|
|
174
|
+
None,
|
|
175
|
+
[],
|
|
176
|
+
]
|
|
177
|
+
]
|
|
178
|
+
).decode(),
|
|
179
|
+
)
|
|
180
|
+
]
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
response_json = json.loads(response.text.split("\n")[2])
|
|
185
|
+
gem_id = json.loads(response_json[0][2])[0]
|
|
186
|
+
except Exception:
|
|
187
|
+
await self.close()
|
|
188
|
+
logger.debug(f"Invalid response: {response.text}")
|
|
189
|
+
raise APIError(
|
|
190
|
+
"Failed to create gem. Invalid response data received. Client will try to re-initialize on next request."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return Gem(
|
|
194
|
+
id=gem_id,
|
|
195
|
+
name=name,
|
|
196
|
+
description=description,
|
|
197
|
+
prompt=prompt,
|
|
198
|
+
predefined=False,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@running(retry=2)
|
|
202
|
+
async def update_gem(
|
|
203
|
+
self, gem: Gem | str, name: str, prompt: str, description: str = ""
|
|
204
|
+
) -> Gem:
|
|
205
|
+
"""
|
|
206
|
+
Update an existing custom gem.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
gem: `Gem | str`
|
|
211
|
+
Gem to update, can be either a `gemini_webapi.types.Gem` object or a gem id string.
|
|
212
|
+
name: `str`
|
|
213
|
+
New name for the custom gem.
|
|
214
|
+
prompt: `str`
|
|
215
|
+
New system instructions for the custom gem.
|
|
216
|
+
description: `str`, optional
|
|
217
|
+
New description of the custom gem (has no effect on the model's behavior).
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
:class:`Gem`
|
|
222
|
+
The updated gem.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
if isinstance(gem, Gem):
|
|
226
|
+
gem_id = gem.id
|
|
227
|
+
else:
|
|
228
|
+
gem_id = gem
|
|
229
|
+
|
|
230
|
+
await self._batch_execute(
|
|
231
|
+
[
|
|
232
|
+
RPCData(
|
|
233
|
+
rpcid=GRPC.UPDATE_GEM,
|
|
234
|
+
payload=json.dumps(
|
|
235
|
+
[
|
|
236
|
+
gem_id,
|
|
237
|
+
[
|
|
238
|
+
name,
|
|
239
|
+
description,
|
|
240
|
+
prompt,
|
|
241
|
+
None,
|
|
242
|
+
None,
|
|
243
|
+
None,
|
|
244
|
+
None,
|
|
245
|
+
None,
|
|
246
|
+
0,
|
|
247
|
+
None,
|
|
248
|
+
1,
|
|
249
|
+
None,
|
|
250
|
+
None,
|
|
251
|
+
None,
|
|
252
|
+
[],
|
|
253
|
+
0,
|
|
254
|
+
],
|
|
255
|
+
]
|
|
256
|
+
).decode(),
|
|
257
|
+
)
|
|
258
|
+
]
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return Gem(
|
|
262
|
+
id=gem_id,
|
|
263
|
+
name=name,
|
|
264
|
+
description=description,
|
|
265
|
+
prompt=prompt,
|
|
266
|
+
predefined=False,
|
|
267
|
+
)
|
|
268
|
+
|
|
134
269
|
@running(retry=2)
|
|
135
270
|
async def delete_gem(self, gem: Gem | str, **kwargs) -> None:
|
|
136
271
|
"""
|
|
137
|
-
Delete a custom gem
|
|
272
|
+
Delete a custom gem.
|
|
138
273
|
|
|
139
274
|
Parameters
|
|
140
275
|
----------
|
|
@@ -143,9 +278,11 @@ class GemMixin:
|
|
|
143
278
|
"""
|
|
144
279
|
|
|
145
280
|
if isinstance(gem, Gem):
|
|
146
|
-
|
|
281
|
+
gem_id = gem.id
|
|
282
|
+
else:
|
|
283
|
+
gem_id = gem
|
|
147
284
|
|
|
148
285
|
await self._batch_execute(
|
|
149
|
-
[RPCData(rpcid=GRPC.DELETE_GEM, payload=[
|
|
286
|
+
[RPCData(rpcid=GRPC.DELETE_GEM, payload=json.dumps([gem_id]).decode())],
|
|
150
287
|
**kwargs,
|
|
151
288
|
)
|
gemini_webapi/constants.py
CHANGED
|
@@ -20,6 +20,8 @@ class GRPC(StrEnum):
|
|
|
20
20
|
|
|
21
21
|
# Gem methods
|
|
22
22
|
LIST_GEMS = "CNgdBe"
|
|
23
|
+
CREATE_GEM = "oMH3Zd"
|
|
24
|
+
UPDATE_GEM = "kHv0Vd"
|
|
23
25
|
DELETE_GEM = "UXcSJb"
|
|
24
26
|
|
|
25
27
|
|
|
@@ -42,12 +44,12 @@ class Model(Enum):
|
|
|
42
44
|
UNSPECIFIED = ("unspecified", {}, False)
|
|
43
45
|
G_2_5_FLASH = (
|
|
44
46
|
"gemini-2.5-flash",
|
|
45
|
-
{"x-goog-ext-525001261-jspb": '[1,null,null,null,"71c2d248d3b102ff"]'},
|
|
47
|
+
{"x-goog-ext-525001261-jspb": '[1,null,null,null,"71c2d248d3b102ff",null,null,0,[4]]'},
|
|
46
48
|
False,
|
|
47
49
|
)
|
|
48
50
|
G_2_5_PRO = (
|
|
49
51
|
"gemini-2.5-pro",
|
|
50
|
-
{"x-goog-ext-525001261-jspb": '[1,null,null,null,"
|
|
52
|
+
{"x-goog-ext-525001261-jspb": '[1,null,null,null,"4af6c7f5da75d65d",null,null,0,[4]]'},
|
|
51
53
|
False,
|
|
52
54
|
)
|
|
53
55
|
G_2_0_FLASH = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemini-webapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.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
|
|
@@ -734,6 +734,10 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
|
|
|
734
734
|
- [Continue previous conversations](#continue-previous-conversations)
|
|
735
735
|
- [Select language model](#select-language-model)
|
|
736
736
|
- [Apply system prompt with Gemini Gems](#apply-system-prompt-with-gemini-gems)
|
|
737
|
+
- [Manage Custom Gems](#manage-custom-gems)
|
|
738
|
+
- [Create a custom gem](#create-a-custom-gem)
|
|
739
|
+
- [Update an existing gem](#update-an-existing-gem)
|
|
740
|
+
- [Delete a custom gem](#delete-a-custom-gem)
|
|
737
741
|
- [Retrieve model's thought process](#retrieve-models-thought-process)
|
|
738
742
|
- [Retrieve images in response](#retrieve-images-in-response)
|
|
739
743
|
- [Generate images with Imagen4](#generate-images-with-imagen4)
|
|
@@ -963,6 +967,75 @@ async def main():
|
|
|
963
967
|
print(response2)
|
|
964
968
|
```
|
|
965
969
|
|
|
970
|
+
### Manage Custom Gems
|
|
971
|
+
|
|
972
|
+
You can create, update, and delete your custom gems programmatically with the API. Note that predefined system gems cannot be modified or deleted.
|
|
973
|
+
|
|
974
|
+
#### Create a custom gem
|
|
975
|
+
|
|
976
|
+
Create a new custom gem with a name, system prompt (instructions), and optional description:
|
|
977
|
+
|
|
978
|
+
```python
|
|
979
|
+
async def main():
|
|
980
|
+
# Create a new custom gem
|
|
981
|
+
new_gem = await client.create_gem(
|
|
982
|
+
name="Python Tutor",
|
|
983
|
+
prompt="You are a helpful Python programming tutor.",
|
|
984
|
+
description="A specialized gem for Python programming"
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
print(f"Custom gem created: {new_gem}")
|
|
988
|
+
|
|
989
|
+
# Use the newly created gem in a conversation
|
|
990
|
+
response = await client.generate_content(
|
|
991
|
+
"Explain how list comprehensions work in Python",
|
|
992
|
+
gem=new_gem
|
|
993
|
+
)
|
|
994
|
+
print(response.text)
|
|
995
|
+
|
|
996
|
+
asyncio.run(main())
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
#### Update an existing gem
|
|
1000
|
+
|
|
1001
|
+
> [!NOTE]
|
|
1002
|
+
>
|
|
1003
|
+
> When updating a gem, you must provide all parameters (name, prompt, description) even if you only want to change one of them.
|
|
1004
|
+
|
|
1005
|
+
```python
|
|
1006
|
+
async def main():
|
|
1007
|
+
# Get a custom gem (assuming you have one named "Python Tutor")
|
|
1008
|
+
await client.fetch_gems()
|
|
1009
|
+
python_tutor = client.gems.get(name="Python Tutor")
|
|
1010
|
+
|
|
1011
|
+
# Update the gem with new instructions
|
|
1012
|
+
updated_gem = await client.update_gem(
|
|
1013
|
+
gem=python_tutor, # Can also pass gem ID string
|
|
1014
|
+
name="Advanced Python Tutor",
|
|
1015
|
+
prompt="You are an expert Python programming tutor.",
|
|
1016
|
+
description="An advanced Python programming assistant"
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
print(f"Custom gem updated: {updated_gem}")
|
|
1020
|
+
|
|
1021
|
+
asyncio.run(main())
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
#### Delete a custom gem
|
|
1025
|
+
|
|
1026
|
+
```python
|
|
1027
|
+
async def main():
|
|
1028
|
+
# Get the gem to delete
|
|
1029
|
+
await client.fetch_gems()
|
|
1030
|
+
gem_to_delete = client.gems.get(name="Advanced Python Tutor")
|
|
1031
|
+
|
|
1032
|
+
# Delete the gem
|
|
1033
|
+
await client.delete_gem(gem_to_delete) # Can also pass gem ID string
|
|
1034
|
+
print(f"Custom gem deleted: {gem_to_delete.name}")
|
|
1035
|
+
|
|
1036
|
+
asyncio.run(main())
|
|
1037
|
+
```
|
|
1038
|
+
|
|
966
1039
|
### Retrieve model's thought process
|
|
967
1040
|
|
|
968
1041
|
When using models with thinking capabilities, the model's thought process will be populated in `ModelOutput.thoughts`.
|
|
@@ -1,9 +1,9 @@
|
|
|
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=G0nmRCeEmT0BpqgYY4zxb_ozFDmb39JqtddKpIN6JlA,26870
|
|
3
|
+
gemini_webapi/constants.py,sha256=z8Jr6NQk79mgZOgmwx8vbgzyORHQVBjoJ4ZhUdDWWXw,2629
|
|
4
4
|
gemini_webapi/exceptions.py,sha256=qkXrIpr0L7LtGbq3VcTO8D1xZ50pJtt0dDRp5I3uDSg,1038
|
|
5
5
|
gemini_webapi/components/__init__.py,sha256=wolxuAJJ32-jmHOKgpsesexP7hXea1JMo5vI52wysTI,48
|
|
6
|
-
gemini_webapi/components/gem_mixin.py,sha256=
|
|
6
|
+
gemini_webapi/components/gem_mixin.py,sha256=WPJkYDS4yQpLMBNQ94LQo5w59RgkllWaSiHsFG1k5GU,8795
|
|
7
7
|
gemini_webapi/types/__init__.py,sha256=1DU4JEw2KHQJbtOgOuvoEXtzQCMwAkyo0koSFVdT9JY,192
|
|
8
8
|
gemini_webapi/types/candidate.py,sha256=67BhY75toE5mVuB21cmHcTFtw332V_KmCjr3-9VTbJo,1477
|
|
9
9
|
gemini_webapi/types/gem.py,sha256=3Ppjq9V22Zp4Lb9a9ZnDviDKQpfSQf8UZxqOEjeEWd4,4070
|
|
@@ -17,8 +17,8 @@ gemini_webapi/utils/load_browser_cookies.py,sha256=A5n_VsB7Rm8ck5lpy856UNJEhv30l
|
|
|
17
17
|
gemini_webapi/utils/logger.py,sha256=0VcxhVLhHBRDQutNCpapP1y_MhPoQ2ud1uIFLqxC3Z8,958
|
|
18
18
|
gemini_webapi/utils/rotate_1psidts.py,sha256=NyQ9OYPLBOcvpc8bodvEYDIVFrsYN0kdfc831lPEctM,1680
|
|
19
19
|
gemini_webapi/utils/upload_file.py,sha256=SJOMr6kryK_ClrKmqI96fqZBNFOMPsyAvFINAGAU3rk,1468
|
|
20
|
-
gemini_webapi-1.
|
|
21
|
-
gemini_webapi-1.
|
|
22
|
-
gemini_webapi-1.
|
|
23
|
-
gemini_webapi-1.
|
|
24
|
-
gemini_webapi-1.
|
|
20
|
+
gemini_webapi-1.15.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
21
|
+
gemini_webapi-1.15.1.dist-info/METADATA,sha256=baIL2RVMPLqKe1l_lMxi5QDpTEJbNdehoF4uk2-467Q,61279
|
|
22
|
+
gemini_webapi-1.15.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
gemini_webapi-1.15.1.dist-info/top_level.txt,sha256=dtWtug_ZrmnUqCYuu8NmGzTgWglHeNzhHU_hXmqZGWE,14
|
|
24
|
+
gemini_webapi-1.15.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|