gemini-webapi 1.9.0__tar.gz → 1.17.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.
Files changed (48) hide show
  1. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.github/workflows/github-release.yml +1 -1
  2. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.github/workflows/pypi-publish.yml +5 -5
  3. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.gitignore +1 -1
  4. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/PKG-INFO +197 -92
  5. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/README.md +192 -89
  6. gemini_webapi-1.17.0/assets/sample.pdf +0 -0
  7. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/pyproject.toml +2 -1
  8. gemini_webapi-1.17.0/src/gemini_webapi/__init__.py +6 -0
  9. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/client.py +279 -155
  10. gemini_webapi-1.17.0/src/gemini_webapi/components/__init__.py +3 -0
  11. gemini_webapi-1.17.0/src/gemini_webapi/components/gem_mixin.py +288 -0
  12. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/constants.py +40 -36
  13. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/exceptions.py +32 -0
  14. gemini_webapi-1.17.0/src/gemini_webapi/types/__init__.py +7 -0
  15. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/types/candidate.py +13 -1
  16. gemini_webapi-1.17.0/src/gemini_webapi/types/gem.py +132 -0
  17. gemini_webapi-1.17.0/src/gemini_webapi/types/grpc.py +34 -0
  18. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/types/image.py +10 -5
  19. gemini_webapi-1.17.0/src/gemini_webapi/utils/__init__.py +14 -0
  20. gemini_webapi-1.17.0/src/gemini_webapi/utils/decorators.py +52 -0
  21. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/utils/get_access_token.py +77 -22
  22. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/utils/load_browser_cookies.py +10 -4
  23. gemini_webapi-1.17.0/src/gemini_webapi/utils/logger.py +37 -0
  24. gemini_webapi-1.17.0/src/gemini_webapi/utils/parsing.py +79 -0
  25. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/utils/rotate_1psidts.py +5 -1
  26. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/utils/upload_file.py +27 -6
  27. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi.egg-info/PKG-INFO +197 -92
  28. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi.egg-info/SOURCES.txt +8 -1
  29. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi.egg-info/requires.txt +2 -1
  30. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/tests/test_client_features.py +67 -57
  31. gemini_webapi-1.17.0/tests/test_gem_mixin.py +88 -0
  32. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/tests/test_save_image.py +15 -9
  33. gemini_webapi-1.9.0/src/gemini_webapi/__init__.py +0 -4
  34. gemini_webapi-1.9.0/src/gemini_webapi/types/__init__.py +0 -3
  35. gemini_webapi-1.9.0/src/gemini_webapi/utils/__init__.py +0 -10
  36. gemini_webapi-1.9.0/src/gemini_webapi/utils/logger.py +0 -39
  37. gemini_webapi-1.9.0/tests/test_rotate_cookies.py +0 -26
  38. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.github/dependabot.yml +0 -0
  39. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.vscode/launch.json +0 -0
  40. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/.vscode/settings.json +0 -0
  41. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/LICENSE +0 -0
  42. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/assets/banner.png +0 -0
  43. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/assets/favicon.png +0 -0
  44. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/assets/logo.svg +0 -0
  45. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/setup.cfg +0 -0
  46. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi/types/modeloutput.py +0 -0
  47. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi.egg-info/dependency_links.txt +0 -0
  48. {gemini_webapi-1.9.0 → gemini_webapi-1.17.0}/src/gemini_webapi.egg-info/top_level.txt +0 -0
@@ -11,7 +11,7 @@ jobs:
11
11
  permissions:
12
12
  contents: write
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v5
15
15
  - uses: ncipollo/release-action@v1
16
16
  with:
17
17
  body: ${{ github.event.head_commit.message }}
@@ -24,9 +24,9 @@ jobs:
24
24
  name: Build package
25
25
  runs-on: ubuntu-latest
26
26
  steps:
27
- - uses: actions/checkout@v4
27
+ - uses: actions/checkout@v5
28
28
  - name: Set up Python
