webscout 8.3__py3-none-any.whl → 8.3.2__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.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (120) hide show
  1. webscout/AIauto.py +4 -4
  2. webscout/AIbase.py +61 -1
  3. webscout/AIutel.py +46 -53
  4. webscout/Bing_search.py +418 -0
  5. webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
  6. webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
  7. webscout/Extra/YTToolkit/ytapi/video.py +10 -10
  8. webscout/Extra/autocoder/autocoder_utiles.py +1 -1
  9. webscout/Extra/gguf.py +706 -177
  10. webscout/Litlogger/formats.py +9 -0
  11. webscout/Litlogger/handlers.py +18 -0
  12. webscout/Litlogger/logger.py +43 -1
  13. webscout/Provider/AISEARCH/genspark_search.py +7 -7
  14. webscout/Provider/AISEARCH/scira_search.py +3 -2
  15. webscout/Provider/GeminiProxy.py +140 -0
  16. webscout/Provider/LambdaChat.py +7 -1
  17. webscout/Provider/MCPCore.py +78 -75
  18. webscout/Provider/OPENAI/BLACKBOXAI.py +1046 -1017
  19. webscout/Provider/OPENAI/GeminiProxy.py +328 -0
  20. webscout/Provider/OPENAI/Qwen3.py +303 -303
  21. webscout/Provider/OPENAI/README.md +5 -0
  22. webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
  23. webscout/Provider/OPENAI/TogetherAI.py +355 -0
  24. webscout/Provider/OPENAI/__init__.py +16 -1
  25. webscout/Provider/OPENAI/autoproxy.py +332 -0
  26. webscout/Provider/OPENAI/base.py +101 -14
  27. webscout/Provider/OPENAI/chatgpt.py +15 -2
  28. webscout/Provider/OPENAI/chatgptclone.py +14 -3
  29. webscout/Provider/OPENAI/deepinfra.py +339 -328
  30. webscout/Provider/OPENAI/e2b.py +295 -74
  31. webscout/Provider/OPENAI/mcpcore.py +109 -70
  32. webscout/Provider/OPENAI/opkfc.py +18 -6
  33. webscout/Provider/OPENAI/scirachat.py +59 -50
  34. webscout/Provider/OPENAI/toolbaz.py +2 -10
  35. webscout/Provider/OPENAI/writecream.py +166 -166
  36. webscout/Provider/OPENAI/x0gpt.py +367 -367
  37. webscout/Provider/OPENAI/xenai.py +514 -0
  38. webscout/Provider/OPENAI/yep.py +389 -383
  39. webscout/Provider/STT/__init__.py +3 -0
  40. webscout/Provider/STT/base.py +281 -0
  41. webscout/Provider/STT/elevenlabs.py +265 -0
  42. webscout/Provider/TTI/__init__.py +4 -1
  43. webscout/Provider/TTI/aiarta.py +399 -365
  44. webscout/Provider/TTI/base.py +74 -2
  45. webscout/Provider/TTI/bing.py +231 -0
  46. webscout/Provider/TTI/fastflux.py +63 -30
  47. webscout/Provider/TTI/gpt1image.py +149 -0
  48. webscout/Provider/TTI/imagen.py +196 -0
  49. webscout/Provider/TTI/magicstudio.py +60 -29
  50. webscout/Provider/TTI/piclumen.py +43 -32
  51. webscout/Provider/TTI/pixelmuse.py +232 -225
  52. webscout/Provider/TTI/pollinations.py +43 -32
  53. webscout/Provider/TTI/together.py +287 -0
  54. webscout/Provider/TTI/utils.py +2 -1
  55. webscout/Provider/TTS/README.md +1 -0
  56. webscout/Provider/TTS/__init__.py +2 -1
  57. webscout/Provider/TTS/freetts.py +140 -0
  58. webscout/Provider/TTS/speechma.py +45 -39
  59. webscout/Provider/TogetherAI.py +366 -0
  60. webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
  61. webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
  62. webscout/Provider/XenAI.py +324 -0
  63. webscout/Provider/__init__.py +8 -0
  64. webscout/Provider/deepseek_assistant.py +378 -0
  65. webscout/Provider/scira_chat.py +3 -2
  66. webscout/Provider/toolbaz.py +0 -1
  67. webscout/auth/__init__.py +44 -0
  68. webscout/auth/api_key_manager.py +189 -0
  69. webscout/auth/auth_system.py +100 -0
  70. webscout/auth/config.py +76 -0
  71. webscout/auth/database.py +400 -0
  72. webscout/auth/exceptions.py +67 -0
  73. webscout/auth/middleware.py +248 -0
  74. webscout/auth/models.py +130 -0
  75. webscout/auth/providers.py +257 -0
  76. webscout/auth/rate_limiter.py +254 -0
  77. webscout/auth/request_models.py +127 -0
  78. webscout/auth/request_processing.py +226 -0
  79. webscout/auth/routes.py +526 -0
  80. webscout/auth/schemas.py +103 -0
  81. webscout/auth/server.py +312 -0
  82. webscout/auth/static/favicon.svg +11 -0
  83. webscout/auth/swagger_ui.py +203 -0
  84. webscout/auth/templates/components/authentication.html +237 -0
  85. webscout/auth/templates/components/base.html +103 -0
  86. webscout/auth/templates/components/endpoints.html +750 -0
  87. webscout/auth/templates/components/examples.html +491 -0
  88. webscout/auth/templates/components/footer.html +75 -0
  89. webscout/auth/templates/components/header.html +27 -0
  90. webscout/auth/templates/components/models.html +286 -0
  91. webscout/auth/templates/components/navigation.html +70 -0
  92. webscout/auth/templates/static/api.js +455 -0
  93. webscout/auth/templates/static/icons.js +168 -0
  94. webscout/auth/templates/static/main.js +784 -0
  95. webscout/auth/templates/static/particles.js +201 -0
  96. webscout/auth/templates/static/styles.css +3353 -0
  97. webscout/auth/templates/static/ui.js +374 -0
  98. webscout/auth/templates/swagger_ui.html +170 -0
  99. webscout/client.py +49 -3
  100. webscout/litagent/Readme.md +12 -3
  101. webscout/litagent/agent.py +99 -62
  102. webscout/scout/core/scout.py +104 -26
  103. webscout/scout/element.py +139 -18
  104. webscout/swiftcli/core/cli.py +14 -3
  105. webscout/swiftcli/decorators/output.py +59 -9
  106. webscout/update_checker.py +31 -49
  107. webscout/version.py +1 -1
  108. webscout/webscout_search.py +4 -12
  109. webscout/webscout_search_async.py +3 -10
  110. webscout/yep_search.py +2 -11
  111. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/METADATA +41 -11
  112. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/RECORD +116 -68
  113. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/entry_points.txt +1 -1
  114. webscout/Provider/HF_space/__init__.py +0 -0
  115. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  116. webscout/Provider/OPENAI/api.py +0 -1035
  117. webscout/Provider/TTI/artbit.py +0 -0
  118. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/WHEEL +0 -0
  119. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/licenses/LICENSE.md +0 -0
  120. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import List, Dict, Optional, Any, Union, Generator
