Skip to content
This repository was archived by the owner on Jun 12, 2024. It is now read-only.

Commit 07a39ff

Browse files
committed
feat: add docstring and type hint for all funcs
1 parent 7d591d3 commit 07a39ff

File tree

7 files changed

+260
-40
lines changed

7 files changed

+260
-40
lines changed

gemini/core.py

+20
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class Gemini:
4141
auto_cookies (bool): Automatically manage cookies if True.
4242
timeout (int): Timeout for requests, defaults to 30 seconds.
4343
proxies (Optional[dict]): Proxy configuration for requests, if any.
44+
rcid (str): Response candidate ID.
4445
"""
4546

4647
def __init__(
@@ -179,6 +180,7 @@ def _construct_payload(
179180
180181
Parameters:
181182
prompt (str): The user prompt to send.
183+
image (Union[bytes, str]): The image data as bytes or file path. Supported formats: webp, jpeg, png.
182184
nonce (str): A one-time token used for request verification.
183185
184186
Returns:
@@ -261,6 +263,15 @@ def generate_content(
261263
return self._create_model_output(parsed_response)
262264

263265
def _create_model_output(self, parsed_response: dict) -> GeminiModelOutput:
266+
"""
267+
Creates model output from parsed response.
268+
269+
Args:
270+
parsed_response (dict): The parsed response data.
271+
272+
Returns:
273+
GeminiModelOutput: The model output containing metadata, candidates, and response dictionary.
274+
"""
264275
candidates = self.collect_candidates(parsed_response)
265276
metadata = parsed_response.get("metadata", [])
266277
try:
@@ -277,6 +288,15 @@ def _create_model_output(self, parsed_response: dict) -> GeminiModelOutput:
277288

278289
@staticmethod
279290
def collect_candidates(data):
291+
"""
292+
Collects candidate data from parsed response.
293+
294+
Args:
295+
data: The parsed response data.
296+
297+
Returns:
298+
List: A list of GeminiCandidate objects.
299+
"""
280300
collected = []
281301
stack = [data]
282302

gemini/src/misc/constants.py

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class Tool(Enum):
1313
YOUTUBE = ["youtube_tool"]
1414

1515

16+
IMAGE_PUSH_ID = "feeds/mcudyrk2a4khkz"
17+
1618
DEFAULT_LANGUAGE = "en"
1719
POST_ENDPOINT = "https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate"
1820
HOST = "https://gemini.google.com"

gemini/src/misc/utils.py

+26-24
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import json
33
import requests
44
from typing import Union
5-
from .constants import REPLIT_SUPPORT_PROGRAM_LANGUAGES
5+
from .constants import REPLIT_SUPPORT_PROGRAM_LANGUAGES, IMAGE_PUSH_ID
66

77

88
def extract_code(text: str, language: str) -> str:
@@ -68,7 +68,7 @@ def upload_image(file: Union[bytes, str]) -> str:
6868
response = requests.post(
6969
url="https://content-push.googleapis.com/upload/",
7070
headers={
71-
"Push-ID": "feeds/mcudyrk2a4khkz",
71+
"Push-ID": IMAGE_PUSH_ID,
7272
"Content-Type": "application/octet-stream",
7373
},
7474
data=file_data,
@@ -79,6 +79,30 @@ def upload_image(file: Union[bytes, str]) -> str:
7979
return response.text
8080

8181

82+
def build_replit_structure(instructions: str, code: str, filename: str) -> list:
83+
"""
84+
Creates and returns the input image data structure based on provided parameters.
85+
86+
Args:
87+
instructions (str): The instruction text.
88+
code (str): The code.
89+
filename (str): The filename.
90+
91+
Returns:
92+
list: The input image data structure.
93+
"""
94+
return [
95+
[
96+
[
97+
"qACoKe",
98+
json.dumps([instructions, 5, code, [[filename, code]]]),
99+
None,
100+
"generic",
101+
]
102+
]
103+
]
104+
105+
82106
def max_token(text: str, n: int) -> str:
83107
"""
84108
Return the first 'n' tokens (words) of the given text.
@@ -122,25 +146,3 @@ def max_sentence(text: str, n: int):
122146
if sentence_count == n:
123147
result = "".join(sentences).strip()
124148
return result
125-
126-
127-
def build_replit_data(instructions: str, code: str, filename: str) -> list:
128-
"""
129-
Creates and returns the input_image_data_struct based on provided parameters.
130-
131-
:param instructions: The instruction text.
132-
:param code: The code.
133-
:param filename: The filename.
134-
135-
:return: The input_image_data_struct.
136-
"""
137-
return [
138-
[
139-
[
140-
"qACoKe",
141-
json.dumps([instructions, 5, code, [[filename, code]]]),
142-
None,
143-
"generic",
144-
]
145-
]
146-
]

