gemini-webapi 1.14.4__tar.gz → 1.15.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.
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.github/workflows/github-release.yml +1 -1
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.github/workflows/pypi-publish.yml +1 -1
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/PKG-INFO +74 -1
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/README.md +73 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/client.py +7 -7
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/components/gem_mixin.py +140 -3
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/constants.py +2 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/PKG-INFO +74 -1
- gemini_webapi-1.15.0/tests/test_gem_mixin.py +88 -0
- gemini_webapi-1.14.4/tests/test_gem_mixin.py +0 -33
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.github/dependabot.yml +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.gitignore +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.vscode/launch.json +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/.vscode/settings.json +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/LICENSE +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/assets/banner.png +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/assets/favicon.png +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/assets/logo.svg +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/assets/sample.pdf +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/pyproject.toml +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/setup.cfg +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/__init__.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/components/__init__.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/exceptions.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/__init__.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/candidate.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/gem.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/grpc.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/image.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/types/modeloutput.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/__init__.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/decorators.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/get_access_token.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/load_browser_cookies.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/logger.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/rotate_1psidts.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/upload_file.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/SOURCES.txt +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/dependency_links.txt +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/requires.txt +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/top_level.txt +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/tests/test_client_features.py +0 -0
- {gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/tests/test_save_image.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemini-webapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.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
|
|
@@ -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`.
|
|
@@ -50,6 +50,10 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
|
|
|
50
50
|
- [Continue previous conversations](#continue-previous-conversations)
|
|
51
51
|
- [Select language model](#select-language-model)
|
|
52
52
|
- [Apply system prompt with Gemini Gems](#apply-system-prompt-with-gemini-gems)
|
|
53
|
+
- [Manage Custom Gems](#manage-custom-gems)
|
|
54
|
+
- [Create a custom gem](#create-a-custom-gem)
|
|
55
|
+
- [Update an existing gem](#update-an-existing-gem)
|
|
56
|
+
- [Delete a custom gem](#delete-a-custom-gem)
|
|
53
57
|
- [Retrieve model's thought process](#retrieve-models-thought-process)
|
|
54
58
|
- [Retrieve images in response](#retrieve-images-in-response)
|
|
55
59
|
- [Generate images with Imagen4](#generate-images-with-imagen4)
|
|
@@ -279,6 +283,75 @@ async def main():
|
|
|
279
283
|
print(response2)
|
|
280
284
|
```
|
|
281
285
|
|
|
286
|
+
### Manage Custom Gems
|
|
287
|
+
|
|
288
|
+
You can create, update, and delete your custom gems programmatically with the API. Note that predefined system gems cannot be modified or deleted.
|
|
289
|
+
|
|
290
|
+
#### Create a custom gem
|
|
291
|
+
|
|
292
|
+
Create a new custom gem with a name, system prompt (instructions), and optional description:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
async def main():
|
|
296
|
+
# Create a new custom gem
|
|
297
|
+
new_gem = await client.create_gem(
|
|
298
|
+
name="Python Tutor",
|
|
299
|
+
prompt="You are a helpful Python programming tutor.",
|
|
300
|
+
description="A specialized gem for Python programming"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
print(f"Custom gem created: {new_gem}")
|
|
304
|
+
|
|
305
|
+
# Use the newly created gem in a conversation
|
|
306
|
+
response = await client.generate_content(
|
|
307
|
+
"Explain how list comprehensions work in Python",
|
|
308
|
+
gem=new_gem
|
|
309
|
+
)
|
|
310
|
+
print(response.text)
|
|
311
|
+
|
|
312
|
+
asyncio.run(main())
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### Update an existing gem
|
|
316
|
+
|
|
317
|
+
> [!NOTE]
|
|
318
|
+
>
|
|
319
|
+
> When updating a gem, you must provide all parameters (name, prompt, description) even if you only want to change one of them.
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
async def main():
|
|
323
|
+
# Get a custom gem (assuming you have one named "Python Tutor")
|
|
324
|
+
await client.fetch_gems()
|
|
325
|
+
python_tutor = client.gems.get(name="Python Tutor")
|
|
326
|
+
|
|
327
|
+
# Update the gem with new instructions
|
|
328
|
+
updated_gem = await client.update_gem(
|
|
329
|
+
gem=python_tutor, # Can also pass gem ID string
|
|
330
|
+
name="Advanced Python Tutor",
|
|
331
|
+
prompt="You are an expert Python programming tutor.",
|
|
332
|
+
description="An advanced Python programming assistant"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
print(f"Custom gem updated: {updated_gem}")
|
|
336
|
+
|
|
337
|
+
asyncio.run(main())
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### Delete a custom gem
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
async def main():
|
|
344
|
+
# Get the gem to delete
|
|
345
|
+
await client.fetch_gems()
|
|
346
|
+
gem_to_delete = client.gems.get(name="Advanced Python Tutor")
|
|
347
|
+
|
|
348
|
+
# Delete the gem
|
|
349
|
+
await client.delete_gem(gem_to_delete) # Can also pass gem ID string
|
|
350
|
+
print(f"Custom gem deleted: {gem_to_delete.name}")
|
|
351
|
+
|
|
352
|
+
asyncio.run(main())
|
|
353
|
+
```
|
|
354
|
+
|
|
282
355
|
### Retrieve model's thought process
|
|
283
356
|
|
|
284
357
|
When using models with thinking capabilities, the model's thought process will be populated in `ModelOutput.thoughts`.
|
|
@@ -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
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gemini-webapi
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.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
|
|
@@ -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`.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import random
|
|
3
|
+
import unittest
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from gemini_webapi import GeminiClient, set_log_level, logger
|
|
7
|
+
from gemini_webapi.exceptions import AuthError
|
|
8
|
+
|
|
9
|
+
logging.getLogger("asyncio").setLevel(logging.ERROR)
|
|
10
|
+
set_log_level("DEBUG")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestGemMixin(unittest.IsolatedAsyncioTestCase):
|
|
14
|
+
async def asyncSetUp(self):
|
|
15
|
+
self.geminiclient = GeminiClient(
|
|
16
|
+
os.getenv("SECURE_1PSID"), os.getenv("SECURE_1PSIDTS"), verify=False
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
await self.geminiclient.init(timeout=60, auto_refresh=False)
|
|
21
|
+
except AuthError as e:
|
|
22
|
+
self.skipTest(e)
|
|
23
|
+
|
|
24
|
+
@logger.catch(reraise=True)
|
|
25
|
+
async def test_fetch_gems(self):
|
|
26
|
+
await self.geminiclient.fetch_gems(include_hidden=True)
|
|
27
|
+
gems = self.geminiclient.gems
|
|
28
|
+
self.assertTrue(len(gems.filter(predefined=True)) > 0)
|
|
29
|
+
for gem in gems:
|
|
30
|
+
logger.debug(gem.name)
|
|
31
|
+
|
|
32
|
+
custom_gems = gems.filter(predefined=False)
|
|
33
|
+
if custom_gems:
|
|
34
|
+
logger.debug(f"Found {len(custom_gems)} custom gems:")
|
|
35
|
+
for gem in custom_gems:
|
|
36
|
+
logger.debug(gem)
|
|
37
|
+
|
|
38
|
+
@logger.catch(reraise=True)
|
|
39
|
+
async def test_create_gem(self):
|
|
40
|
+
gem = await self.geminiclient.create_gem(
|
|
41
|
+
name="Test Gem",
|
|
42
|
+
prompt="Gemini API has launched creating gem functionality on Aug 1st, 2025",
|
|
43
|
+
description="This gem is used for testing the functionality of Gemini API",
|
|
44
|
+
)
|
|
45
|
+
logger.debug(f"Gem created: {gem}")
|
|
46
|
+
|
|
47
|
+
@logger.catch(reraise=True)
|
|
48
|
+
async def test_update_gem(self):
|
|
49
|
+
await self.geminiclient.fetch_gems()
|
|
50
|
+
custom_gems = self.geminiclient.gems.filter(predefined=False)
|
|
51
|
+
if not custom_gems:
|
|
52
|
+
self.skipTest("No custom gems available to update.")
|
|
53
|
+
|
|
54
|
+
last_created_gem = next(iter(custom_gems.values()))
|
|
55
|
+
randint = random.randint(0, 100)
|
|
56
|
+
updated_gem = await self.geminiclient.update_gem(
|
|
57
|
+
last_created_gem.id,
|
|
58
|
+
name="Updated Test Gem",
|
|
59
|
+
prompt="Updated prompt for the gem.",
|
|
60
|
+
description=f"{randint}",
|
|
61
|
+
)
|
|
62
|
+
logger.debug(f"Gem updated: {updated_gem}")
|
|
63
|
+
|
|
64
|
+
await self.geminiclient.fetch_gems()
|
|
65
|
+
custom_gems = self.geminiclient.gems.filter(predefined=False)
|
|
66
|
+
last_created_gem = next(iter(custom_gems.values()))
|
|
67
|
+
self.assertEqual(last_created_gem.description, updated_gem.description)
|
|
68
|
+
|
|
69
|
+
@logger.catch(reraise=True)
|
|
70
|
+
async def test_delete_gem(self):
|
|
71
|
+
await self.geminiclient.fetch_gems()
|
|
72
|
+
custom_gems = self.geminiclient.gems.filter(predefined=False)
|
|
73
|
+
total_before_deletion = len(custom_gems)
|
|
74
|
+
if total_before_deletion == 0:
|
|
75
|
+
self.skipTest("No custom gems available to delete.")
|
|
76
|
+
|
|
77
|
+
last_created_gem = next(iter(custom_gems.values()))
|
|
78
|
+
await self.geminiclient.delete_gem(last_created_gem.id)
|
|
79
|
+
logger.debug(f"Gem deleted: {last_created_gem}")
|
|
80
|
+
|
|
81
|
+
await self.geminiclient.fetch_gems()
|
|
82
|
+
custom_gems = self.geminiclient.gems.filter(predefined=False)
|
|
83
|
+
total_after_deletion = len(custom_gems)
|
|
84
|
+
self.assertEqual(total_after_deletion, total_before_deletion - 1)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
unittest.main()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import unittest
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
from gemini_webapi import GeminiClient, set_log_level, logger
|
|
6
|
-
from gemini_webapi.exceptions import AuthError
|
|
7
|
-
|
|
8
|
-
logging.getLogger("asyncio").setLevel(logging.ERROR)
|
|
9
|
-
set_log_level("DEBUG")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestGemMixin(unittest.IsolatedAsyncioTestCase):
|
|
13
|
-
async def asyncSetUp(self):
|
|
14
|
-
self.geminiclient = GeminiClient(
|
|
15
|
-
os.getenv("SECURE_1PSID"), os.getenv("SECURE_1PSIDTS"), verify=False
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
await self.geminiclient.init(timeout=60, auto_refresh=False)
|
|
20
|
-
except AuthError as e:
|
|
21
|
-
self.skipTest(e)
|
|
22
|
-
|
|
23
|
-
@logger.catch(reraise=True)
|
|
24
|
-
async def test_fetch_gems(self):
|
|
25
|
-
await self.geminiclient.fetch_gems(include_hidden=True)
|
|
26
|
-
gems = self.geminiclient.gems
|
|
27
|
-
self.assertTrue(len(gems.filter(predefined=True)) > 0)
|
|
28
|
-
for gem in gems:
|
|
29
|
-
logger.debug(gem.name)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if __name__ == "__main__":
|
|
33
|
-
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi/utils/load_browser_cookies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gemini_webapi-1.14.4 → gemini_webapi-1.15.0}/src/gemini_webapi.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|