29
- uses: actions/setup-python@v5
29
+ uses: actions/setup-python@v6
30
30
  with:
31
31
  python-version: '3.x'
32
32
  - name: Install dependencies
@@ -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.0
39
+ uses: actions/upload-artifact@v4.6.2
40
40
  with:
41
41
  name: dist
42
42
  path: dist
@@ -52,9 +52,9 @@ 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@v4.1.8
55
+ uses: actions/download-artifact@v5.0.0
56
56
  with:
57
57
  name: dist
58
58
  path: dist
59
59
  - name: Publish package distributions to PyPI
60
- uses: pypa/gh-action-pypi-publish@v1.12.4
60
+ uses: pypa/gh-action-pypi-publish@v1.13.0
@@ -201,4 +201,4 @@ Temporary Items
201
201
  .apdisk
202
202
 
203
203
  # Temporary files
204
- temp/
204
+ .temp/
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: gemini-webapi
3
- Version: 1.9.0
3
+ Version: 1.17.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
@@ -677,8 +677,10 @@ Requires-Python: >=3.10
677
677
  Description-Content-Type: text/markdown
678
678
  License-File: LICENSE
679
679
  Requires-Dist: httpx[http2]~=0.28.1
680
- Requires-Dist: pydantic~=2.10.5
681
680
  Requires-Dist: loguru~=0.7.3
681
+ Requires-Dist: orjson~=3.11.1
682
+ Requires-Dist: pydantic~=2.12.2
683
+ Dynamic: license-file
682
684
 
683
685
  <p align="center">
684
686
  <img src="https://raw.githubusercontent.com/HanaokaYuzu/Gemini-API/master/assets/banner.png" width="55%" alt="Gemini Banner" align="center">
@@ -711,9 +713,10 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
711
713
  ## Features
712
714
 
713
715
  - **Persistent Cookies** - Automatically refreshes cookies in background. Optimized for always-on services.
