webscout 8.3.7__py3-none-any.whl → 2025.10.13__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 +250 -250
- webscout/AIbase.py +379 -379
- webscout/AIutel.py +60 -60
- webscout/Bard.py +1012 -1012
- webscout/Bing_search.py +417 -417
- webscout/DWEBS.py +529 -529
- webscout/Extra/Act.md +309 -309
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/README.md +110 -110
- webscout/Extra/GitToolkit/gitapi/__init__.py +11 -11
- webscout/Extra/GitToolkit/gitapi/repository.py +195 -195
- webscout/Extra/GitToolkit/gitapi/user.py +96 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +61 -61
- webscout/Extra/YTToolkit/README.md +375 -375
- webscout/Extra/YTToolkit/YTdownloader.py +956 -956
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +475 -475
- webscout/Extra/YTToolkit/ytapi/README.md +44 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +6 -6
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +118 -118
- webscout/Extra/YTToolkit/ytapi/https.py +88 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/query.py +39 -39
- webscout/Extra/YTToolkit/ytapi/stream.py +62 -62
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +232 -232
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder.py +1105 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +332 -332
- webscout/Extra/gguf.md +429 -429
- webscout/Extra/gguf.py +1213 -1213
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +27 -27
- webscout/Extra/tempmail/async_utils.py +140 -140
- webscout/Extra/tempmail/base.py +160 -160
- webscout/Extra/tempmail/cli.py +186 -186
- webscout/Extra/tempmail/emailnator.py +84 -84
- webscout/Extra/tempmail/mail_tm.py +360 -360
- webscout/Extra/tempmail/temp_mail_io.py +291 -291
- webscout/Extra/weather.md +281 -281
- webscout/Extra/weather.py +193 -193
- webscout/Litlogger/README.md +10 -10
- webscout/Litlogger/__init__.py +15 -15
- webscout/Litlogger/formats.py +13 -13
- webscout/Litlogger/handlers.py +121 -121
- webscout/Litlogger/levels.py +13 -13
- webscout/Litlogger/logger.py +134 -134
- webscout/Provider/AISEARCH/Perplexity.py +332 -332
- webscout/Provider/AISEARCH/README.md +279 -279
- webscout/Provider/AISEARCH/__init__.py +16 -1
- webscout/Provider/AISEARCH/felo_search.py +206 -206
- webscout/Provider/AISEARCH/genspark_search.py +323 -323
- webscout/Provider/AISEARCH/hika_search.py +185 -185
- webscout/Provider/AISEARCH/iask_search.py +410 -410
- webscout/Provider/AISEARCH/monica_search.py +219 -219
- webscout/Provider/AISEARCH/scira_search.py +316 -316
- webscout/Provider/AISEARCH/stellar_search.py +177 -177
- webscout/Provider/AISEARCH/webpilotai_search.py +255 -255
- webscout/Provider/Aitopia.py +314 -314
- webscout/Provider/Andi.py +1 -1
- webscout/Provider/Apriel.py +306 -0
- webscout/Provider/ChatGPTClone.py +237 -236
- webscout/Provider/ChatSandbox.py +343 -343
- webscout/Provider/Cloudflare.py +324 -324
- webscout/Provider/Cohere.py +208 -208
- webscout/Provider/Deepinfra.py +370 -366
- webscout/Provider/ExaAI.py +260 -260
- webscout/Provider/ExaChat.py +308 -308
- webscout/Provider/Flowith.py +221 -221
- webscout/Provider/GMI.py +293 -0
- webscout/Provider/Gemini.py +164 -164
- webscout/Provider/GeminiProxy.py +167 -167
- webscout/Provider/GithubChat.py +371 -372
- webscout/Provider/Groq.py +800 -800
- webscout/Provider/HeckAI.py +383 -383
- webscout/Provider/Jadve.py +282 -282
- webscout/Provider/K2Think.py +307 -307
- webscout/Provider/Koboldai.py +205 -205
- webscout/Provider/LambdaChat.py +423 -423
- webscout/Provider/Nemotron.py +244 -244
- webscout/Provider/Netwrck.py +248 -248
- webscout/Provider/OLLAMA.py +395 -395
- webscout/Provider/OPENAI/Cloudflare.py +393 -393
- webscout/Provider/OPENAI/FalconH1.py +451 -451
- webscout/Provider/OPENAI/FreeGemini.py +296 -296
- webscout/Provider/OPENAI/K2Think.py +431 -431
- webscout/Provider/OPENAI/NEMOTRON.py +240 -240
- webscout/Provider/OPENAI/PI.py +427 -427
- webscout/Provider/OPENAI/README.md +959 -959
- webscout/Provider/OPENAI/TogetherAI.py +345 -345
- webscout/Provider/OPENAI/TwoAI.py +465 -465
- webscout/Provider/OPENAI/__init__.py +33 -18
- webscout/Provider/OPENAI/base.py +248 -248
- webscout/Provider/OPENAI/chatglm.py +528 -0
- webscout/Provider/OPENAI/chatgpt.py +592 -592
- webscout/Provider/OPENAI/chatgptclone.py +521 -521
- webscout/Provider/OPENAI/chatsandbox.py +202 -202
- webscout/Provider/OPENAI/deepinfra.py +318 -314
- webscout/Provider/OPENAI/e2b.py +1665 -1665
- webscout/Provider/OPENAI/exaai.py +420 -420
- webscout/Provider/OPENAI/exachat.py +452 -452
- webscout/Provider/OPENAI/friendli.py +232 -232
- webscout/Provider/OPENAI/{refact.py → gmi.py} +324 -274
- webscout/Provider/OPENAI/groq.py +364 -364
- webscout/Provider/OPENAI/heckai.py +314 -314
- webscout/Provider/OPENAI/llmchatco.py +337 -337
- webscout/Provider/OPENAI/netwrck.py +355 -355
- webscout/Provider/OPENAI/oivscode.py +290 -290
- webscout/Provider/OPENAI/opkfc.py +518 -518
- webscout/Provider/OPENAI/pydantic_imports.py +1 -1
- webscout/Provider/OPENAI/scirachat.py +535 -535
- webscout/Provider/OPENAI/sonus.py +308 -308
- webscout/Provider/OPENAI/standardinput.py +442 -442
- webscout/Provider/OPENAI/textpollinations.py +340 -340
- webscout/Provider/OPENAI/toolbaz.py +419 -416
- webscout/Provider/OPENAI/typefully.py +362 -362
- webscout/Provider/OPENAI/utils.py +295 -295
- webscout/Provider/OPENAI/venice.py +436 -436
- webscout/Provider/OPENAI/wisecat.py +387 -387
- webscout/Provider/OPENAI/writecream.py +166 -166
- webscout/Provider/OPENAI/x0gpt.py +378 -378
- webscout/Provider/OPENAI/yep.py +389 -389
- webscout/Provider/OpenGPT.py +230 -230
- webscout/Provider/Openai.py +243 -243
- webscout/Provider/PI.py +405 -405
- webscout/Provider/Perplexitylabs.py +430 -430
- webscout/Provider/QwenLM.py +272 -272
- webscout/Provider/STT/__init__.py +16 -1
- webscout/Provider/Sambanova.py +257 -257
- webscout/Provider/StandardInput.py +309 -309
- webscout/Provider/TTI/README.md +82 -82
- webscout/Provider/TTI/__init__.py +33 -18
- webscout/Provider/TTI/aiarta.py +413 -413
- webscout/Provider/TTI/base.py +136 -136
- webscout/Provider/TTI/bing.py +243 -243
- webscout/Provider/TTI/gpt1image.py +149 -149
- webscout/Provider/TTI/imagen.py +196 -196
- webscout/Provider/TTI/infip.py +211 -211
- webscout/Provider/TTI/magicstudio.py +232 -232
- webscout/Provider/TTI/monochat.py +219 -219
- webscout/Provider/TTI/piclumen.py +214 -214
- webscout/Provider/TTI/pixelmuse.py +232 -232
- webscout/Provider/TTI/pollinations.py +232 -232
- webscout/Provider/TTI/together.py +288 -288
- webscout/Provider/TTI/utils.py +12 -12
- webscout/Provider/TTI/venice.py +367 -367
- webscout/Provider/TTS/README.md +192 -192
- webscout/Provider/TTS/__init__.py +33 -18
- webscout/Provider/TTS/parler.py +110 -110
- webscout/Provider/TTS/streamElements.py +333 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TeachAnything.py +237 -237
- webscout/Provider/TextPollinationsAI.py +310 -310
- webscout/Provider/TogetherAI.py +356 -356
- webscout/Provider/TwoAI.py +312 -312
- webscout/Provider/TypliAI.py +311 -311
- webscout/Provider/UNFINISHED/ChatHub.py +208 -208
- webscout/Provider/UNFINISHED/ChutesAI.py +313 -313
- webscout/Provider/UNFINISHED/GizAI.py +294 -294
- webscout/Provider/UNFINISHED/Marcus.py +198 -198
- webscout/Provider/UNFINISHED/Qodo.py +477 -477
- webscout/Provider/UNFINISHED/VercelAIGateway.py +338 -338
- webscout/Provider/UNFINISHED/XenAI.py +324 -324
- webscout/Provider/UNFINISHED/Youchat.py +330 -330
- webscout/Provider/UNFINISHED/liner.py +334 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +262 -262
- webscout/Provider/UNFINISHED/puterjs.py +634 -634
- webscout/Provider/UNFINISHED/samurai.py +223 -223
- webscout/Provider/UNFINISHED/test_lmarena.py +119 -119
- webscout/Provider/Venice.py +250 -250
- webscout/Provider/VercelAI.py +256 -256
- webscout/Provider/WiseCat.py +231 -231
- webscout/Provider/WrDoChat.py +366 -366
- webscout/Provider/__init__.py +33 -18
- webscout/Provider/ai4chat.py +174 -174
- webscout/Provider/akashgpt.py +331 -331
- webscout/Provider/cerebras.py +446 -446
- webscout/Provider/chatglm.py +394 -301
- webscout/Provider/cleeai.py +211 -211
- webscout/Provider/elmo.py +282 -282
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +261 -261
- webscout/Provider/hermes.py +263 -263
- webscout/Provider/julius.py +223 -223
- webscout/Provider/learnfastai.py +309 -309
- webscout/Provider/llama3mitril.py +214 -214
- webscout/Provider/llmchat.py +243 -243
- webscout/Provider/llmchatco.py +290 -290
- webscout/Provider/meta.py +801 -801
- webscout/Provider/oivscode.py +309 -309
- webscout/Provider/scira_chat.py +383 -383
- webscout/Provider/searchchat.py +292 -292
- webscout/Provider/sonus.py +258 -258
- webscout/Provider/toolbaz.py +370 -367
- webscout/Provider/turboseek.py +273 -273
- webscout/Provider/typefully.py +207 -207
- webscout/Provider/yep.py +372 -372
- webscout/__init__.py +27 -31
- webscout/__main__.py +5 -5
- webscout/auth/api_key_manager.py +189 -189
- webscout/auth/config.py +175 -175
- webscout/auth/models.py +185 -185
- webscout/auth/routes.py +663 -664
- webscout/auth/simple_logger.py +236 -236
- webscout/cli.py +523 -523
- webscout/conversation.py +438 -438
- webscout/exceptions.py +361 -361
- webscout/litagent/Readme.md +298 -298
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +581 -581
- webscout/litagent/constants.py +59 -59
- webscout/litprinter/__init__.py +58 -58
- webscout/models.py +181 -181
- webscout/optimizers.py +419 -419
- webscout/prompt_manager.py +288 -288
- webscout/sanitize.py +1078 -1078
- webscout/scout/README.md +401 -401
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +6 -6
- webscout/scout/core/crawler.py +297 -297
- webscout/scout/core/scout.py +706 -706
- webscout/scout/core/search_result.py +95 -95
- webscout/scout/core/text_analyzer.py +62 -62
- webscout/scout/core/text_utils.py +277 -277
- webscout/scout/core/web_analyzer.py +51 -51
- webscout/scout/element.py +599 -599
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +37 -37
- webscout/search/__init__.py +51 -0
- webscout/search/base.py +195 -0
- webscout/search/duckduckgo_main.py +54 -0
- webscout/search/engines/__init__.py +48 -0
- webscout/search/engines/bing.py +84 -0
- webscout/search/engines/bing_news.py +52 -0
- webscout/search/engines/brave.py +43 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +78 -0
- webscout/search/engines/duckduckgo/base.py +187 -0
- webscout/search/engines/duckduckgo/images.py +97 -0
- webscout/search/engines/duckduckgo/maps.py +168 -0
- webscout/search/engines/duckduckgo/news.py +68 -0
- webscout/search/engines/duckduckgo/suggestions.py +21 -0
- webscout/search/engines/duckduckgo/text.py +211 -0
- webscout/search/engines/duckduckgo/translate.py +47 -0
- webscout/search/engines/duckduckgo/videos.py +63 -0
- webscout/search/engines/duckduckgo/weather.py +74 -0
- webscout/search/engines/mojeek.py +37 -0
- webscout/search/engines/wikipedia.py +56 -0
- webscout/search/engines/yahoo.py +65 -0
- webscout/search/engines/yahoo_news.py +64 -0
- webscout/search/engines/yandex.py +43 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +32 -0
- webscout/search/engines/yep/images.py +99 -0
- webscout/search/engines/yep/suggestions.py +35 -0
- webscout/search/engines/yep/text.py +114 -0
- webscout/search/http_client.py +156 -0
- webscout/search/results.py +137 -0
- webscout/search/yep_main.py +44 -0
- webscout/swiftcli/Readme.md +323 -323
- webscout/swiftcli/__init__.py +95 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +308 -308
- webscout/swiftcli/core/context.py +104 -104
- webscout/swiftcli/core/group.py +241 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +221 -221
- webscout/swiftcli/decorators/options.py +220 -220
- webscout/swiftcli/decorators/output.py +302 -302
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +135 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +59 -59
- webscout/swiftcli/utils/formatting.py +252 -252
- webscout/swiftcli/utils/parsing.py +267 -267
- webscout/update_checker.py +117 -117
- webscout/version.py +1 -1
- webscout/version.py.bak +2 -0
- webscout/zeroart/README.md +89 -89
- webscout/zeroart/__init__.py +134 -134
- webscout/zeroart/base.py +66 -66
- webscout/zeroart/effects.py +100 -100
- webscout/zeroart/fonts.py +1238 -1238
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/METADATA +936 -937
- webscout-2025.10.13.dist-info/RECORD +329 -0
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/OPENAI/Qwen3.py +0 -303
- webscout/Provider/OPENAI/qodo.py +0 -630
- webscout/Provider/OPENAI/xenai.py +0 -514
- webscout/tempid.py +0 -134
- webscout/webscout_search.py +0 -1183
- webscout/webscout_search_async.py +0 -649
- webscout/yep_search.py +0 -346
- webscout-8.3.7.dist-info/RECORD +0 -301
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/WHEEL +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/entry_points.txt +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.7.dist-info → webscout-2025.10.13.dist-info}/top_level.txt +0 -0
|
@@ -1,220 +1,220 @@
|
|
|
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
|
-
from requests.adapters import HTTPAdapter
|
|
11
|
-
from urllib3.util.retry import Retry
|
|
12
|
-
import os
|
|
13
|
-
import tempfile
|
|
14
|
-
import time
|
|
15
|
-
import json
|
|
16
|
-
from io import BytesIO
|
|
17
|
-
|
|
18
|
-
try:
|
|
19
|
-
from PIL import Image
|
|
20
|
-
except ImportError:
|
|
21
|
-
Image = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class Images(BaseImages):
|
|
25
|
-
def __init__(self, client):
|
|
26
|
-
self._client = client
|
|
27
|
-
|
|
28
|
-
def create(
|
|
29
|
-
self,
|
|
30
|
-
*,
|
|
31
|
-
model: str,
|
|
32
|
-
prompt: str,
|
|
33
|
-
n: int = 1,
|
|
34
|
-
size: str = "1024x1024",
|
|
35
|
-
response_format: str = "b64_json",
|
|
36
|
-
user: Optional[str] = None,
|
|
37
|
-
style: str = None,
|
|
38
|
-
aspect_ratio: str = None,
|
|
39
|
-
timeout: int = 60,
|
|
40
|
-
image_format: str = "png",
|
|
41
|
-
**kwargs,
|
|
42
|
-
) -> ImageResponse:
|
|
43
|
-
if not prompt:
|
|
44
|
-
raise ValueError(
|
|
45
|
-
"Describe the image you want to create (use the 'prompt' property)."
|
|
46
|
-
)
|
|
47
|
-
# Only one image is supported by MonoChat API, but keep n for compatibility
|
|
48
|
-
body = {
|
|
49
|
-
"prompt": prompt,
|
|
50
|
-
"model": model
|
|
51
|
-
}
|
|
52
|
-
session = self._client.session
|
|
53
|
-
headers = self._client.headers
|
|
54
|
-
images = []
|
|
55
|
-
urls = []
|
|
56
|
-
|
|
57
|
-
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
58
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
59
|
-
for attempt in range(max_retries):
|
|
60
|
-
tmp_path = None
|
|
61
|
-
try:
|
|
62
|
-
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
63
|
-
tmp.write(img_bytes)
|
|
64
|
-
tmp.flush()
|
|
65
|
-
tmp_path = tmp.name
|
|
66
|
-
with open(tmp_path, "rb") as f:
|
|
67
|
-
files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
|
|
68
|
-
data = {"reqtype": "fileupload", "json": "true"}
|
|
69
|
-
headers = {"User-Agent": LitAgent().random()}
|
|
70
|
-
if attempt > 0:
|
|
71
|
-
headers["Connection"] = "close"
|
|
72
|
-
resp = requests.post(
|
|
73
|
-
"https://catbox.moe/user/api.php",
|
|
74
|
-
files=files,
|
|
75
|
-
data=data,
|
|
76
|
-
headers=headers,
|
|
77
|
-
timeout=timeout,
|
|
78
|
-
)
|
|
79
|
-
if resp.status_code == 200 and resp.text.strip():
|
|
80
|
-
text = resp.text.strip()
|
|
81
|
-
if text.startswith("http"):
|
|
82
|
-
return text
|
|
83
|
-
try:
|
|
84
|
-
result = resp.json()
|
|
85
|
-
if "url" in result:
|
|
86
|
-
return result["url"]
|
|
87
|
-
except json.JSONDecodeError:
|
|
88
|
-
if "http" in text:
|
|
89
|
-
return text
|
|
90
|
-
except Exception:
|
|
91
|
-
if attempt < max_retries - 1:
|
|
92
|
-
time.sleep(1 * (attempt + 1))
|
|
93
|
-
finally:
|
|
94
|
-
if tmp_path and os.path.isfile(tmp_path):
|
|
95
|
-
try:
|
|
96
|
-
os.remove(tmp_path)
|
|
97
|
-
except Exception:
|
|
98
|
-
pass
|
|
99
|
-
return None
|
|
100
|
-
|
|
101
|
-
def upload_file_alternative(img_bytes, image_format):
|
|
102
|
-
try:
|
|
103
|
-
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
104
|
-
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
105
|
-
tmp.write(img_bytes)
|
|
106
|
-
tmp.flush()
|
|
107
|
-
tmp_path = tmp.name
|
|
108
|
-
try:
|
|
109
|
-
if not os.path.isfile(tmp_path):
|
|
110
|
-
return None
|
|
111
|
-
with open(tmp_path, "rb") as img_file:
|
|
112
|
-
files = {"file": img_file}
|
|
113
|
-
response = requests.post("https://0x0.st", files=files)
|
|
114
|
-
response.raise_for_status()
|
|
115
|
-
image_url = response.text.strip()
|
|
116
|
-
if not image_url.startswith("http"):
|
|
117
|
-
return None
|
|
118
|
-
return image_url
|
|
119
|
-
except Exception:
|
|
120
|
-
return None
|
|
121
|
-
finally:
|
|
122
|
-
try:
|
|
123
|
-
os.remove(tmp_path)
|
|
124
|
-
except Exception:
|
|
125
|
-
pass
|
|
126
|
-
except Exception:
|
|
127
|
-
return None
|
|
128
|
-
|
|
129
|
-
try:
|
|
130
|
-
resp = session.post(
|
|
131
|
-
f"{self._client.api_endpoint}/image",
|
|
132
|
-
json=body,
|
|
133
|
-
headers=headers,
|
|
134
|
-
timeout=timeout,
|
|
135
|
-
)
|
|
136
|
-
resp.raise_for_status()
|
|
137
|
-
data = resp.json()
|
|
138
|
-
if not data.get("image"):
|
|
139
|
-
raise RuntimeError("Failed to process image. No image data found.")
|
|
140
|
-
# Always decode the base64 image
|
|
141
|
-
image_bytes = base64.b64decode(data.get("image"))
|
|
142
|
-
if response_format == "b64_json":
|
|
143
|
-
result_data = [ImageData(b64_json=data.get("image"))]
|
|
144
|
-
elif response_format == "url":
|
|
145
|
-
if Image is None:
|
|
146
|
-
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
147
|
-
# Convert to png or jpeg in memory
|
|
148
|
-
with BytesIO(image_bytes) as input_io:
|
|
149
|
-
with Image.open(input_io) as im:
|
|
150
|
-
out_io = BytesIO()
|
|
151
|
-
if image_format.lower() == "jpeg":
|
|
152
|
-
im = im.convert("RGB")
|
|
153
|
-
im.save(out_io, format="JPEG")
|
|
154
|
-
else:
|
|
155
|
-
im.save(out_io, format="PNG")
|
|
156
|
-
img_bytes = out_io.getvalue()
|
|
157
|
-
# Try primary upload method with retries
|
|
158
|
-
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
159
|
-
# If primary method fails, try alternative
|
|
160
|
-
if not uploaded_url:
|
|
161
|
-
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
162
|
-
if uploaded_url:
|
|
163
|
-
result_data = [ImageData(url=uploaded_url)]
|
|
164
|
-
else:
|
|
165
|
-
raise RuntimeError("Failed to upload image to catbox.moe using all available methods")
|
|
166
|
-
else:
|
|
167
|
-
raise ValueError("response_format must be 'url' or 'b64_json'")
|
|
168
|
-
from time import time as _time
|
|
169
|
-
return ImageResponse(created=int(_time()), data=result_data)
|
|
170
|
-
except Exception as e:
|
|
171
|
-
raise RuntimeError(f"An error occurred: {str(e)}")
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
class MonoChatAI(TTICompatibleProvider):
|
|
175
|
-
AVAILABLE_MODELS = ["nextlm-image-1", "gpt-image-1", "dall-e-3", "dall-e-2"]
|
|
176
|
-
|
|
177
|
-
def __init__(self):
|
|
178
|
-
self.api_endpoint = "https://gg.is-a-furry.dev/api"
|
|
179
|
-
self.session = requests.Session()
|
|
180
|
-
self._setup_session_with_retries()
|
|
181
|
-
self.user_agent = LitAgent().random()
|
|
182
|
-
self.headers = {
|
|
183
|
-
"accept": "*/*",
|
|
184
|
-
"content-type": "application/json",
|
|
185
|
-
"origin": "https://gg.is-a-furry.dev",
|
|
186
|
-
"referer": "https://gg.is-a-furry.dev/",
|
|
187
|
-
"user-agent": self.user_agent,
|
|
188
|
-
}
|
|
189
|
-
self.session.headers.update(self.headers)
|
|
190
|
-
self.images = Images(self)
|
|
191
|
-
|
|
192
|
-
def _setup_session_with_retries(self):
|
|
193
|
-
retry_strategy = Retry(
|
|
194
|
-
total=3,
|
|
195
|
-
status_forcelist=[429, 500, 502, 503, 504],
|
|
196
|
-
backoff_factor=1,
|
|
197
|
-
allowed_methods=["HEAD", "GET", "OPTIONS", "POST"],
|
|
198
|
-
)
|
|
199
|
-
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
200
|
-
self.session.mount("http://", adapter)
|
|
201
|
-
self.session.mount("https://", adapter)
|
|
202
|
-
|
|
203
|
-
@property
|
|
204
|
-
def models(self):
|
|
205
|
-
class _ModelList:
|
|
206
|
-
def list(inner_self):
|
|
207
|
-
return type(self).AVAILABLE_MODELS
|
|
208
|
-
return _ModelList()
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if __name__ == "__main__":
|
|
212
|
-
from rich import print
|
|
213
|
-
client = MonoChatAI()
|
|
214
|
-
response = client.images.create(
|
|
215
|
-
model="dall-e-3",
|
|
216
|
-
prompt="A red car on a sunny day",
|
|
217
|
-
response_format="url",
|
|
218
|
-
timeout=60000,
|
|
219
|
-
)
|
|
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
|
+
from requests.adapters import HTTPAdapter
|
|
11
|
+
from urllib3.util.retry import Retry
|
|
12
|
+
import os
|
|
13
|
+
import tempfile
|
|
14
|
+
import time
|
|
15
|
+
import json
|
|
16
|
+
from io import BytesIO
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from PIL import Image
|
|
20
|
+
except ImportError:
|
|
21
|
+
Image = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Images(BaseImages):
|
|
25
|
+
def __init__(self, client):
|
|
26
|
+
self._client = client
|
|
27
|
+
|
|
28
|
+
def create(
|
|
29
|
+
self,
|
|
30
|
+
*,
|
|
31
|
+
model: str,
|
|
32
|
+
prompt: str,
|
|
33
|
+
n: int = 1,
|
|
34
|
+
size: str = "1024x1024",
|
|
35
|
+
response_format: str = "b64_json",
|
|
36
|
+
user: Optional[str] = None,
|
|
37
|
+
style: str = None,
|
|
38
|
+
aspect_ratio: str = None,
|
|
39
|
+
timeout: int = 60,
|
|
40
|
+
image_format: str = "png",
|
|
41
|
+
**kwargs,
|
|
42
|
+
) -> ImageResponse:
|
|
43
|
+
if not prompt:
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"Describe the image you want to create (use the 'prompt' property)."
|
|
46
|
+
)
|
|
47
|
+
# Only one image is supported by MonoChat API, but keep n for compatibility
|
|
48
|
+
body = {
|
|
49
|
+
"prompt": prompt,
|
|
50
|
+
"model": model
|
|
51
|
+
}
|
|
52
|
+
session = self._client.session
|
|
53
|
+
headers = self._client.headers
|
|
54
|
+
images = []
|
|
55
|
+
urls = []
|
|
56
|
+
|
|
57
|
+
def upload_file_with_retry(img_bytes, image_format, max_retries=3):
|
|
58
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
59
|
+
for attempt in range(max_retries):
|
|
60
|
+
tmp_path = None
|
|
61
|
+
try:
|
|
62
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
63
|
+
tmp.write(img_bytes)
|
|
64
|
+
tmp.flush()
|
|
65
|
+
tmp_path = tmp.name
|
|
66
|
+
with open(tmp_path, "rb") as f:
|
|
67
|
+
files = {"fileToUpload": (f"image.{ext}", f, f"image/{ext}")}
|
|
68
|
+
data = {"reqtype": "fileupload", "json": "true"}
|
|
69
|
+
headers = {"User-Agent": LitAgent().random()}
|
|
70
|
+
if attempt > 0:
|
|
71
|
+
headers["Connection"] = "close"
|
|
72
|
+
resp = requests.post(
|
|
73
|
+
"https://catbox.moe/user/api.php",
|
|
74
|
+
files=files,
|
|
75
|
+
data=data,
|
|
76
|
+
headers=headers,
|
|
77
|
+
timeout=timeout,
|
|
78
|
+
)
|
|
79
|
+
if resp.status_code == 200 and resp.text.strip():
|
|
80
|
+
text = resp.text.strip()
|
|
81
|
+
if text.startswith("http"):
|
|
82
|
+
return text
|
|
83
|
+
try:
|
|
84
|
+
result = resp.json()
|
|
85
|
+
if "url" in result:
|
|
86
|
+
return result["url"]
|
|
87
|
+
except json.JSONDecodeError:
|
|
88
|
+
if "http" in text:
|
|
89
|
+
return text
|
|
90
|
+
except Exception:
|
|
91
|
+
if attempt < max_retries - 1:
|
|
92
|
+
time.sleep(1 * (attempt + 1))
|
|
93
|
+
finally:
|
|
94
|
+
if tmp_path and os.path.isfile(tmp_path):
|
|
95
|
+
try:
|
|
96
|
+
os.remove(tmp_path)
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
def upload_file_alternative(img_bytes, image_format):
|
|
102
|
+
try:
|
|
103
|
+
ext = "jpg" if image_format.lower() == "jpeg" else "png"
|
|
104
|
+
with tempfile.NamedTemporaryFile(suffix=f".{ext}", delete=False) as tmp:
|
|
105
|
+
tmp.write(img_bytes)
|
|
106
|
+
tmp.flush()
|
|
107
|
+
tmp_path = tmp.name
|
|
108
|
+
try:
|
|
109
|
+
if not os.path.isfile(tmp_path):
|
|
110
|
+
return None
|
|
111
|
+
with open(tmp_path, "rb") as img_file:
|
|
112
|
+
files = {"file": img_file}
|
|
113
|
+
response = requests.post("https://0x0.st", files=files)
|
|
114
|
+
response.raise_for_status()
|
|
115
|
+
image_url = response.text.strip()
|
|
116
|
+
if not image_url.startswith("http"):
|
|
117
|
+
return None
|
|
118
|
+
return image_url
|
|
119
|
+
except Exception:
|
|
120
|
+
return None
|
|
121
|
+
finally:
|
|
122
|
+
try:
|
|
123
|
+
os.remove(tmp_path)
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
except Exception:
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
resp = session.post(
|
|
131
|
+
f"{self._client.api_endpoint}/image",
|
|
132
|
+
json=body,
|
|
133
|
+
headers=headers,
|
|
134
|
+
timeout=timeout,
|
|
135
|
+
)
|
|
136
|
+
resp.raise_for_status()
|
|
137
|
+
data = resp.json()
|
|
138
|
+
if not data.get("image"):
|
|
139
|
+
raise RuntimeError("Failed to process image. No image data found.")
|
|
140
|
+
# Always decode the base64 image
|
|
141
|
+
image_bytes = base64.b64decode(data.get("image"))
|
|
142
|
+
if response_format == "b64_json":
|
|
143
|
+
result_data = [ImageData(b64_json=data.get("image"))]
|
|
144
|
+
elif response_format == "url":
|
|
145
|
+
if Image is None:
|
|
146
|
+
raise ImportError("Pillow (PIL) is required for image format conversion.")
|
|
147
|
+
# Convert to png or jpeg in memory
|
|
148
|
+
with BytesIO(image_bytes) as input_io:
|
|
149
|
+
with Image.open(input_io) as im:
|
|
150
|
+
out_io = BytesIO()
|
|
151
|
+
if image_format.lower() == "jpeg":
|
|
152
|
+
im = im.convert("RGB")
|
|
153
|
+
im.save(out_io, format="JPEG")
|
|
154
|
+
else:
|
|
155
|
+
im.save(out_io, format="PNG")
|
|
156
|
+
img_bytes = out_io.getvalue()
|
|
157
|
+
# Try primary upload method with retries
|
|
158
|
+
uploaded_url = upload_file_with_retry(img_bytes, image_format)
|
|
159
|
+
# If primary method fails, try alternative
|
|
160
|
+
if not uploaded_url:
|
|
161
|
+
uploaded_url = upload_file_alternative(img_bytes, image_format)
|
|
162
|
+
if uploaded_url:
|
|
163
|
+
result_data = [ImageData(url=uploaded_url)]
|
|
164
|
+
else:
|
|
165
|
+
raise RuntimeError("Failed to upload image to catbox.moe using all available methods")
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError("response_format must be 'url' or 'b64_json'")
|
|
168
|
+
from time import time as _time
|
|
169
|
+
return ImageResponse(created=int(_time()), data=result_data)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
raise RuntimeError(f"An error occurred: {str(e)}")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class MonoChatAI(TTICompatibleProvider):
|
|
175
|
+
AVAILABLE_MODELS = ["nextlm-image-1", "gpt-image-1", "dall-e-3", "dall-e-2"]
|
|
176
|
+
|
|
177
|
+
def __init__(self):
|
|
178
|
+
self.api_endpoint = "https://gg.is-a-furry.dev/api"
|
|
179
|
+
self.session = requests.Session()
|
|
180
|
+
self._setup_session_with_retries()
|
|
181
|
+
self.user_agent = LitAgent().random()
|
|
182
|
+
self.headers = {
|
|
183
|
+
"accept": "*/*",
|
|
184
|
+
"content-type": "application/json",
|
|
185
|
+
"origin": "https://gg.is-a-furry.dev",
|
|
186
|
+
"referer": "https://gg.is-a-furry.dev/",
|
|
187
|
+
"user-agent": self.user_agent,
|
|
188
|
+
}
|
|
189
|
+
self.session.headers.update(self.headers)
|
|
190
|
+
self.images = Images(self)
|
|
191
|
+
|
|
192
|
+
def _setup_session_with_retries(self):
|
|
193
|
+
retry_strategy = Retry(
|
|
194
|
+
total=3,
|
|
195
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
196
|
+
backoff_factor=1,
|
|
197
|
+
allowed_methods=["HEAD", "GET", "OPTIONS", "POST"],
|
|
198
|
+
)
|
|
199
|
+
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
200
|
+
self.session.mount("http://", adapter)
|
|
201
|
+
self.session.mount("https://", adapter)
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def models(self):
|
|
205
|
+
class _ModelList:
|
|
206
|
+
def list(inner_self):
|
|
207
|
+
return type(self).AVAILABLE_MODELS
|
|
208
|
+
return _ModelList()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
from rich import print
|
|
213
|
+
client = MonoChatAI()
|
|
214
|
+
response = client.images.create(
|
|
215
|
+
model="dall-e-3",
|
|
216
|
+
prompt="A red car on a sunny day",
|
|
217
|
+
response_format="url",
|
|
218
|
+
timeout=60000,
|
|
219
|
+
)
|
|
220
220
|
print(response)
|