2
+ from typing import Any, Optional
3
3
  from .utils import ImageResponse
4
- import random
4
+
5
+
5
6
 
6
7
  class BaseImages(ABC):
7
8
  @abstractmethod
@@ -43,10 +44,81 @@ class BaseImages(ABC):
43
44
  """
44
45
  raise NotImplementedError
45
46
 
47
+ # class ProxyAutoMeta(ABCMeta):
48
+ # """Metaclass providing seamless proxy injection for providers."""
49
+
50
+ # def __call__(cls, *args, **kwargs):
51
+ # # Determine if automatic proxying should be disabled
52
+ # disable_auto_proxy = kwargs.get('disable_auto_proxy', False) or getattr(cls, 'DISABLE_AUTO_PROXY', False)
53
+
54
+ # # Proxies may be supplied explicitly
55
+ # proxies = kwargs.get('proxies', None)
56
+
57
+ # # Otherwise try to fetch one automatically
58
+ # if proxies is None and not disable_auto_proxy:
59
+ # try:
60
+ # proxies = {"http": get_auto_proxy(), "https": get_auto_proxy()}
61
+ # except Exception as e:
62
+ # print(f"Failed to fetch auto-proxy: {e}")
63
+ # proxies = {}
64
+ # elif proxies is None:
65
+ # proxies = {}
66
+
67
+ # # No global monkeypatching, just set proxies on the instance
68
+ # instance = super().__call__(*args, **kwargs)
69
+ # instance.proxies = proxies
70
+
71
+ # # If proxies are set, patch any existing session-like attributes
72
+ # if proxies:
73
+ # for attr in dir(instance):
74
+ # obj = getattr(instance, attr)
75
+ # if isinstance(obj, requests.Session):
76
+ # obj.proxies.update(proxies)
77
+ # if CurlSession and isinstance(obj, CurlSession):
78
+ # try:
79
+ # obj.proxies.update(proxies)
80
+ # except (ValueError, KeyError, AttributeError):
81
+ # print("Failed to update proxies for CurlSession due to an expected error.")
82
+ # if CurlAsyncSession and isinstance(obj, CurlAsyncSession):
83
+ # try:
84
+ # obj.proxies.update(proxies)
85
+ # except (ValueError, KeyError, AttributeError):
86
+ # print("Failed to update proxies for CurlAsyncSession due to an expected error.")
87
+
88
+ # # Helper for backward compatibility
89
+ # def get_proxied_session():
90
+ # s = requests.Session()
91
+ # s.proxies.update(proxies)
92
+ # return s
93
+
94
+ # instance.get_proxied_session = get_proxied_session
95
+
96
+ # def get_proxied_curl_session(impersonate="chrome120", **kw):
97
+ # if CurlSession:
98
+ # return CurlSession(proxies=proxies, impersonate=impersonate, **kw)
99
+ # raise ImportError("curl_cffi is not installed")
100
+
101
+ # instance.get_proxied_curl_session = get_proxied_curl_session
102
+
103
+ # def get_proxied_curl_async_session(impersonate="chrome120", **kw):
104
+ # if CurlAsyncSession:
105
+ # return CurlAsyncSession(proxies=proxies, impersonate=impersonate, **kw)
106
+ # raise ImportError("curl_cffi is not installed")
107
+
108
+ # instance.get_proxied_curl_async_session = get_proxied_curl_async_session
109
+
110
+ # return instance
111
+
46
112
  class TTICompatibleProvider(ABC):
47
113
  """