714
- - **ImageFx Support** - Supports retrieving images generated by ImageFx, Google's latest AI image generator.
716
+ - **Image Generation** - Natively supports generating and editing images with natural language.
717
+ - **System Prompt** - Supports customizing model's system prompt with [Gemini Gems](https://gemini.google.com/gems/view).
715
718
  - **Extension Support** - Supports generating contents with [Gemini extensions](https://gemini.google.com/extensions) on, like YouTube and Gmail.
716
- - **Classified Outputs** - Automatically categorizes texts, web images and AI generated images in the response.
719
+ - **Classified Outputs** - Categorizes texts, thoughts, web images and AI generated images in the response.
717
720
  - **Official Flavor** - Provides a simple and elegant interface inspired by [Google Generative AI](https://ai.google.dev/tutorials/python_quickstart)'s official API.
718
721
  - **Asynchronous** - Utilizes `asyncio` to run generating tasks and return outputs efficiently.
719
722
 
@@ -725,18 +728,22 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
725
728
  - [Authentication](#authentication)
726
729
  - [Usage](#usage)
727
730
  - [Initialization](#initialization)
728
- - [Select language model](#select-language-model)
729
- - [Generate contents from text](#generate-contents-from-text)
730
- - [Generate contents from image](#generate-contents-from-image)
731
+ - [Generate contents](#generate-contents)
732
+ - [Generate contents with files](#generate-contents-with-files)
731
733
  - [Conversations across multiple turns](#conversations-across-multiple-turns)
732
734
  - [Continue previous conversations](#continue-previous-conversations)
735
+ - [Select language model](#select-language-model)
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)
733
741
  - [Retrieve model's thought process](#retrieve-models-thought-process)
734
742
  - [Retrieve images in response](#retrieve-images-in-response)
735
- - [Generate images with ImageFx](#generate-images-with-imagefx)
736
- - [Save images to local files](#save-images-to-local-files)
743
+ - [Generate and edit images](#generate-and-edit-images)
737
744
  - [Generate contents with Gemini extensions](#generate-contents-with-gemini-extensions)
738
745
  - [Check and switch to other reply candidates](#check-and-switch-to-other-reply-candidates)
739
- - [Control log level](#control-log-level)
746
+ - [Logging Configuration](#logging-configuration)
740
747
  - [References](#references)
741
748
  - [Stargazers](#stargazers)
742
749
 
@@ -748,13 +755,13 @@ A reverse-engineered asynchronous python wrapper for [Google Gemini](https://gem
748
755
 
749
756
  Install/update the package with pip.
750
757
 
751
- ```bash
758
+ ```sh
752
759
  pip install -U gemini_webapi
753
760
  ```
754
761
 
755
762
  Optionally, package offers a way to automatically import cookies from your local browser. To enable this feature, install `browser-cookie3` as well. Supported platforms and browsers can be found [here](https://github.com/borisbabic/browser_cookie3?tab=readme-ov-file#contribute).
756
763
 
757
- ```bash
764
+ ```sh
758
765
  pip install -U browser-cookie3
759
766
  ```
760
767
 
@@ -770,15 +777,17 @@ pip install -U browser-cookie3
770
777
 
771
778
  > [!NOTE]
772
779
  >
773
- > If your application is deployed in a containerized environment (e.g. Docker), you may want to persist the cookies with a volume to avoid re-authentication every time the container rebuilds.
780
+ > If your application is deployed in a containerized environment (e.g. Docker), you may want to persist the cookies with a volume to avoid re-authentication every time the container rebuilds. You can set `GEMINI_COOKIE_PATH` environment variable to specify the path where auto-refreshed cookies are stored. Make sure the path is writable by the application.
774
781
  >
775
782
  > Here's part of a sample `docker-compose.yml` file:
776
783
 
777
784
  ```yaml
778
785
  services:
779
- main:
780
- volumes:
781
- - ./gemini_cookies:/usr/local/lib/python3.12/site-packages/gemini_webapi/utils/temp
786
+ main:
787
+ environment:
788
+ GEMINI_COOKIE_PATH: /tmp/gemini_webapi
789
+ volumes:
790
+ - ./gemini_cookies:/tmp/gemini_webapi
782
791
  ```
783
792
 
784
793
  > [!NOTE]
@@ -816,44 +825,9 @@ asyncio.run(main())
816
825
  >
817
826
  > `auto_close` and `close_delay` are optional arguments for automatically closing the client after a certain period of inactivity. This feature is disabled by default. In an always-on service like chatbot, it's recommended to set `auto_close` to `True` combined with reasonable seconds of `close_delay` for better resource management.
818
827
 
819
- ### Select language model
820
-
821
- 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`.
822
-
823
- Currently available models (as of Feb 5, 2025):
824
-
825
- - `unspecified` - Default model (same as `gemini-2.0-flash` if account does NOT have Gemini Advanced subscription)
826
- - `gemini-2.0-flash` - Gemini 2.0 Flash
827
- - `gemini-2.0-flash-thinking` - Gemini 2.0 Flash Thinking Experimental
828
- - `gemini-2.0-flash-thinking-with-apps` - Gemini 2.0 Flash Thinking Experimental with apps
829
- - `gemini-1.5-flash` - Gemini 1.5 Flash
830
-
831
- Models pending update (may not work as expected):
828
+ ### Generate contents
832
829
 
833
- - `gemini-2.0-exp-advanced` - Gemini 2.0 Experimental Advanced **(requires Gemini Advanced account)**
834
- - `gemini-1.5-pro` - Gemini 1.5 Pro **(requires Gemini Advanced account)**
835
- - `gemini-1.5-pro-research` - Gemini 1.5 Pro with Deep Research **(requires Gemini Advanced account)**
836
-
837
- ```python
838
- from gemini_webapi.constants import Model
839
-
840
- async def main():
841
- response1 = await client.generate_content(
842
- "What's you language model version? Reply version number only.",
843
- model=Model.G_2_0_FLASH,
844
- )
845
- print(f"Model version ({Model.G_2_0_FLASH.model_name}): {response1.text}")
846
-
847
- chat = client.start_chat(model="gemini-2.0-flash-thinking")
848
- response2 = await chat.send_message("What's you language model version? Reply version number only.")
849
- print(f"Model version (gemini-2.0-flash-thinking): {response2.text}")
850
-
851
- asyncio.run(main())
852
- ```
853
-
854
- ### Generate contents from text
855
-
856
- Ask a one-turn quick question by calling `GeminiClient.generate_content`.
830
+ Ask a single-turn question by calling `GeminiClient.generate_content`, which returns a `gemini_webapi.ModelOutput` object containing the generated text, images, thoughts, and conversation metadata.
857
831
 
858
832
  ```python
859
833
  async def main():
@@ -867,15 +841,15 @@ asyncio.run(main())
867
841
  >
868
842
  > Simply use `print(response)` to get the same output if you just want to see the response text
869
843
 
870
- ### Generate contents from image
844
+ ### Generate contents with files
871
845
 
872
- Gemini supports image recognition and generating contents from images. Optionally, you can pass images in a list of file data in `bytes` or their paths in `str` or `pathlib.Path` to `GeminiClient.generate_content` together with text prompt.
846
+ Gemini supports file input, including images and documents. Optionally, you can pass files as a list of paths in `str` or `pathlib.Path` to `GeminiClient.generate_content` together with text prompt.
873
847
 
874
848
  ```python
875
849
  async def main():
876
850
  response = await client.generate_content(
877
- "Describe each of these images",
878
- images=["assets/banner.png", "assets/favicon.png"],
851
+ "Introduce the contents of these two files. Is there any connection between them?",
852
+ files=["assets/sample.pdf", Path("assets/banner.png")],
879
853
  )
880
854
  print(response.text)
881
855
 
@@ -884,14 +858,20 @@ asyncio.run(main())
884
858
 
885
859
  ### Conversations across multiple turns
886
860
 
887
- If you want to keep conversation continuous, please use `GeminiClient.start_chat` to create a `ChatSession` object and send messages through it. The conversation history will be automatically handled and get updated after each turn.
861
+ If you want to keep conversation continuous, please use `GeminiClient.start_chat` to create a `gemini_webapi.ChatSession` object and send messages through it. The conversation history will be automatically handled and get updated after each turn.
888
862
 
889
863
  ```python
890
864
  async def main():
891
865
  chat = client.start_chat()
892
- response1 = await chat.send_message("Briefly introduce Europe")
893
- response2 = await chat.send_message("What's the population there?")
894
- print(response1.text, response2.text, sep="\n\n----------------------------------\n\n")
866
+ response1 = await chat.send_message(
867
+ "Introduce the contents of these two files. Is there any connection between them?",
868
+ files=["assets/sample.pdf", Path("assets/banner.png")],
869
+ )
870
+ print(response1.text)
871
+ response2 = await chat.send_message(
872
+ "Use image generation tool to modify the banner with another font and design."
873
+ )
874
+ print(response2.text, response2.images, sep="\n\n----------------------------------\n\n")
895
875
 
896
876
  asyncio.run(main())
897
877
  ```
@@ -921,6 +901,139 @@ async def main():
921
901
  asyncio.run(main())
922
902
  ```
923
903
 
904
+ ### Select language model
905
+
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
+
908
+ Currently available models (as of November 20, 2025):
909
+
910
+ - `unspecified` - Default model
911
+ - `gemini-3.0-pro` - Gemini 3.0 Pro
912
+ - `gemini-2.5-pro` - Gemini 2.5 Pro
913
+ - `gemini-2.5-flash` - Gemini 2.5 Flash
914
+
915
+ ```python
916
+ from gemini_webapi.constants import Model
917
+
918
+ async def main():
919
+ response1 = await client.generate_content(
920
+ "What's you language model version? Reply version number only.",
921
+ model=Model.G_2_5_FLASH,
922
+ )
923
+ print(f"Model version ({Model.G_2_5_FLASH.model_name}): {response1.text}")
924
+
925
+ chat = client.start_chat(model="gemini-2.5-pro")
926
+ response2 = await chat.send_message("What's you language model version? Reply version number only.")
927
+ print(f"Model version (gemini-2.5-pro): {response2.text}")
928
+
929
+ asyncio.run(main())
930
+ ```
931
+
932
+ ### Apply system prompt with Gemini Gems
933
+
934
+ 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.
935
+
936
+ > [!TIP]
937
+ >
938
+ > 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.
939
+
940
+ ```python
941
+ async def main():
942
+ # Fetch all gems for the current account, including both predefined and user-created ones
943
+ await client.fetch_gems(include_hidden=False)
944
+
945
+ # Once fetched, gems will be cached in `GeminiClient.gems`
946
+ gems = client.gems
947
+
948
+ # Get the gem you want to use
949
+ system_gems = gems.filter(predefined=True)
950
+ coding_partner = system_gems.get(id="coding-partner")
951
+
952
+ response1 = await client.generate_content(
953
+ "what's your system prompt?",
954
+ model=Model.G_2_5_FLASH,
955
+ gem=coding_partner,
956
+ )
957
+ print(response1.text)
958
+
959
+ # Another example with a user-created custom gem
960
+ # Gem ids are consistent strings. Store them somewhere to avoid fetching gems every time
961
+ your_gem = gems.get(name="Your Gem Name")
962
+ your_gem_id = your_gem.id
963
+ chat = client.start_chat(gem=your_gem_id)
964
+ response2 = await chat.send_message("what's your system prompt?")
965
+ print(response2)
966
+ ```
967
+
968
+ ### Manage Custom Gems
969
+
970
+ You can create, update, and delete your custom gems programmatically with the API. Note that predefined system gems cannot be modified or deleted.
971
+
972
+ #### Create a custom gem
973
+
974
+ Create a new custom gem with a name, system prompt (instructions), and optional description:
975
+
976
+ ```python
977
+ async def main():
978
+ # Create a new custom gem
979
+ new_gem = await client.create_gem(
980
+ name="Python Tutor",
981
+ prompt="You are a helpful Python programming tutor.",
982
+ description="A specialized gem for Python programming"
983
+ )
984
+
985
+ print(f"Custom gem created: {new_gem}")
986
+
987
+ # Use the newly created gem in a conversation
988
+ response = await client.generate_content(
989
+ "Explain how list comprehensions work in Python",
990
+ gem=new_gem
991
+ )
992
+ print(response.text)
993
+
994
+ asyncio.run(main())
995
+ ```
996
+
997
+ #### Update an existing gem
998
+
999
+ > [!NOTE]
1000
+ >
1001
+ > When updating a gem, you must provide all parameters (name, prompt, description) even if you only want to change one of them.
1002
+
1003
+ ```python
1004
+ async def main():
1005
+ # Get a custom gem (assuming you have one named "Python Tutor")
1006
+ await client.fetch_gems()
1007
+ python_tutor = client.gems.get(name="Python Tutor")
1008
+
1009
+ # Update the gem with new instructions
1010
+ updated_gem = await client.update_gem(
1011
+ gem=python_tutor, # Can also pass gem ID string
1012
+ name="Advanced Python Tutor",
1013
+ prompt="You are an expert Python programming tutor.",
1014
+ description="An advanced Python programming assistant"
1015
+ )
1016
+
1017
+ print(f"Custom gem updated: {updated_gem}")
1018
+
1019
+ asyncio.run(main())
1020
+ ```
1021
+
1022
+ #### Delete a custom gem
1023
+
1024
+ ```python
1025
+ async def main():
1026
+ # Get the gem to delete
1027
+ await client.fetch_gems()
1028
+ gem_to_delete = client.gems.get(name="Advanced Python Tutor")
1029
+
1030
+ # Delete the gem
1031
+ await client.delete_gem(gem_to_delete) # Can also pass gem ID string
1032
+ print(f"Custom gem deleted: {gem_to_delete.name}")
1033
+
1034
+ asyncio.run(main())
1035
+ ```
1036
+
924
1037
  ### Retrieve model's thought process
925
1038
 
926
1039
  When using models with thinking capabilities, the model's thought process will be populated in `ModelOutput.thoughts`.
@@ -928,7 +1041,7 @@ When using models with thinking capabilities, the model's thought process will b
928
1041
  ```python
929
1042
  async def main():
930
1043
  response = await client.generate_content(
931
- "What's 1+1?", model="gemini-2.0-flash-thinking"
1044
+ "What's 1+1?", model="gemini-2.5-pro"
932
1045
  )
933
1046
  print(response.thoughts)
934
1047
  print(response.text)
@@ -938,7 +1051,7 @@ asyncio.run(main())
938
1051
 
939
1052
  ### Retrieve images in response
940
1053
 
941
- Images in the API's output are stored as a list of `Image` objects. You can access the image title, URL, and description by calling `image.title`, `image.url` and `image.alt` respectively.
1054
+ Images in the API's output are stored as a list of `gemini_webapi.Image` objects. You can access the image title, URL, and description by calling `Image.title`, `Image.url` and `Image.alt` respectively.
942
1055
 
943
1056
  ```python
944
1057
  async def main():
@@ -949,24 +1062,27 @@ async def main():
949
1062
  asyncio.run(main())
950
1063
  ```
951
1064
 
952
- ### Generate images with ImageFx
1065
+ ### Generate and edit images
953
1066
 
954
- In February 2022, Google introduced a new AI image generator called ImageFx and integrated it into Gemini. You can ask Gemini to generate images with ImageFx simply by natural language.
1067
+ You can ask Gemini to generate and edit images with Nano Banana, Google's latest image model, simply by natural language.
955
1068
 
956
1069
  > [!IMPORTANT]
957
1070
  >
958
- > Google has some limitations on the image generation feature in Gemini, so its availability could be different per region/account. Here's a summary copied from [official documentation](https://support.google.com/gemini/answer/14286560) (as of February 15th, 2024):
1071
+ > Google has some limitations on the image generation feature in Gemini, so its availability could be different per region/account. Here's a summary copied from [official documentation](https://support.google.com/gemini/answer/14286560) (as of Sep 10, 2025):
959
1072
  >
960
- > > Image generation in Gemini Apps is available in most countries, except in the European Economic Area (EEA), Switzerland, and the UK. It’s only available for **English prompts**.
961
- > >
962
1073
  > > This feature’s availability in any specific Gemini app is also limited to the supported languages and countries of that app.
963
1074
  > >
964
1075
  > > For now, this feature isn’t available to users under 18.
1076
+ > >
1077
+ > > To use this feature, you must be signed in to Gemini Apps.
1078
+
1079
+ You can save images returned from Gemini to local by calling `Image.save()`. Optionally, you can specify the file path and file name by passing `path` and `filename` arguments to the function and skip images with invalid file names by passing `skip_invalid_filename=True`. Works for both `WebImage` and `GeneratedImage`.
965
1080
 
966
1081
  ```python
967
1082
  async def main():
968
1083
  response = await client.generate_content("Generate some pictures of cats")
969
- for image in response.images:
1084
+ for i, image in enumerate(response.images):
1085
+ await image.save(path="temp/", filename=f"cat_{i}.png", verbose=True)
970
1086
  print(image, "\n\n----------------------------------\n")
971
1087
 
972
1088
  asyncio.run(main())
@@ -976,32 +1092,17 @@ asyncio.run(main())
976
1092
  >
977
1093
  > by default, when asked to send images (like the previous example), Gemini will send images fetched from web instead of generating images with AI model, unless you specifically require to "generate" images in your prompt. In this package, web images and generated images are treated differently as `WebImage` and `GeneratedImage`, and will be automatically categorized in the output.
978
1094
 
979
- ### Save images to local files
980
-
981
- You can save images returned from Gemini to local files under `/temp` by calling `Image.save()`. Optionally, you can specify the file path and file name by passing `path` and `filename` arguments to the function and skip images with invalid file names by passing `skip_invalid_filename=True`. Works for both `WebImage` and `GeneratedImage`.
982
-
983
- ```python
984
- async def main():
985
- response = await client.generate_content("Generate some pictures of cats")
986
- for i, image in enumerate(response.images):
987
- await image.save(path="temp/", filename=f"cat_{i}.png", verbose=True)
988
-
989
- asyncio.run(main())
990
- ```
991
-
992
1095
  ### Generate contents with Gemini extensions
993
1096
 
994
1097
  > [!IMPORTANT]
995
1098
  >
996
- > To access Gemini extensions in API, you must activate them on the [Gemini website](https://gemini.google.com/extensions) first. Same as image generation, Google also has limitations on the availability of Gemini extensions. Here's a summary copied from [official documentation](https://support.google.com/gemini/answer/13695044) (as of February 18th, 2024):
1099
+ > To access Gemini extensions in API, you must activate them on the [Gemini website](https://gemini.google.com/extensions) first. Same as image generation, Google also has limitations on the availability of Gemini extensions. Here's a summary copied from [official documentation](https://support.google.com/gemini/answer/13695044) (as of March 19th, 2025):
997
1100
  >
998
- > > To use extensions in Gemini Apps:
999
- > >
1000
- > > Sign in with your personal Google Account that you manage on your own. Extensions, including the Google Workspace extension, are currently not available to Google Workspace accounts for school, business, or other organizations.
1101
+ > > To connect apps to Gemini, you must have​​​​ Gemini Apps Activity on.
1001
1102
  > >
1002
- > > Have Gemini Apps Activity on. Extensions are only available when Gemini Apps Activity is turned on.
1103
+ > > To use this feature, you must be signed in to Gemini Apps.
1003
1104
  > >
1004
- > > Important: For now, extensions are available in **English, Japanese, and Korean** only.
1105
+ > > Important: If you’re under 18, Google Workspace and Maps apps currently only work with English prompts in Gemini.
1005
1106
 
1006
1107
  After activating extensions for your account, you can access them in your prompts either by natural language or by starting your prompt with "@" followed by the extension keyword.
1007
1108
 
@@ -1022,7 +1123,7 @@ asyncio.run(main())
1022
1123
 
1023
1124
  ### Check and switch to other reply candidates
1024
1125
 
1025
- A response from Gemini usually contains multiple reply candidates with different generated contents. You can check all candidates and choose one to continue the conversation. By default, the first candidate will be chosen automatically.
1126
+ A response from Gemini sometimes contains multiple reply candidates with different generated contents. You can check all candidates and choose one to continue the conversation. By default, the first candidate will be chosen.
1026
1127
 
1027
1128
  ```python
1028
1129
  async def main():
@@ -1043,9 +1144,9 @@ async def main():
1043
1144
  asyncio.run(main())
1044
1145
  ```
1045
1146
 
1046
- ### Control log level
1147
+ ### Logging Configuration
1047
1148
 
1048
- You can set the log level of the package to one of the following values: `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL`. The default value is `INFO`.
1149
+ This package uses [loguru](https://loguru.readthedocs.io/en/stable/) for logging, and exposes a function `set_log_level` to control log level. You can set log level to one of the following values: `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL`. The default value is `INFO`.
1049
1150
 
1050
1151
  ```python
1051
1152
  from gemini_webapi import set_log_level
@@ -1053,6 +1154,10 @@ from gemini_webapi import set_log_level
1053
1154
  set_log_level("DEBUG")
1054
1155
  ```
1055
1156
 
1157
+ > [!NOTE]
1158
+ >
1159
+ > Calling `set_log_level` for the first time will **globally** remove all existing loguru handlers. You may want to configure logging directly with loguru to avoid this issue and have more advanced control over logging behaviors.
1160
+
1056
1161
  ## References
1057
1162
 
1058
1163
  [Google AI Studio](https://ai.google.dev/tutorials/ai-studio_quickstart)