gemini/src/model/decorator.py

+48-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
import functools
22
import time
33
import requests
4+
from typing import Callable
45

56

6-
def retry(attempts=3, delay=2, backoff=2):
7-
def retry_decorator(func):
7+
def retry(attempts: int = 3, delay: int = 2, backoff: int = 2) -> Callable:
8+
"""
9+
Retries a function call with exponential backoff.
10+
11+
Args:
12+
attempts (int): The maximum number of attempts. Defaults to 3.
13+
delay (int): The initial delay in seconds between retries. Defaults to 2.
14+
backoff (int): The backoff factor. Defaults to 2.
15+
16+
Returns:
17+
Callable: Decorator function.
18+
"""
19+
20+
def retry_decorator(func: Callable) -> Callable:
821
@functools.wraps(func)
922
def wrapper(*args, **kwargs):
1023
_attempts, _delay = attempts, delay
@@ -23,7 +36,17 @@ def wrapper(*args, **kwargs):
2336
return retry_decorator
2437

2538

26-
def log_method(func):
39+
def log_method(func: Callable) -> Callable:
40+
"""
41+
Logs method entry and exit.
42+
43+
Args:
44+
func (Callable): The function to decorate.
45+
46+
Returns:
47+
Callable: Decorated function.
48+
"""
49+
2750
@functools.wraps(func)
2851
def wrapper(*args, **kwargs):
2952
className = args[0].__class__.__name__
@@ -39,7 +62,17 @@ def wrapper(*args, **kwargs):
3962
return wrapper
4063

4164

42-
def time_execution(func):
65+
def time_execution(func: Callable) -> Callable:
66+
"""
67+
Measures the execution time of a function.
68+
69+
Args:
70+
func (Callable): The function to decorate.
71+
72+
Returns:
73+
Callable: Decorated function.
74+
"""
75+
4376
@functools.wraps(func)
4477
def wrapper(*args, **kwargs):
4578
start_time = time.time()
@@ -51,7 +84,17 @@ def wrapper(*args, **kwargs):
5184
return wrapper
5285

5386

54-
def handle_errors(func):
87+
def handle_errors(func: Callable) -> Callable:
88+
"""
89+
Handles errors that occur during function execution.
90+
91+
Args:
92+
func (Callable): The function to decorate.
93+
94+
Returns:
95+
Callable: Decorated function.
96+
"""
97+
5598
@functools.wraps(func)
5699
def wrapper(*args, **kwargs):
57100
try:

gemini/src/model/exceptions.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
class PackageError(Exception):
2-
"""Invalid credentials/cookies."""
2+
"""
3+
Exception raised when encountering invalid credentials or cookies.
4+
5+
This exception indicates that the provided credentials or cookies are invalid or insufficient for the operation.
6+
7+
"""
38

49
pass
510

611

712
class GeminiAPIError(Exception):
8-
"""Unhandled server error."""
13+
"""
14+
Exception raised for unhandled errors from the Gemini server.
15+
16+
This exception indicates that an unexpected error occurred on the Gemini server side, which was not properly handled by the client.
17+
18+
"""
919

1020
pass
1121

1222

1323
class TimeoutError(GeminiAPIError):
14-
"""Request timeout."""
24+
"""
25+
Exception raised when a request times out.
26+
27+
This exception is a subclass of GeminiAPIError and is raised when a request to the Gemini server exceeds the specified timeout period without receiving a response.
28+
29+
"""
1530

1631
pass

gemini/src/model/image.py

+67
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,41 @@
1010

1111