48
114
  Abstract Base Class for TTI providers mimicking the OpenAI Python client structure.
49
115
  Requires a nested 'images.create' structure.
116
+ All subclasses automatically get proxy support via ProxyAutoMeta.
117
+
118
+ Available proxy helpers:
119
+ - self.get_proxied_session() - returns a requests.Session with proxies
120
+ - self.get_proxied_curl_session() - returns a curl_cffi.Session with proxies
121
+ - self.get_proxied_curl_async_session() - returns a curl_cffi.AsyncSession with proxies
50
122
  """
51
123
  images: BaseImages
52
124
 
@@ -0,0 +1,231 @@
1
+ import requests
2
+ import time
3
+ import tempfile
4
+ import os
5
+ from typing import Optional
6
+ from webscout.Provider.TTI.utils import ImageData, ImageResponse
7
+ from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
8
+ from io import BytesIO
9
+ from webscout.litagent import LitAgent
10
+
11
+ try:
12
+ from PIL import Image
13
+ except ImportError:
14
+ Image = None
15
+
16
+ class Images(BaseImages):
17
+ def __init__(self, client):
18
+ self._client = client
19
+
20
+ def create(
21
+ self,
22
+ *,
23
+ model: str = "bing",
24
+ prompt: str,
25
+ n: int = 1,
26
+ size: str = "1024x1024",
27
+ response_format: str = "url",
28
+ user: Optional[str] = None,
29
+ style: str = "none",
30
+ aspect_ratio: str = "1:1",
31
+ timeout: int = 60,
32
+ image_format: str = "png",
33
+ seed: Optional[int] = None,
34
+ **kwargs
35
+ ) -> ImageResponse:
36
+ if not prompt:
37
+ raise ValueError("Parameter 'prompt' is required")
38
+ if Image is None:
39
+ raise ImportError("Pillow (PIL) is required for image format conversion.")
40
+ agent = LitAgent()
41
+ session = self._client.session
42
+ headers = self._client.headers
43
+ images = []
44
+ urls = []
45
+ for _ in range(n):
46
+ data = {
47
+ "q": prompt,
48
+ "rt": "4",
49
+ "FORM": "GENCRE"
50
+ }
51
+ response = session.post(
52
+ "https://www.bing.com/images/create",
53
+ data=data,
54
+ headers=headers,
55
+ allow_redirects=False,
56
+ timeout=timeout
57
+ )
58
+ redirect_url = response.headers.get("Location")
59
+ if not redirect_url:
60
+ raise Exception("Failed to get redirect URL")
61
+ from urllib.parse import urlparse, parse_qs
62
+ query = urlparse(redirect_url).query
63
+ request_id = parse_qs(query).get("id", [None])[0]
64
+ if not request_id:
65
+ raise Exception("ID not found in URL")
66
+ polling_url = f"https://www.bing.com/images/create/async/results/{request_id}?q={requests.utils.quote(prompt)}"
67
+ attempts = 0
68
+ img_url = None
69
+ while attempts < 10:
70
+ time.sleep(3)
71
+ try:
72
+ poll_resp = session.get(polling_url, headers=headers, timeout=timeout)
73
+ from bs4 import BeautifulSoup
74
+ soup = BeautifulSoup(poll_resp.text, "html.parser")
75
+ imgs = [img["src"].split("?")[0] for img in soup.select(".img_cont .mimg") if img.get("src")]
76
+ if imgs:
77
+ img_url = imgs[0]
78
+ break
79
+ except Exception:
80
+ pass
81
+ attempts += 1
82
+ if not img_url:
83
+ raise Exception("Failed to get images after polling.")
84
+ img_bytes = session.get(img_url, headers=headers, timeout=timeout).content
85
+ # Convert to png or jpeg in memory
86
+ with BytesIO(img_bytes) as input_io:
87
+ with Image.open(input_io) as im:
88
+ out_io = BytesIO()
89
+ if image_format.lower() == "jpeg":
90
+ im = im.convert("RGB")
91
+ im.save(out_io, format="JPEG")
92
+ else:
93
+ im.save(out_io, format="PNG")
94
+ img_bytes = out_io.getvalue()
95
+ images.append(img_bytes)
96
+ if response_format == "url":
97
+ def upload_file_with_retry(img_bytes, image_format, max_retries=3):
98
+ ext = "jpg" if image_format.lower() == "jpeg" else "png"
99
+ for attempt in range(max_retries):
100
+ tmp_path = None
101
+ try:
102
+ with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
103
+ tmp.write(img_bytes)
104
+ tmp.flush()
105
+ tmp_path = tmp.name
106
+ with open(tmp_path, "rb") as f:
107
+ files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
108
+ data = {"reqtype": "fileupload", "json": "true"}
109
+ headers2 = {"User-Agent": agent.random()}
110
+ if attempt > 0:
111
+ headers2["Connection"] = "close"
112
+ resp2 = requests.post(
113
+ "https://catbox.moe/user/api.php",
114
+ files=files,
115
+ data=data,
116
+ headers=headers2,
117
+ timeout=timeout,
118
+ )
119
+ if resp2.status_code == 200 and resp2.text.strip():
120
+ text = resp2.text.strip()
121
+ if text.startswith("http"):
122
+ return text
123
+ try:
124
+ result = resp2.json()
125
+ if "url" in result:
126
+ return result["url"]
127
+ except Exception:
128
+ if "http" in text:
129
+ return text
130
+ except Exception:
131
+ if attempt < max_retries - 1:
132
+ time.sleep(1 * (attempt + 1))
133
+ finally:
134
+ if tmp_path and os.path.isfile(tmp_path):
135
+ try:
136
+ os.remove(tmp_path)
137
+ except Exception:
138
+ pass
139
+ return None
140
+ def upload_file_alternative(img_bytes, image_format):
141
+ try:
142
+ ext = "jpg" if image_format.lower() == "jpeg" else "png"
143
+ with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
144
+ tmp.write(img_bytes)
145
+ tmp.flush()
146
+ tmp_path = tmp.name
147
+ try:
148
+ if not os.path.isfile(tmp_path):
149
+ return None
150
+ with open(tmp_path, "rb") as img_file:
151
+ files = {"file": img_file}
152
+ alt_resp = requests.post("https://0x0.st", files=files)
153
+ alt_resp.raise_for_status()
154
+ image_url = alt_resp.text.strip()
155
+ if not image_url.startswith("http"):
156
+ return None
157
+ return image_url
158
+ except Exception:
159
+ return None
160
+ finally:
161
+ try:
162
+ os.remove(tmp_path)
163
+ except Exception:
164
+ pass
165
+ except Exception:
166
+ return None
167
+ uploaded_url = upload_file_with_retry(img_bytes, image_format)
168
+ if not uploaded_url:
169
+ uploaded_url = upload_file_alternative(img_bytes, image_format)
170
+ if uploaded_url:
171
+ urls.append(uploaded_url)
172
+ else:
173
+ raise RuntimeError(
174
+ "Failed to upload image to catbox.moe using all available methods"
175
+ )
176
+ result_data = []
177
+ if response_format == "url":
178
+ for url in urls:
179
+ result_data.append(ImageData(url=url))
180
+ elif response_format == "b64_json":
181
+ import base64
182
+ for img in images:
183
+ b64 = base64.b64encode(img).decode("utf-8")
184
+ result_data.append(ImageData(b64_json=b64))
185
+ else:
186
+ raise ValueError("response_format must be 'url' or 'b64_json'")
187
+ from time import time as _time
188
+ return ImageResponse(created=int(_time()), data=result_data)
189
+
190
+ class BingImageAI(TTICompatibleProvider):
191
+ AVAILABLE_MODELS = ["bing"]
192
+ def __init__(self, cookie: Optional[str] = None):
193
+ self.session = requests.Session()
194
+ self.user_agent = LitAgent().random()
195
+ self.headers = {
196
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
197
+ "accept-language": "id-ID,id;q=0.9",
198
+ "cache-control": "max-age=0",
199
+ "content-type": "application/x-www-form-urlencoded",
200
+ "origin": "https://www.bing.com",
201
+ "referer": "https://www.bing.com/images/create?&wlexpsignin=1",
202
+ "sec-ch-ua": '"Chromium";v="131", "Not_A Brand";v="24", "Microsoft Edge Simulate";v="131", "Lemur";v="131"',
203
+ "sec-ch-ua-mobile": "?1",
204
+ "sec-fetch-site": "same-origin",
205
+ "sec-fetch-mode": "navigate",
206
+ "sec-fetch-dest": "document",
207
+ "upgrade-insecure-requests": "1",
208
+ "user-agent": self.user_agent,
209
+ }
210
+ self.session.headers.update(self.headers)
211
+ self.cookie = cookie
212
+ if cookie:
213
+ self.session.cookies.set("_U", cookie, domain="bing.com")
214
+ self.images = Images(self)
215
+ @property
216
+ def models(self):
217
+ class _ModelList:
218
+ def list(inner_self):
219
+ return type(self).AVAILABLE_MODELS
220
+ return _ModelList()
221
+
222
+ if __name__ == "__main__":
223
+ from rich import print
224
+ client = BingImageAI(cookie="1pkdvumH1SEjFkDjFymRYKouIRoXZlh_p5RTfAttx4DaaNOSDyz8qFP2M7LbZ93fbl4f6Xm8fTGwXHNDB648Gom5jfnTU_Iz-VH47l0HTYJDS1sItbBBS-sqSISFgXR62SoqnW5eX5MFht-j2uB1gZ4uDnpR_60fLRTCdW1SIRegDvnBm1TGhRiZsi6wUPyzwFg7-PsXAs3Fq9iV9m-0FEw")
225
+ response = client.images.create(
226
+ prompt="A cat riding a bicycle",
227
+ response_format="url",
228
+ n=4,
229
+ timeout=30
230
+ )
231
+ print(response)
@@ -2,8 +2,12 @@ import requests
2
2
  import os
3
3
  import tempfile
4
4
  import time
5
+ import base64
5
6
  from typing import Optional, List, Dict, Any
6
- from webscout.Provider.TTI.utils import ImageData, ImageResponse
7
+ from webscout.Provider.TTI.utils import (
8
+ ImageData,
9
+ ImageResponse
10
+ )
7
11
  from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
8
12
  from io import BytesIO
9
13
  from webscout.litagent import LitAgent
@@ -13,11 +17,13 @@ try:
13
17
  except ImportError:
14
18
  Image = None
15
19
 
20
+
16
21
  class Images(BaseImages):
17
22
  def __init__(self, client):
18
23
  self._client = client
19
24
 
20
- def create(self,
25
+ def create(
26
+ self,
21
27
  model: str = "flux_1_schnell",
22
28
  prompt: str = None,
23
29
  n: int = 1,
@@ -29,7 +35,7 @@ class Images(BaseImages):
29
35
  timeout: int = 60,
30
36
  image_format: str = "png",
31
37
  is_public: bool = False,
32
- **kwargs
38
+ **kwargs,
33
39
  ) -> ImageResponse:
34
40
  if not prompt:
35
41
  raise ValueError("Prompt is required!")
@@ -41,15 +47,19 @@ class Images(BaseImages):
41
47
  "prompt": prompt,
42
48
  "model": model,
43
49
  "size": size,
44
- "isPublic": is_public
50
+ "isPublic": is_public,
45
51
  }
46
52
  for _ in range(n):
47
- resp = self._client.session.post(api_url, json=payload, timeout=timeout)
53
+ resp = self._client.session.post(
54
+ api_url,
55
+ json=payload,
56
+ timeout=timeout,
57
+ )
48
58
  resp.raise_for_status()
49
59
  result = resp.json()
50
- if result and 'result' in result:
51
- image_data = result['result']
52
- base64_data = image_data.split(',')[1]
60
+ if result and "result" in result:
61
+ image_data = result["result"]
62
+ base64_data = image_data.split(",")[1]
53
63
  img_bytes = base64.b64decode(base64_data)
54
64
  # Convert to png or jpeg in memory if needed
55
65
  if Image is not None:
@@ -64,37 +74,47 @@ class Images(BaseImages):
64
74
  img_bytes = out_io.getvalue()
65
75
  images.append(img_bytes)
66
76
  if response_format == "url":
77
+
67
78
  def upload_file_with_retry(img_bytes, image_format, max_retries=3):
68
79
  ext = "jpg" if image_format.lower() == "jpeg" else "png"
69
80
  for attempt in range(max_retries):
70
81
  tmp_path = None
71
82
  try:
72
- with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
83
+ with tempfile.NamedTemporaryFile(
84
+ suffix=f".{ext}", delete=False
85
+ ) as tmp:
73
86
  tmp.write(img_bytes)
74
87
  tmp.flush()
75
88
  tmp_path = tmp.name
76
- with open(tmp_path, 'rb') as f:
89
+ with open(tmp_path, "rb") as f:
77
90
  files = {
78
- 'fileToUpload': (f'image.{ext}', f, f'image/{ext}')
91
+ "fileToUpload": (
92
+ f"image.{ext}",
93
+ f,
94
+ f"image/{ext}",
95
+ )
79
96
  }
80
- data = {
81
- 'reqtype': 'fileupload',
82
- 'json': 'true'
83
- }
84
- headers = {'User-Agent': agent.random()}
97
+ data = {"reqtype": "fileupload", "json": "true"}
98
+ headers = {"User-Agent": agent.random()}
85
99
  if attempt > 0:
86
- headers['Connection'] = 'close'
87
- resp = requests.post("https://catbox.moe/user/api.php", files=files, data=data, headers=headers, timeout=timeout)
100
+ headers["Connection"] = "close"
101
+ resp = requests.post(
102
+ "https://catbox.moe/user/api.php",
103
+ files=files,
104
+ data=data,
105
+ headers=headers,
106
+ timeout=timeout,
107
+ )
88
108
  if resp.status_code == 200 and resp.text.strip():
89
109
  text = resp.text.strip()
90
- if text.startswith('http'):
110
+ if text.startswith("http"):
91
111
  return text
92
112
  try:
93
113
  result = resp.json()
94
114
  if "url" in result:
95
115
  return result["url"]
96
116
  except Exception:
97
- if 'http' in text:
117
+ if "http" in text:
98
118
  return text
99
119
  except Exception:
100
120
  if attempt < max_retries - 1:
@@ -106,22 +126,27 @@ class Images(BaseImages):
106
126
  except Exception:
107
127
  pass
108
128
  return None
129
+
109
130
  def upload_file_alternative(img_bytes, image_format):
110
131
  try:
111
132
  ext = "jpg" if image_format.lower() == "jpeg" else "png"
112
- with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
133
+ with tempfile.NamedTemporaryFile(
134
+ suffix=f".{ext}", delete=False
135
+ ) as tmp:
113
136
  tmp.write(img_bytes)
114
137
  tmp.flush()
115
138
  tmp_path = tmp.name
116
139
  try:
117
140
  if not os.path.isfile(tmp_path):
118
141
  return None
119
- with open(tmp_path, 'rb') as img_file:
120
- files = {'file': img_file}
121
- response = requests.post('https://0x0.st', files=files)
142
+ with open(tmp_path, "rb") as img_file:
143
+ files = {"file": img_file}
144
+ response = requests.post(
145
+ "https://0x0.st", files=files
146
+ )
122
147
  response.raise_for_status()
123
148
  image_url = response.text.strip()
124
- if not image_url.startswith('http'):
149
+ if not image_url.startswith("http"):
125
150
  return None
126
151
  return image_url
127
152
  except Exception:
@@ -133,13 +158,16 @@ class Images(BaseImages):
133
158
  pass
134
159
  except Exception:
135
160
  return None
161
+
136
162
  uploaded_url = upload_file_with_retry(img_bytes, image_format)
137
163
  if not uploaded_url:
138
164
  uploaded_url = upload_file_alternative(img_bytes, image_format)
139
165
  if uploaded_url:
140
166
  urls.append(uploaded_url)
141
167
  else:
142
- raise RuntimeError("Failed to upload image to catbox.moe using all available methods")
168
+ raise RuntimeError(
169
+ "Failed to upload image to catbox.moe using all available methods"
170
+ )
143
171
  else:
144
172
  raise RuntimeError("No image data received from FastFlux API")
145
173
  result_data = []
@@ -148,21 +176,22 @@ class Images(BaseImages):
148
176
  result_data.append(ImageData(url=url))
149
177
  elif response_format == "b64_json":
150
178
  import base64
179
+
151
180
  for img in images:
152
181
  b64 = base64.b64encode(img).decode("utf-8")
153
182
  result_data.append(ImageData(b64_json=b64))
154
183
  else:
155
184
  raise ValueError("response_format must be 'url' or 'b64_json'")
156
185
  from time import time as _time
157
- return ImageResponse(
158
- created=int(_time()),
159
- data=result_data
160
- )
186
+
187
+ return ImageResponse(created=int(_time()), data=result_data)
188
+
161
189
 
162
190
  class FastFluxAI(TTICompatibleProvider):
163
191
  AVAILABLE_MODELS = [
164
192
  "flux_1_schnell",
165
193
  ]
194
+
166
195
  def __init__(self, api_key: str = None):
167
196
  self.api_endpoint = "https://api.freeflux.ai/v1/images/generate"
168
197
  self.session = requests.Session()
@@ -180,15 +209,19 @@ class FastFluxAI(TTICompatibleProvider):
180
209
  self.headers["authorization"] = f"Bearer {self.api_key}"
181
210
  self.session.headers.update(self.headers)
182
211
  self.images = Images(self)
212
+
183
213
  @property
184
214
  def models(self):
185
215
  class _ModelList:
186
216
  def list(inner_self):
187
217
  return type(self).AVAILABLE_MODELS
218
+
188
219
  return _ModelList()
189
220
 
221
+
190
222
  if __name__ == "__main__":
191
223
  from rich import print
224
+
192
225
  client = FastFluxAI()
193
226
  response = client.images.create(
194
227
  model="flux_1_schnell",