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.
- webscout/AIauto.py +4 -4
- webscout/AIbase.py +61 -1
- webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
- webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
- webscout/Extra/YTToolkit/ytapi/video.py +10 -10
- webscout/Extra/autocoder/autocoder_utiles.py +1 -1
- webscout/Litlogger/formats.py +9 -0
- webscout/Litlogger/handlers.py +18 -0
- webscout/Litlogger/logger.py +43 -1
- webscout/Provider/AISEARCH/scira_search.py +3 -2
- webscout/Provider/LambdaChat.py +7 -1
- webscout/Provider/OPENAI/BLACKBOXAI.py +1049 -1017
- webscout/Provider/OPENAI/Qwen3.py +303 -303
- webscout/Provider/OPENAI/README.md +3 -0
- webscout/Provider/OPENAI/TogetherAI.py +355 -0
- webscout/Provider/OPENAI/__init__.py +2 -1
- webscout/Provider/OPENAI/api.py +298 -13
- webscout/Provider/OPENAI/autoproxy.py +39 -0
- webscout/Provider/OPENAI/base.py +89 -12
- webscout/Provider/OPENAI/chatgpt.py +15 -2
- webscout/Provider/OPENAI/chatgptclone.py +14 -3
- webscout/Provider/OPENAI/deepinfra.py +339 -328
- webscout/Provider/OPENAI/e2b.py +295 -73
- webscout/Provider/OPENAI/opkfc.py +18 -6
- webscout/Provider/OPENAI/scirachat.py +3 -2
- webscout/Provider/OPENAI/toolbaz.py +0 -1
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +367 -367
- webscout/Provider/OPENAI/yep.py +383 -383
- webscout/Provider/STT/__init__.py +3 -0
- webscout/Provider/STT/base.py +281 -0
- webscout/Provider/STT/elevenlabs.py +265 -0
- webscout/Provider/TTI/__init__.py +3 -1
- webscout/Provider/TTI/aiarta.py +399 -365
- webscout/Provider/TTI/base.py +74 -2
- webscout/Provider/TTI/fastflux.py +63 -30
- webscout/Provider/TTI/gpt1image.py +149 -0
- webscout/Provider/TTI/imagen.py +196 -0
- webscout/Provider/TTI/magicstudio.py +60 -29
- webscout/Provider/TTI/piclumen.py +43 -32
- webscout/Provider/TTI/pixelmuse.py +232 -225
- webscout/Provider/TTI/pollinations.py +43 -32
- webscout/Provider/TTI/together.py +287 -0
- webscout/Provider/TTI/utils.py +2 -1
- webscout/Provider/TTS/README.md +1 -0
- webscout/Provider/TTS/__init__.py +2 -1
- webscout/Provider/TTS/freetts.py +140 -0
- webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
- webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
- webscout/Provider/__init__.py +3 -0
- webscout/Provider/scira_chat.py +3 -2
- webscout/Provider/toolbaz.py +0 -1
- webscout/litagent/Readme.md +12 -3
- webscout/litagent/agent.py +99 -62
- webscout/version.py +1 -1
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/METADATA +1 -1
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/RECORD +61 -51
- webscout/Provider/TTI/artbit.py +0 -0
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/WHEEL +0 -0
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.dist-info → webscout-8.3.1.dist-info}/top_level.txt +0 -0
webscout/Provider/TTI/base.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any, Optional
|
|
3
3
|
from .utils import ImageResponse
|
|
4
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
51
|
-
image_data = result[
|
|
52
|
-
base64_data = image_data.split(
|
|
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(
|
|
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,
|
|
89
|
+
with open(tmp_path, "rb") as f:
|
|
77
90
|
files = {
|
|
78
|
-
|
|
91
|
+
"fileToUpload": (
|
|
92
|
+
f"image.{ext}",
|
|
93
|
+
f,
|
|
94
|
+
f"image/{ext}",
|
|
95
|
+
)
|
|
79
96
|
}
|
|
80
|
-
data = {
|
|
81
|
-
|
|
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[
|
|
87
|
-
resp = requests.post(
|
|
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(
|
|
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
|
|
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(
|
|
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,
|
|
120
|
-
files = {
|
|
121
|
-
response = requests.post(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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}")
|