1212
class GeminiImage(BaseModel):
13+
"""
14+
Represents an image with URL, title, and alt text.
15+
16+
Attributes:
17+
url (HttpUrl): The URL of the image.
18+
title (str): The title of the image. Defaults to "[Image]".
19+
alt (str): The alt text of the image. Defaults to "".
20+
21+
Methods:
22+
validate_images(cls, images): Validates the input images list.
23+
save(cls, images: List["GeminiImage"], save_path: str = "cached", cookies: Optional[dict] = None) -> Optional[Path]:
24+
Downloads and saves images asynchronously.
25+
fetch_bytes(url: HttpUrl, cookies: Optional[dict] = None) -> Optional[bytes]:
26+
Fetches bytes of an image asynchronously.
27+
fetch_images_dict(cls, images: List["GeminiImage"], cookies: Optional[dict] = None) -> Dict[str, bytes]:
28+
Fetches images asynchronously and returns a dictionary of image data.
29+
save_images(cls, image_data: Dict[str, bytes], save_path: str = "cached"):
30+
Saves images locally.
31+
"""
32+
1333
url: HttpUrl
1434
title: str = "[Image]"
1535
alt: str = ""
1636

1737
@classmethod
1838
def validate_images(cls, images):
39+
"""
40+
Validates the input images list.
41+
42+
Args:
43+
images: The list of GeminiImage objects.
44+
45+
Raises:
46+
ValueError: If the input images list is empty.
47+
"""
1948
if not images:
2049
raise ValueError(
2150
"Input is empty. Please provide images infomation to proceed."
@@ -29,6 +58,17 @@ async def save(
2958
save_path: str = "cached",
3059
cookies: Optional[dict] = None,
3160
) -> Optional[Path]:
61+
"""
62+
Downloads and saves images asynchronously.
63+
64+
Args:
65+
images (List["GeminiImage"]): The list of GeminiImage objects to download.
66+
save_path (str): The directory path to save the images. Defaults to "cached".
67+
cookies (Optional[dict]): Cookies to be used for downloading images. Defaults to None.
68+
69+
Returns:
70+
Optional[Path]: The path to the directory where the images are saved, or None if saving fails.
71+
"""
3272
cls.validate_images(images)
3373
image_data = await cls.fetch_images_dict(images, cookies)
3474
await cls.save_images(image_data, save_path)
@@ -37,6 +77,16 @@ async def save(
3777
async def fetch_bytes(
3878
url: HttpUrl, cookies: Optional[dict] = None
3979
) -> Optional[bytes]:
80+
"""
81+
Fetches bytes of an image asynchronously.
82+
83+
Args:
84+
url (HttpUrl): The URL of the image.
85+
cookies (Optional[dict]): Cookies to be used for fetching the image. Defaults to None.
86+
87+
Returns:
88+
Optional[bytes]: The bytes of the image, or None if fetching fails.
89+
"""
4090
try:
4191
async with httpx.AsyncClient(follow_redirects=True) as client:
4292
response = await client.get(str(url), cookies=cookies)
@@ -50,13 +100,30 @@ async def fetch_bytes(
50100
async def fetch_images_dict(
51101
cls, images: List["GeminiImage"], cookies: Optional[dict] = None
52102
) -> Dict[str, bytes]:
103+
"""
104+
Fetches images asynchronously and returns a dictionary of image data.
105+
106+
Args:
107+
images (List["GeminiImage"]): The list of GeminiImage objects to fetch.
108+
cookies (Optional[dict]): Cookies to be used for fetching the images. Defaults to None.
109+
110+
Returns:
111+
Dict[str, bytes]: A dictionary containing image titles as keys and image bytes as values.
112+
"""
53113
cls.validate_images(images)
54114
tasks = [cls.fetch_bytes(image.url, cookies=cookies) for image in images]
55115
results = await asyncio.gather(*tasks)
56116
return {image.title: result for image, result in zip(images, results) if result}
57117

58118
@staticmethod
59119
async def save_images(image_data: Dict[str, bytes], save_path: str = "cached"):
120+
"""
121+
Saves images locally.
122+
123+
Args:
124+
image_data (Dict[str, bytes]): A dictionary containing image titles as keys and image bytes as values.
125+
save_path (str): The directory path to save the images. Defaults to "cached".
126+
"""
60127
os.makedirs(save_path, exist_ok=True)
61128
for title, data in image_data.items():
62129
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")

0 commit comments

Comments
 (0)