webscout 8.3__py3-none-any.whl → 8.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (62) hide show
  1. webscout/AIauto.py +4 -4
  2. webscout/AIbase.py +61 -1
  3. webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
  4. webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
  5. webscout/Extra/YTToolkit/ytapi/video.py +10 -10
  6. webscout/Extra/autocoder/autocoder_utiles.py +1 -1
  7. webscout/Litlogger/formats.py +9 -0
  8. webscout/Litlogger/handlers.py +18 -0
  9. webscout/Litlogger/logger.py +43 -1
  10. webscout/Provider/AISEARCH/scira_search.py +3 -2
  11. webscout/Provider/LambdaChat.py +7 -1
  12. webscout/Provider/OPENAI/BLACKBOXAI.py +1049 -1017
  13. webscout/Provider/OPENAI/Qwen3.py +303 -303
  14. webscout/Provider/OPENAI/README.md +3 -0
  15. webscout/Provider/OPENAI/TogetherAI.py +355 -0
  16. webscout/Provider/OPENAI/__init__.py +2 -1
  17. webscout/Provider/OPENAI/api.py +298 -13
  18. webscout/Provider/OPENAI/autoproxy.py +39 -0
  19. webscout/Provider/OPENAI/base.py +89 -12
  20. webscout/Provider/OPENAI/chatgpt.py +15 -2
  21. webscout/Provider/OPENAI/chatgptclone.py +14 -3
  22. webscout/Provider/OPENAI/deepinfra.py +339 -328
  23. webscout/Provider/OPENAI/e2b.py +295 -73
  24. webscout/Provider/OPENAI/opkfc.py +18 -6
  25. webscout/Provider/OPENAI/scirachat.py +3 -2
  26. webscout/Provider/OPENAI/toolbaz.py +0 -1
  27. webscout/Provider/OPENAI/writecream.py +166 -166
  28. webscout/Provider/OPENAI/x0gpt.py +367 -367
  29. webscout/Provider/OPENAI/yep.py +383 -383
  30. webscout/Provider/STT/__init__.py +3 -0
  31. webscout/Provider/STT/base.py +281 -0
  32. webscout/Provider/STT/elevenlabs.py +265 -0
  33. webscout/Provider/TTI/__init__.py +3 -1
  34. webscout/Provider/TTI/aiarta.py +399 -365
  35. webscout/Provider/TTI/base.py +74 -2
  36. webscout/Provider/TTI/fastflux.py +63 -30
  37. webscout/Provider/TTI/gpt1image.py +149 -0
  38. webscout/Provider/TTI/imagen.py +196 -0
  39. webscout/Provider/TTI/magicstudio.py +60 -29
  40. webscout/Provider/TTI/piclumen.py +43 -32
  41. webscout/Provider/TTI/pixelmuse.py +232 -225
  42. webscout/Provider/TTI/pollinations.py +43 -32
  43. webscout/Provider/TTI/together.py +287 -0
  44. webscout/Provider/TTI/utils.py +2 -1
  45. webscout/Provider/TTS/README.md +1 -0
  46. webscout/Provider/TTS/__init__.py +2 -1
  47. webscout/Provider/TTS/freetts.py +140 -0
  48. webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
  49. webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
  50. webscout/Provider/__init__.py +3 -0
  51. webscout/Provider/scira_chat.py +3 -2
  52. webscout/Provider/toolbaz.py +0 -1
  53. webscout/litagent/Readme.md +12 -3
  54. webscout/litagent/agent.py +99 -62
  55. webscout/version.py +1 -1
  56. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/METADATA +1 -1
  57. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/RECORD +61 -51
  58. webscout/Provider/TTI/artbit.py +0 -0
  59. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/WHEEL +0 -0
  60. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/entry_points.txt +0 -0
  61. {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/licenses/LICENSE.md +0 -0
  62. {webscout-8.3.dist-info → webscout-8.3.1.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
 
@@ -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",
@@ -0,0 +1,149 @@
1
+ import requests
2
+ import random
3
+ import string
4
+ from typing import Optional, List, Dict, Any
5
+ from webscout.Provider.TTI.utils import (
6
+ ImageData,
7
+ ImageResponse
8
+ )
9
+ from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
10
+ from io import BytesIO
11
+ import os
12
+ import tempfile
13
+ from webscout.litagent import LitAgent
14
+ import time
15
+ from requests.adapters import HTTPAdapter
16
+ from urllib3.util.retry import Retry
17
+
18
+
19
+ class Images(BaseImages):
20
+ def __init__(self, client):
21
+ self._client = client
22
+ self.base_url = "https://gpt1image.exomlapi.com"
23
+ # Create a session - it will automatically get proxies from the global monkey patch!
24
+ self.session = requests.Session()
25
+ self._setup_session_with_retries()
26
+
27
+ def _setup_session_with_retries(self):
28
+ """Setup session with retry strategy and timeout configurations"""
29
+ # Configure retry strategy
30
+ retry_strategy = Retry(
31
+ total=3,
32
+ status_forcelist=[429, 500, 502, 503, 504],
33
+ backoff_factor=1,
34
+ allowed_methods=["HEAD", "GET", "OPTIONS", "POST"],
35
+ )
36
+
37
+ adapter = HTTPAdapter(max_retries=retry_strategy)
38
+ self.session.mount("http://", adapter)
39
+ self.session.mount("https://", adapter)
40
+
41
+ # Set timeouts
42
+ # self.session.timeout = (10, 30) # (connect_timeout, read_timeout)
43
+ # Unlimited timeout: do not set session timeout here
44
+
45
+ def build_headers(self, extra: Optional[Dict[str, str]] = None) -> Dict[str, str]:
46
+ agent = LitAgent()
47
+ fp = agent.generate_fingerprint("chrome")
48
+ headers = {
49
+ "Content-Type": "application/json",
50
+ "accept": fp["accept"],
51
+ "accept-language": fp["accept_language"],
52
+ "origin": self.base_url,
53
+ "referer": f"{self.base_url}/",
54
+ "user-agent": fp["user_agent"],
55
+ "sec-ch-ua": fp["sec_ch_ua"],
56
+ "sec-ch-ua-mobile": "?0",
57
+ "sec-ch-ua-platform": '"Windows"',
58
+ "sec-fetch-dest": "empty",
59
+ "sec-fetch-mode": "cors",
60
+ "sec-fetch-site": "same-origin",
61
+ "x-forwarded-for": fp["x-forwarded-for"],
62
+ "x-real-ip": fp["x-real-ip"],
63
+ "x-request-id": fp["x-request-id"],
64
+ }
65
+ if extra:
66
+ headers.update(extra)
67
+ return headers
68
+
69
+ def create(
70
+ self,
71
+ model: str = None,
72
+ prompt: str = None,
73
+ n: int = 1,
74
+ size: str = "1024x1024",
75
+ response_format: str = "url",
76
+ user: Optional[str] = None,
77
+ style: str = None,
78
+ aspect_ratio: str = None,
79
+ timeout: int = 60,
80
+ image_format: str = "png",
81
+ enhance: bool = True,
82
+ **kwargs,
83
+ ) -> ImageResponse:
84
+ if not prompt:
85
+ raise ValueError(
86
+ "Describe the image you want to create (use the 'prompt' property)."
87
+ )
88
+ body = {
89
+ "prompt": prompt,
90
+ "n": n,
91
+ "size": size,
92
+ "is_enhance": enhance,
93
+ "response_format": response_format,
94
+ }
95
+ try:
96
+ # Use direct session.request instead of request_with_proxy_fallback
97
+ resp = self.session.request(
98
+ "post",
99
+ f"{self.base_url}/v1/images/generations",
100
+ json=body,
101
+ headers=self.build_headers(),
102
+ timeout=timeout,
103
+ )
104
+ data = resp.json()
105
+ if not data.get("data") or len(data["data"]) == 0:
106
+ error_info = (
107
+ f", server info: {data.get('error')}" if data.get("error") else ""
108
+ )
109
+ raise RuntimeError(
110
+ f"Failed to process image. No data found{error_info}."
111
+ )
112
+ result = data["data"]
113
+ result_data = []
114
+ for item in result:
115
+ if response_format == "url":
116
+ result_data.append(ImageData(url=item.get("url")))
117
+ else:
118
+ result_data.append(ImageData(b64_json=item.get("b64_json")))
119
+ return ImageResponse(data=result_data)
120
+ except Exception as e:
121
+ raise RuntimeError(f"An error occurred: {str(e)}")
122
+
123
+
124
+ class GPT1Image(TTICompatibleProvider):
125
+ AVAILABLE_MODELS = ["gpt1image"]
126
+
127
+ def __init__(self):
128
+ self.images = Images(self)
129
+
130
+ @property
131
+ def models(self):
132
+ class _ModelList:
133
+ def list(inner_self):
134
+ return type(self).AVAILABLE_MODELS
135
+
136
+ return _ModelList()
137
+
138
+
139
+ if __name__ == "__main__":
140
+ from rich import print
141
+
142
+ client = GPT1Image()
143
+ response = client.images.create(
144
+ prompt="A futuristic robot in a neon city",
145
+ response_format="url",
146
+ n=1,
147
+ timeout=None,
148
+ )
149
+ print(response)
@@ -0,0 +1,196 @@
1
+ import requests
2
+ import base64
3
+ from typing import Optional, List, Dict, Any
4
+ from webscout.Provider.TTI.utils import (
5
+ ImageData,
6
+ ImageResponse,
7
+ )
8
+ from webscout.Provider.TTI.base import TTICompatibleProvider, BaseImages
9
+ from webscout.litagent import LitAgent
10
+ import time
11
+
12
+
13
+ class Images(BaseImages):
14
+ def __init__(self, client):
15
+ self._client = client
16
+
17
+ def create(
18
+ self,
19
+ *,
20
+ model: str,
21
+ prompt: str,
22
+ n: int = 1,
23
+ size: str = "1024x1024",
24
+ response_format: str = "url",
25
+ user: Optional[str] = None,
26
+ style: str = "none",
27
+ aspect_ratio: str = "1:1",
28
+ timeout: int = 60,
29
+ image_format: str = "png",
30
+ seed: Optional[int] = None,
31
+ **kwargs,
32
+ ) -> ImageResponse:
33
+ """
34
+ Create images using the Imagen API.
35
+
36
+ Args:
37
+ model: The model to use (e.g., "imagen_3_5")
38
+ prompt: The text prompt for image generation
39
+ n: Number of images to generate
40
+ size: Image size (e.g., "1024x1024")
41
+ response_format: "url" or "b64_json"
42
+ timeout: Request timeout in seconds
43
+ **kwargs: Additional parameters
44
+
45
+ Returns:
46
+ ImageResponse: The generated images
47
+ """
48
+ if not prompt:
49
+ raise ValueError("Prompt is required!")
50
+
51
+ result_data = []
52
+
53
+ for _ in range(n):
54
+ # Prepare the request payload
55
+ payload = {
56
+ "prompt": prompt,
57
+ "model": model,
58
+ "size": size,
59
+ "response_format": "url", # Always request URL from API
60
+ }
61
+
62
+ try:
63
+ # Make the API request
64
+ resp = self._client.session.request(
65
+ "post",
66
+ self._client.api_endpoint,
67
+ json=payload,
68
+ timeout=timeout,
69
+ )
70
+ resp.raise_for_status()
71
+
72
+ # Parse the response
73
+ result = resp.json()
74
+
75
+ if not result or "data" not in result:
76
+ raise RuntimeError("Invalid response from Imagen API")
77
+
78
+ # Process each image in the response
79
+ for item in result["data"]:
80
+ if response_format == "url":
81
+ if "url" in item and item["url"]:
82
+ result_data.append(ImageData(url=item["url"]))
83
+ else:
84
+ raise RuntimeError("No URL found in API response")
85
+
86
+ elif response_format == "b64_json":
87
+ if "url" in item and item["url"]:
88
+ # Download the image and convert to base64
89
+ img_resp = self._client.session.request(
90
+ "get",
91
+ item["url"],
92
+ timeout=timeout,
93
+ )
94
+ img_resp.raise_for_status()
95
+ img_bytes = img_resp.content
96
+ b64_string = base64.b64encode(img_bytes).decode("utf-8")
97
+ result_data.append(ImageData(b64_json=b64_string))
98
+ elif "b64_json" in item and item["b64_json"]:
99
+ result_data.append(ImageData(b64_json=item["b64_json"]))
100
+ else:
101
+ raise RuntimeError("No image data found in API response")
102
+
103
+ else:
104
+ raise ValueError("response_format must be 'url' or 'b64_json'")
105
+
106
+ except requests.exceptions.RequestException as e:
107
+ raise RuntimeError(f"Failed to generate image with Imagen API: {e}")
108
+ except Exception as e:
109
+ raise RuntimeError(f"Error processing Imagen API response: {e}")
110
+
111
+ return ImageResponse(created=int(time.time()), data=result_data)
112
+
113
+
114
+ class ImagenAI(TTICompatibleProvider):
115
+ """
116
+ Imagen API provider for text-to-image generation.
117
+
118
+ This provider interfaces with the Imagen API at imagen.exomlapi.com
119
+ to generate images from text prompts.
120
+ """
121
+
122
+ AVAILABLE_MODELS = ["imagen_3_5", "imagen_3"]
123
+
124
+ def __init__(self, api_key: Optional[str] = None):
125
+ """
126
+ Initialize the Imagen API client.
127
+
128
+ Args:
129
+ api_key: Optional API key for authentication (if required)
130
+ """
131
+ self.api_endpoint = "https://imagen.exomlapi.com/v1/images/generations"
132
+ self.session = requests.Session()
133
+ self.user_agent = LitAgent().random()
134
+ self.api_key = api_key
135
+
136
+ # Set up headers based on the provided request details
137
+ self.headers = {
138
+ "accept": "*/*",
139
+ "accept-encoding": "gzip, deflate, br, zstd",
140
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8",
141
+ "content-type": "application/json",
142
+ "dnt": "1",
143
+ "origin": "https://imagen.exomlapi.com",
144
+ "referer": "https://imagen.exomlapi.com/",
145
+ "sec-ch-ua": '"Chromium";v="136", "Microsoft Edge";v="136", "Not.A/Brand";v="99"',
146
+ "sec-ch-ua-mobile": "?0",
147
+ "sec-ch-ua-platform": '"Windows"',
148
+ "sec-fetch-dest": "empty",
149
+ "sec-fetch-mode": "cors",
150
+ "sec-fetch-site": "same-origin",
151
+ "sec-gpc": "1",
152
+ "user-agent": self.user_agent,
153
+ }
154
+
155
+ # Add API key to headers if provided
156
+ if self.api_key:
157
+ self.headers["authorization"] = f"Bearer {self.api_key}"
158
+
159
+ self.session.headers.update(self.headers)
160
+ self.images = Images(self)
161
+
162
+ @property
163
+ def models(self):
164
+ """
165
+ Get available models for the Imagen API.
166
+
167
+ Returns:
168
+ Object with list() method that returns available models
169
+ """
170
+
171
+ class _ModelList:
172
+ def list(inner_self):
173
+ return type(self).AVAILABLE_MODELS
174
+
175
+ return _ModelList()
176
+
177
+
178
+ if __name__ == "__main__":
179
+ from rich import print
180
+
181
+ # Example usage
182
+ client = ImagenAI()
183
+
184
+ try:
185
+ response = client.images.create(
186
+ model="imagen_3_5",
187
+ prompt="red car",
188
+ response_format="url",
189
+ n=1,
190
+ size="1024x1024",
191
+ timeout=30,
192
+ )
193
+ print("Generated image successfully:")
194
+ print(response)
195
+ except Exception as e:
196
+ print(f"Error: {e}")