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,394 +1,394 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import time
|
|
3
|
-
import uuid
|
|
4
|
-
import re
|
|
5
|
-
from typing import List, Dict, Optional, Union, Generator, Any
|
|
6
|
-
|
|
7
|
-
from curl_cffi import CurlError
|
|
8
|
-
from curl_cffi.requests import Session
|
|
9
|
-
from uuid import uuid4
|
|
10
|
-
|
|
11
|
-
# Import base classes and utility structures
|
|
12
|
-
from webscout.Provider.OPENAI.base import OpenAICompatibleProvider, BaseChat, BaseCompletions
|
|
13
|
-
from webscout.Provider.OPENAI.utils import (
|
|
14
|
-
ChatCompletionChunk, ChatCompletion, Choice, ChoiceDelta,
|
|
15
|
-
ChatCompletionMessage, CompletionUsage, count_tokens
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
from webscout.AIutel import sanitize_stream
|
|
19
|
-
from webscout.litagent import LitAgent
|
|
20
|
-
|
|
21
|
-
class Completions(BaseCompletions):
|
|
22
|
-
def __init__(self, client: 'Cloudflare'):
|
|
23
|
-
self._client = client
|
|
24
|
-
|
|
25
|
-
def create(
|
|
26
|
-
self,
|
|
27
|
-
*,
|
|
28
|
-
model: str,
|
|
29
|
-
messages: List[Dict[str, str]],
|
|
30
|
-
max_tokens: Optional[int] = None,
|
|
31
|
-
stream: bool = False,
|
|
32
|
-
temperature: Optional[float] = None,
|
|
33
|
-
top_p: Optional[float] = None,
|
|
34
|
-
timeout: Optional[int] = None,
|
|
35
|
-
proxies: Optional[dict] = None,
|
|
36
|
-
**kwargs: Any
|
|
37
|
-
) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
|
|
38
|
-
"""
|
|
39
|
-
Create a chat completion with Cloudflare API.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
model: The model to use (from AVAILABLE_MODELS)
|
|
43
|
-
messages: List of message dictionaries with 'role' and 'content'
|
|
44
|
-
max_tokens: Maximum number of tokens to generate
|
|
45
|
-
stream: Whether to stream the response
|
|
46
|
-
temperature: Sampling temperature (0-1)
|
|
47
|
-
top_p: Nucleus sampling parameter (0-1)
|
|
48
|
-
**kwargs: Additional parameters to pass to the API
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
If stream=False, returns a ChatCompletion object
|
|
52
|
-
If stream=True, returns a Generator yielding ChatCompletionChunk objects
|
|
53
|
-
"""
|
|
54
|
-
# Prepare the payload
|
|
55
|
-
payload = {
|
|
56
|
-
"messages": messages,
|
|
57
|
-
"lora": None,
|
|
58
|
-
"model": model,
|
|
59
|
-
"max_tokens": max_tokens or 600,
|
|
60
|
-
"stream": True # Always use streaming API
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
# Generate request ID and timestamp
|
|
64
|
-
request_id = str(uuid.uuid4())
|
|
65
|
-
created_time = int(time.time())
|
|
66
|
-
|
|
67
|
-
# Use streaming implementation if requested
|
|
68
|
-
if stream:
|
|
69
|
-
return self._create_streaming(
|
|
70
|
-
request_id=request_id,
|
|
71
|
-
created_time=created_time,
|
|
72
|
-
model=model,
|
|
73
|
-
payload=payload,
|
|
74
|
-
timeout=timeout,
|
|
75
|
-
proxies=proxies
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
# Otherwise use non-streaming implementation
|
|
79
|
-
return self._create_non_streaming(
|
|
80
|
-
request_id=request_id,
|
|
81
|
-
created_time=created_time,
|
|
82
|
-
model=model,
|
|
83
|
-
payload=payload,
|
|
84
|
-
timeout=timeout,
|
|
85
|
-
proxies=proxies
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
def _create_streaming(
|
|
89
|
-
self,
|
|
90
|
-
*,
|
|
91
|
-
request_id: str,
|
|
92
|
-
created_time: int,
|
|
93
|
-
model: str,
|
|
94
|
-
payload: Dict[str, Any],
|
|
95
|
-
timeout: Optional[int] = None,
|
|
96
|
-
proxies: Optional[dict] = None
|
|
97
|
-
) -> Generator[ChatCompletionChunk, None, None]:
|
|
98
|
-
"""Implementation for streaming chat completions."""
|
|
99
|
-
original_proxies = self._client.session.proxies
|
|
100
|
-
if proxies is not None:
|
|
101
|
-
self._client.session.proxies = proxies
|
|
102
|
-
try:
|
|
103
|
-
response = self._client.session.post(
|
|
104
|
-
self._client.chat_endpoint,
|
|
105
|
-
headers=self._client.headers,
|
|
106
|
-
cookies=self._client.cookies,
|
|
107
|
-
data=json.dumps(payload),
|
|
108
|
-
stream=True,
|
|
109
|
-
timeout=timeout if timeout is not None else self._client.timeout,
|
|
110
|
-
impersonate="chrome120"
|
|
111
|
-
)
|
|
112
|
-
response.raise_for_status()
|
|
113
|
-
|
|
114
|
-
# Process the stream using sanitize_stream
|
|
115
|
-
# This handles the extraction of content from Cloudflare's response format
|
|
116
|
-
processed_stream = sanitize_stream(
|
|
117
|
-
data=response.iter_content(chunk_size=None),
|
|
118
|
-
intro_value=None,
|
|
119
|
-
to_json=False,
|
|
120
|
-
skip_markers=None,
|
|
121
|
-
content_extractor=self._cloudflare_extractor,
|
|
122
|
-
yield_raw_on_error=False
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
# Track accumulated content for token counting
|
|
126
|
-
accumulated_content = ""
|
|
127
|
-
|
|
128
|
-
# Stream the chunks
|
|
129
|
-
for content_chunk in processed_stream:
|
|
130
|
-
if content_chunk and isinstance(content_chunk, str):
|
|
131
|
-
accumulated_content += content_chunk
|
|
132
|
-
|
|
133
|
-
# Create and yield a chunk
|
|
134
|
-
delta = ChoiceDelta(content=content_chunk)
|
|
135
|
-
choice = Choice(index=0, delta=delta, finish_reason=None)
|
|
136
|
-
|
|
137
|
-
# Estimate token usage using count_tokens
|
|
138
|
-
prompt_tokens = count_tokens([msg.get("content", "") for msg in payload["messages"]])
|
|
139
|
-
completion_tokens = count_tokens(accumulated_content)
|
|
140
|
-
|
|
141
|
-
chunk = ChatCompletionChunk(
|
|
142
|
-
id=request_id,
|
|
143
|
-
choices=[choice],
|
|
144
|
-
created=created_time,
|
|
145
|
-
model=model
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
yield chunk
|
|
149
|
-
|
|
150
|
-
# Final chunk with finish_reason
|
|
151
|
-
delta = ChoiceDelta(content=None)
|
|
152
|
-
choice = Choice(index=0, delta=delta, finish_reason="stop")
|
|
153
|
-
chunk = ChatCompletionChunk(
|
|
154
|
-
id=request_id,
|
|
155
|
-
choices=[choice],
|
|
156
|
-
created=created_time,
|
|
157
|
-
model=model
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
yield chunk
|
|
161
|
-
|
|
162
|
-
except CurlError as e:
|
|
163
|
-
raise IOError(f"Cloudflare streaming request failed (CurlError): {e}") from e
|
|
164
|
-
except Exception as e:
|
|
165
|
-
raise IOError(f"Cloudflare streaming request failed: {e}") from e
|
|
166
|
-
finally:
|
|
167
|
-
if proxies is not None:
|
|
168
|
-
self._client.session.proxies = original_proxies
|
|
169
|
-
|
|
170
|
-
def _create_non_streaming(
|
|
171
|
-
self,
|
|
172
|
-
*,
|
|
173
|
-
request_id: str,
|
|
174
|
-
created_time: int,
|
|
175
|
-
model: str,
|
|
176
|
-
payload: Dict[str, Any],
|
|
177
|
-
timeout: Optional[int] = None,
|
|
178
|
-
proxies: Optional[dict] = None
|
|
179
|
-
) -> ChatCompletion:
|
|
180
|
-
"""Implementation for non-streaming chat completions."""
|
|
181
|
-
original_proxies = self._client.session.proxies
|
|
182
|
-
if proxies is not None:
|
|
183
|
-
self._client.session.proxies = proxies
|
|
184
|
-
try:
|
|
185
|
-
response = self._client.session.post(
|
|
186
|
-
self._client.chat_endpoint,
|
|
187
|
-
headers=self._client.headers,
|
|
188
|
-
cookies=self._client.cookies,
|
|
189
|
-
data=json.dumps(payload),
|
|
190
|
-
stream=True, # Still use streaming API but collect all chunks
|
|
191
|
-
timeout=timeout if timeout is not None else self._client.timeout,
|
|
192
|
-
impersonate="chrome120"
|
|
193
|
-
)
|
|
194
|
-
response.raise_for_status()
|
|
195
|
-
|
|
196
|
-
# Process the stream and collect all content
|
|
197
|
-
processed_stream = sanitize_stream(
|
|
198
|
-
data=response.iter_content(chunk_size=None),
|
|
199
|
-
intro_value=None,
|
|
200
|
-
to_json=False,
|
|
201
|
-
skip_markers=None,
|
|
202
|
-
content_extractor=self._cloudflare_extractor,
|
|
203
|
-
yield_raw_on_error=False
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
full_content = ""
|
|
207
|
-
for content_chunk in processed_stream:
|
|
208
|
-
if content_chunk and isinstance(content_chunk, str):
|
|
209
|
-
full_content += content_chunk
|
|
210
|
-
|
|
211
|
-
# Create the completion message
|
|
212
|
-
message = ChatCompletionMessage(
|
|
213
|
-
role="assistant",
|
|
214
|
-
content=full_content
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
# Create the choice
|
|
218
|
-
choice = Choice(
|
|
219
|
-
index=0,
|
|
220
|
-
message=message,
|
|
221
|
-
finish_reason="stop"
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
# Estimate token usage using count_tokens
|
|
225
|
-
prompt_tokens = count_tokens([msg.get("content", "") for msg in payload["messages"]])
|
|
226
|
-
completion_tokens = count_tokens(full_content)
|
|
227
|
-
usage = CompletionUsage(
|
|
228
|
-
prompt_tokens=prompt_tokens,
|
|
229
|
-
completion_tokens=completion_tokens,
|
|
230
|
-
total_tokens=prompt_tokens + completion_tokens
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
# Create the completion object
|
|
234
|
-
completion = ChatCompletion(
|
|
235
|
-
id=request_id,
|
|
236
|
-
choices=[choice],
|
|
237
|
-
created=created_time,
|
|
238
|
-
model=model,
|
|
239
|
-
usage=usage,
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
return completion
|
|
243
|
-
|
|
244
|
-
except CurlError as e:
|
|
245
|
-
raise IOError(f"Cloudflare request failed (CurlError): {e}") from e
|
|
246
|
-
except Exception as e:
|
|
247
|
-
raise IOError(f"Cloudflare request failed: {e}") from e
|
|
248
|
-
finally:
|
|
249
|
-
if proxies is not None:
|
|
250
|
-
self._client.session.proxies = original_proxies
|
|
251
|
-
|
|
252
|
-
@staticmethod
|
|
253
|
-
def _cloudflare_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
254
|
-
"""
|
|
255
|
-
Extracts content from Cloudflare stream JSON objects.
|
|
256
|
-
|
|
257
|
-
Args:
|
|
258
|
-
chunk: The chunk to extract content from
|
|
259
|
-
|
|
260
|
-
Returns:
|
|
261
|
-
Extracted content or None if extraction failed
|
|
262
|
-
"""
|
|
263
|
-
if isinstance(chunk, str):
|
|
264
|
-
# Use re.search to find the pattern 0:"<content>"
|
|
265
|
-
match = re.search(r'0:"(.*?)"(?=,|$)', chunk)
|
|
266
|
-
if match:
|
|
267
|
-
# Decode potential unicode escapes and handle escaped quotes/backslashes
|
|
268
|
-
content = match.group(1).encode().decode('unicode_escape')
|
|
269
|
-
return content.replace('\\\\', '\\').replace('\\"', '"')
|
|
270
|
-
return None
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
class Chat(BaseChat):
|
|
274
|
-
def __init__(self, client: 'Cloudflare'):
|
|
275
|
-
self.completions = Completions(client)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
class Cloudflare(OpenAICompatibleProvider):
|
|
279
|
-
"""
|
|
280
|
-
OpenAI-compatible client for Cloudflare API.
|
|
281
|
-
|
|
282
|
-
Usage:
|
|
283
|
-
client = Cloudflare()
|
|
284
|
-
response = client.chat.completions.create(
|
|
285
|
-
model="@cf/meta/llama-3-8b-instruct",
|
|
286
|
-
messages=[{"role": "user", "content": "Hello!"}]
|
|
287
|
-
)
|
|
288
|
-
print(response.choices[0].message.content)
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
AVAILABLE_MODELS = [
|
|
292
|
-
"@hf/thebloke/deepseek-coder-6.7b-base-awq",
|
|
293
|
-
"@hf/thebloke/deepseek-coder-6.7b-instruct-awq",
|
|
294
|
-
"@cf/deepseek-ai/deepseek-math-7b-instruct",
|
|
295
|
-
"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
296
|
-
"@cf/thebloke/discolm-german-7b-v1-awq",
|
|
297
|
-
"@cf/tiiuae/falcon-7b-instruct",
|
|
298
|
-
"@cf/google/gemma-3-12b-it",
|
|
299
|
-
"@hf/google/gemma-7b-it",
|
|
300
|
-
"@hf/nousresearch/hermes-2-pro-mistral-7b",
|
|
301
|
-
"@hf/thebloke/llama-2-13b-chat-awq",
|
|
302
|
-
"@cf/meta/llama-2-7b-chat-fp16",
|
|
303
|
-
"@cf/meta/llama-2-7b-chat-int8",
|
|
304
|
-
"@cf/meta/llama-3-8b-instruct",
|
|
305
|
-
"@cf/meta/llama-3-8b-instruct-awq",
|
|
306
|
-
"@cf/meta/llama-3.1-8b-instruct-awq",
|
|
307
|
-
"@cf/meta/llama-3.1-8b-instruct-fp8",
|
|
308
|
-
"@cf/meta/llama-3.2-11b-vision-instruct",
|
|
309
|
-
"@cf/meta/llama-3.2-1b-instruct",
|
|
310
|
-
"@cf/meta/llama-3.2-3b-instruct",
|
|
311
|
-
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
|
|
312
|
-
"@cf/meta/llama-4-scout-17b-16e-instruct",
|
|
313
|
-
"@cf/meta/llama-guard-3-8b",
|
|
314
|
-
"@hf/thebloke/llamaguard-7b-awq",
|
|
315
|
-
"@hf/meta-llama/meta-llama-3-8b-instruct",
|
|
316
|
-
"@cf/mistral/mistral-7b-instruct-v0.1",
|
|
317
|
-
"@hf/thebloke/mistral-7b-instruct-v0.1-awq",
|
|
318
|
-
"@hf/mistral/mistral-7b-instruct-v0.2",
|
|
319
|
-
"@cf/mistralai/mistral-small-3.1-24b-instruct",
|
|
320
|
-
"@hf/thebloke/neural-chat-7b-v3-1-awq",
|
|
321
|
-
"@cf/openchat/openchat-3.5-0106",
|
|
322
|
-
"@hf/thebloke/openhermes-2.5-mistral-7b-awq",
|
|
323
|
-
"@cf/microsoft/phi-2",
|
|
324
|
-
"@cf/qwen/qwen1.5-0.5b-chat",
|
|
325
|
-
"@cf/qwen/qwen1.5-1.8b-chat",
|
|
326
|
-
"@cf/qwen/qwen1.5-14b-chat-awq",
|
|
327
|
-
"@cf/qwen/qwen1.5-7b-chat-awq",
|
|
328
|
-
"@cf/qwen/qwen2.5-coder-32b-instruct",
|
|
329
|
-
"@cf/qwen/qwq-32b",
|
|
330
|
-
"@cf/defog/sqlcoder-7b-2",
|
|
331
|
-
"@hf/nexusflow/starling-lm-7b-beta",
|
|
332
|
-
"@cf/tinyllama/tinyllama-1.1b-chat-v1.0",
|
|
333
|
-
"@cf/fblgit/una-cybertron-7b-v2-bf16",
|
|
334
|
-
"@hf/thebloke/zephyr-7b-beta-awq"
|
|
335
|
-
]
|
|
336
|
-
|
|
337
|
-
def __init__(
|
|
338
|
-
self,
|
|
339
|
-
api_key: Optional[str] = None, # Not used but included for compatibility
|
|
340
|
-
proxies: Optional[dict] = None
|
|
341
|
-
):
|
|
342
|
-
"""
|
|
343
|
-
Initialize the Cloudflare client.
|
|
344
|
-
|
|
345
|
-
Args:
|
|
346
|
-
api_key: Not used but included for compatibility with OpenAI interface
|
|
347
|
-
proxies: Optional proxy configuration dictionary
|
|
348
|
-
"""
|
|
349
|
-
super().__init__(proxies=proxies)
|
|
350
|
-
self.timeout = 30
|
|
351
|
-
self.chat_endpoint = "https://playground.ai.cloudflare.com/api/inference"
|
|
352
|
-
|
|
353
|
-
# Set headers
|
|
354
|
-
self.headers = {
|
|
355
|
-
'Accept': 'text/event-stream',
|
|
356
|
-
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
|
357
|
-
'Accept-Language': 'en-US,en;q=0.9,en-IN;q=0.8',
|
|
358
|
-
'Content-Type': 'application/json',
|
|
359
|
-
'DNT': '1',
|
|
360
|
-
'Origin': 'https://playground.ai.cloudflare.com',
|
|
361
|
-
'Referer': 'https://playground.ai.cloudflare.com/',
|
|
362
|
-
'Sec-CH-UA': '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
|
|
363
|
-
'Sec-CH-UA-Mobile': '?0',
|
|
364
|
-
'Sec-CH-UA-Platform': '"Windows"',
|
|
365
|
-
'Sec-Fetch-Dest': 'empty',
|
|
366
|
-
'Sec-Fetch-Mode': 'cors',
|
|
367
|
-
'Sec-Fetch-Site': 'same-origin',
|
|
368
|
-
'User-Agent': LitAgent().random()
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
# Set cookies
|
|
372
|
-
self.cookies = {
|
|
373
|
-
'cfzs_amplitude': uuid4().hex,
|
|
374
|
-
'cfz_amplitude': uuid4().hex,
|
|
375
|
-
'__cf_bm': uuid4().hex,
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
# Apply headers to session
|
|
379
|
-
self.session.headers.update(self.headers)
|
|
380
|
-
|
|
381
|
-
# Initialize chat interface
|
|
382
|
-
self.chat = Chat(self)
|
|
383
|
-
|
|
384
|
-
@property
|
|
385
|
-
def models(self):
|
|
386
|
-
class _ModelList:
|
|
387
|
-
def list(inner_self):
|
|
388
|
-
return type(self).AVAILABLE_MODELS
|
|
389
|
-
return _ModelList()
|
|
390
|
-
|
|
391
|
-
# @classmethod
|
|
392
|
-
# def models(cls):
|
|
393
|
-
# """Return the list of available models for Cloudflare."""
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
import re
|
|
5
|
+
from typing import List, Dict, Optional, Union, Generator, Any
|
|
6
|
+
|
|
7
|
+
from curl_cffi import CurlError
|
|
8
|
+
from curl_cffi.requests import Session
|
|
9
|
+
from uuid import uuid4
|
|
10
|
+
|
|
11
|
+
# Import base classes and utility structures
|
|
12
|
+
from webscout.Provider.OPENAI.base import OpenAICompatibleProvider, BaseChat, BaseCompletions
|
|
13
|
+
from webscout.Provider.OPENAI.utils import (
|
|
14
|
+
ChatCompletionChunk, ChatCompletion, Choice, ChoiceDelta,
|
|
15
|
+
ChatCompletionMessage, CompletionUsage, count_tokens
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from webscout.AIutel import sanitize_stream
|
|
19
|
+
from webscout.litagent import LitAgent
|
|
20
|
+
|
|
21
|
+
class Completions(BaseCompletions):
|
|
22
|
+
def __init__(self, client: 'Cloudflare'):
|
|
23
|
+
self._client = client
|
|
24
|
+
|
|
25
|
+
def create(
|
|
26
|
+
self,
|
|
27
|
+
*,
|
|
28
|
+
model: str,
|
|
29
|
+
messages: List[Dict[str, str]],
|
|
30
|
+
max_tokens: Optional[int] = None,
|
|
31
|
+
stream: bool = False,
|
|
32
|
+
temperature: Optional[float] = None,
|
|
33
|
+
top_p: Optional[float] = None,
|
|
34
|
+
timeout: Optional[int] = None,
|
|
35
|
+
proxies: Optional[dict] = None,
|
|
36
|
+
**kwargs: Any
|
|
37
|
+
) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
|
|
38
|
+
"""
|
|
39
|
+
Create a chat completion with Cloudflare API.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
model: The model to use (from AVAILABLE_MODELS)
|
|
43
|
+
messages: List of message dictionaries with 'role' and 'content'
|
|
44
|
+
max_tokens: Maximum number of tokens to generate
|
|
45
|
+
stream: Whether to stream the response
|
|
46
|
+
temperature: Sampling temperature (0-1)
|
|
47
|
+
top_p: Nucleus sampling parameter (0-1)
|
|
48
|
+
**kwargs: Additional parameters to pass to the API
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
If stream=False, returns a ChatCompletion object
|
|
52
|
+
If stream=True, returns a Generator yielding ChatCompletionChunk objects
|
|
53
|
+
"""
|
|
54
|
+
# Prepare the payload
|
|
55
|
+
payload = {
|
|
56
|
+
"messages": messages,
|
|
57
|
+
"lora": None,
|
|
58
|
+
"model": model,
|
|
59
|
+
"max_tokens": max_tokens or 600,
|
|
60
|
+
"stream": True # Always use streaming API
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Generate request ID and timestamp
|
|
64
|
+
request_id = str(uuid.uuid4())
|
|
65
|
+
created_time = int(time.time())
|
|
66
|
+
|
|
67
|
+
# Use streaming implementation if requested
|
|
68
|
+
if stream:
|
|
69
|
+
return self._create_streaming(
|
|
70
|
+
request_id=request_id,
|
|
71
|
+
created_time=created_time,
|
|
72
|
+
model=model,
|
|
73
|
+
payload=payload,
|
|
74
|
+
timeout=timeout,
|
|
75
|
+
proxies=proxies
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Otherwise use non-streaming implementation
|
|
79
|
+
return self._create_non_streaming(
|
|
80
|
+
request_id=request_id,
|
|
81
|
+
created_time=created_time,
|
|
82
|
+
model=model,
|
|
83
|
+
payload=payload,
|
|
84
|
+
timeout=timeout,
|
|
85
|
+
proxies=proxies
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def _create_streaming(
|
|
89
|
+
self,
|
|
90
|
+
*,
|
|
91
|
+
request_id: str,
|
|
92
|
+
created_time: int,
|
|
93
|
+
model: str,
|
|
94
|
+
payload: Dict[str, Any],
|
|
95
|
+
timeout: Optional[int] = None,
|
|
96
|
+
proxies: Optional[dict] = None
|
|
97
|
+
) -> Generator[ChatCompletionChunk, None, None]:
|
|
98
|
+
"""Implementation for streaming chat completions."""
|
|
99
|
+
original_proxies = self._client.session.proxies
|
|
100
|
+
if proxies is not None:
|
|
101
|
+
self._client.session.proxies = proxies
|
|
102
|
+
try:
|
|
103
|
+
response = self._client.session.post(
|
|
104
|
+
self._client.chat_endpoint,
|
|
105
|
+
headers=self._client.headers,
|
|
106
|
+
cookies=self._client.cookies,
|
|
107
|
+
data=json.dumps(payload),
|
|
108
|
+
stream=True,
|
|
109
|
+
timeout=timeout if timeout is not None else self._client.timeout,
|
|
110
|
+
impersonate="chrome120"
|
|
111
|
+
)
|
|
112
|
+
response.raise_for_status()
|
|
113
|
+
|
|
114
|
+
# Process the stream using sanitize_stream
|
|
115
|
+
# This handles the extraction of content from Cloudflare's response format
|
|
116
|
+
processed_stream = sanitize_stream(
|
|
117
|
+
data=response.iter_content(chunk_size=None),
|
|
118
|
+
intro_value=None,
|
|
119
|
+
to_json=False,
|
|
120
|
+
skip_markers=None,
|
|
121
|
+
content_extractor=self._cloudflare_extractor,
|
|
122
|
+
yield_raw_on_error=False
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Track accumulated content for token counting
|
|
126
|
+
accumulated_content = ""
|
|
127
|
+
|
|
128
|
+
# Stream the chunks
|
|
129
|
+
for content_chunk in processed_stream:
|
|
130
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
131
|
+
accumulated_content += content_chunk
|
|
132
|
+
|
|
133
|
+
# Create and yield a chunk
|
|
134
|
+
delta = ChoiceDelta(content=content_chunk)
|
|
135
|
+
choice = Choice(index=0, delta=delta, finish_reason=None)
|
|
136
|
+
|
|
137
|
+
# Estimate token usage using count_tokens
|
|
138
|
+
prompt_tokens = count_tokens([msg.get("content", "") for msg in payload["messages"]])
|
|
139
|
+
completion_tokens = count_tokens(accumulated_content)
|
|
140
|
+
|
|
141
|
+
chunk = ChatCompletionChunk(
|
|
142
|
+
id=request_id,
|
|
143
|
+
choices=[choice],
|
|
144
|
+
created=created_time,
|
|
145
|
+
model=model
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
yield chunk
|
|
149
|
+
|
|
150
|
+
# Final chunk with finish_reason
|
|
151
|
+
delta = ChoiceDelta(content=None)
|
|
152
|
+
choice = Choice(index=0, delta=delta, finish_reason="stop")
|
|
153
|
+
chunk = ChatCompletionChunk(
|
|
154
|
+
id=request_id,
|
|
155
|
+
choices=[choice],
|
|
156
|
+
created=created_time,
|
|
157
|
+
model=model
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
yield chunk
|
|
161
|
+
|
|
162
|
+
except CurlError as e:
|
|
163
|
+
raise IOError(f"Cloudflare streaming request failed (CurlError): {e}") from e
|
|
164
|
+
except Exception as e:
|
|
165
|
+
raise IOError(f"Cloudflare streaming request failed: {e}") from e
|
|
166
|
+
finally:
|
|
167
|
+
if proxies is not None:
|
|
168
|
+
self._client.session.proxies = original_proxies
|
|
169
|
+
|
|
170
|
+
def _create_non_streaming(
|
|
171
|
+
self,
|
|
172
|
+
*,
|
|
173
|
+
request_id: str,
|
|
174
|
+
created_time: int,
|
|
175
|
+
model: str,
|
|
176
|
+
payload: Dict[str, Any],
|
|
177
|
+
timeout: Optional[int] = None,
|
|
178
|
+
proxies: Optional[dict] = None
|
|
179
|
+
) -> ChatCompletion:
|
|
180
|
+
"""Implementation for non-streaming chat completions."""
|
|
181
|
+
original_proxies = self._client.session.proxies
|
|
182
|
+
if proxies is not None:
|
|
183
|
+
self._client.session.proxies = proxies
|
|
184
|
+
try:
|
|
185
|
+
response = self._client.session.post(
|
|
186
|
+
self._client.chat_endpoint,
|
|
187
|
+
headers=self._client.headers,
|
|
188
|
+
cookies=self._client.cookies,
|
|
189
|
+
data=json.dumps(payload),
|
|
190
|
+
stream=True, # Still use streaming API but collect all chunks
|
|
191
|
+
timeout=timeout if timeout is not None else self._client.timeout,
|
|
192
|
+
impersonate="chrome120"
|
|
193
|
+
)
|
|
194
|
+
response.raise_for_status()
|
|
195
|
+
|
|
196
|
+
# Process the stream and collect all content
|
|
197
|
+
processed_stream = sanitize_stream(
|
|
198
|
+
data=response.iter_content(chunk_size=None),
|
|
199
|
+
intro_value=None,
|
|
200
|
+
to_json=False,
|
|
201
|
+
skip_markers=None,
|
|
202
|
+
content_extractor=self._cloudflare_extractor,
|
|
203
|
+
yield_raw_on_error=False
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
full_content = ""
|
|
207
|
+
for content_chunk in processed_stream:
|
|
208
|
+
if content_chunk and isinstance(content_chunk, str):
|
|
209
|
+
full_content += content_chunk
|
|
210
|
+
|
|
211
|
+
# Create the completion message
|
|
212
|
+
message = ChatCompletionMessage(
|
|
213
|
+
role="assistant",
|
|
214
|
+
content=full_content
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
# Create the choice
|
|
218
|
+
choice = Choice(
|
|
219
|
+
index=0,
|
|
220
|
+
message=message,
|
|
221
|
+
finish_reason="stop"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Estimate token usage using count_tokens
|
|
225
|
+
prompt_tokens = count_tokens([msg.get("content", "") for msg in payload["messages"]])
|
|
226
|
+
completion_tokens = count_tokens(full_content)
|
|
227
|
+
usage = CompletionUsage(
|
|
228
|
+
prompt_tokens=prompt_tokens,
|
|
229
|
+
completion_tokens=completion_tokens,
|
|
230
|
+
total_tokens=prompt_tokens + completion_tokens
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Create the completion object
|
|
234
|
+
completion = ChatCompletion(
|
|
235
|
+
id=request_id,
|
|
236
|
+
choices=[choice],
|
|
237
|
+
created=created_time,
|
|
238
|
+
model=model,
|
|
239
|
+
usage=usage,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
return completion
|
|
243
|
+
|
|
244
|
+
except CurlError as e:
|
|
245
|
+
raise IOError(f"Cloudflare request failed (CurlError): {e}") from e
|
|
246
|
+
except Exception as e:
|
|
247
|
+
raise IOError(f"Cloudflare request failed: {e}") from e
|
|
248
|
+
finally:
|
|
249
|
+
if proxies is not None:
|
|
250
|
+
self._client.session.proxies = original_proxies
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def _cloudflare_extractor(chunk: Union[str, Dict[str, Any]]) -> Optional[str]:
|
|
254
|
+
"""
|
|
255
|
+
Extracts content from Cloudflare stream JSON objects.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
chunk: The chunk to extract content from
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Extracted content or None if extraction failed
|
|
262
|
+
"""
|
|
263
|
+
if isinstance(chunk, str):
|
|
264
|
+
# Use re.search to find the pattern 0:"<content>"
|
|
265
|
+
match = re.search(r'0:"(.*?)"(?=,|$)', chunk)
|
|
266
|
+
if match:
|
|
267
|
+
# Decode potential unicode escapes and handle escaped quotes/backslashes
|
|
268
|
+
content = match.group(1).encode().decode('unicode_escape')
|
|
269
|
+
return content.replace('\\\\', '\\').replace('\\"', '"')
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class Chat(BaseChat):
|
|
274
|
+
def __init__(self, client: 'Cloudflare'):
|
|
275
|
+
self.completions = Completions(client)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class Cloudflare(OpenAICompatibleProvider):
|
|
279
|
+
"""
|
|
280
|
+
OpenAI-compatible client for Cloudflare API.
|
|
281
|
+
|
|
282
|
+
Usage:
|
|
283
|
+
client = Cloudflare()
|
|
284
|
+
response = client.chat.completions.create(
|
|
285
|
+
model="@cf/meta/llama-3-8b-instruct",
|
|
286
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
287
|
+
)
|
|
288
|
+
print(response.choices[0].message.content)
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
AVAILABLE_MODELS = [
|
|
292
|
+
"@hf/thebloke/deepseek-coder-6.7b-base-awq",
|
|
293
|
+
"@hf/thebloke/deepseek-coder-6.7b-instruct-awq",
|
|
294
|
+
"@cf/deepseek-ai/deepseek-math-7b-instruct",
|
|
295
|
+
"@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
296
|
+
"@cf/thebloke/discolm-german-7b-v1-awq",
|
|
297
|
+
"@cf/tiiuae/falcon-7b-instruct",
|
|
298
|
+
"@cf/google/gemma-3-12b-it",
|
|
299
|
+
"@hf/google/gemma-7b-it",
|
|
300
|
+
"@hf/nousresearch/hermes-2-pro-mistral-7b",
|
|
301
|
+
"@hf/thebloke/llama-2-13b-chat-awq",
|
|
302
|
+
"@cf/meta/llama-2-7b-chat-fp16",
|
|
303
|
+
"@cf/meta/llama-2-7b-chat-int8",
|
|
304
|
+
"@cf/meta/llama-3-8b-instruct",
|
|
305
|
+
"@cf/meta/llama-3-8b-instruct-awq",
|
|
306
|
+
"@cf/meta/llama-3.1-8b-instruct-awq",
|
|
307
|
+
"@cf/meta/llama-3.1-8b-instruct-fp8",
|
|
308
|
+
"@cf/meta/llama-3.2-11b-vision-instruct",
|
|
309
|
+
"@cf/meta/llama-3.2-1b-instruct",
|
|
310
|
+
"@cf/meta/llama-3.2-3b-instruct",
|
|
311
|
+
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
|
|
312
|
+
"@cf/meta/llama-4-scout-17b-16e-instruct",
|
|
313
|
+
"@cf/meta/llama-guard-3-8b",
|
|
314
|
+
"@hf/thebloke/llamaguard-7b-awq",
|
|
315
|
+
"@hf/meta-llama/meta-llama-3-8b-instruct",
|
|
316
|
+
"@cf/mistral/mistral-7b-instruct-v0.1",
|
|
317
|
+
"@hf/thebloke/mistral-7b-instruct-v0.1-awq",
|
|
318
|
+
"@hf/mistral/mistral-7b-instruct-v0.2",
|
|
319
|
+
"@cf/mistralai/mistral-small-3.1-24b-instruct",
|
|
320
|
+
"@hf/thebloke/neural-chat-7b-v3-1-awq",
|
|
321
|
+
"@cf/openchat/openchat-3.5-0106",
|
|
322
|
+
"@hf/thebloke/openhermes-2.5-mistral-7b-awq",
|
|
323
|
+
"@cf/microsoft/phi-2",
|
|
324
|
+
"@cf/qwen/qwen1.5-0.5b-chat",
|
|
325
|
+
"@cf/qwen/qwen1.5-1.8b-chat",
|
|
326
|
+
"@cf/qwen/qwen1.5-14b-chat-awq",
|
|
327
|
+
"@cf/qwen/qwen1.5-7b-chat-awq",
|
|
328
|
+
"@cf/qwen/qwen2.5-coder-32b-instruct",
|
|
329
|
+
"@cf/qwen/qwq-32b",
|
|
330
|
+
"@cf/defog/sqlcoder-7b-2",
|
|
331
|
+
"@hf/nexusflow/starling-lm-7b-beta",
|
|
332
|
+
"@cf/tinyllama/tinyllama-1.1b-chat-v1.0",
|
|
333
|
+
"@cf/fblgit/una-cybertron-7b-v2-bf16",
|
|
334
|
+
"@hf/thebloke/zephyr-7b-beta-awq"
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
def __init__(
|
|
338
|
+
self,
|
|
339
|
+
api_key: Optional[str] = None, # Not used but included for compatibility
|
|
340
|
+
proxies: Optional[dict] = None
|
|
341
|
+
):
|
|
342
|
+
"""
|
|
343
|
+
Initialize the Cloudflare client.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
api_key: Not used but included for compatibility with OpenAI interface
|
|
347
|
+
proxies: Optional proxy configuration dictionary
|
|
348
|
+
"""
|
|
349
|
+
super().__init__(proxies=proxies)
|
|
350
|
+
self.timeout = 30
|
|
351
|
+
self.chat_endpoint = "https://playground.ai.cloudflare.com/api/inference"
|
|
352
|
+
|
|
353
|
+
# Set headers
|
|
354
|
+
self.headers = {
|
|
355
|
+
'Accept': 'text/event-stream',
|
|
356
|
+
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
|
357
|
+
'Accept-Language': 'en-US,en;q=0.9,en-IN;q=0.8',
|
|
358
|
+
'Content-Type': 'application/json',
|
|
359
|
+
'DNT': '1',
|
|
360
|
+
'Origin': 'https://playground.ai.cloudflare.com',
|
|
361
|
+
'Referer': 'https://playground.ai.cloudflare.com/',
|
|
362
|
+
'Sec-CH-UA': '"Not)A;Brand";v="99", "Microsoft Edge";v="127", "Chromium";v="127"',
|
|
363
|
+
'Sec-CH-UA-Mobile': '?0',
|
|
364
|
+
'Sec-CH-UA-Platform': '"Windows"',
|
|
365
|
+
'Sec-Fetch-Dest': 'empty',
|
|
366
|
+
'Sec-Fetch-Mode': 'cors',
|
|
367
|
+
'Sec-Fetch-Site': 'same-origin',
|
|
368
|
+
'User-Agent': LitAgent().random()
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
# Set cookies
|
|
372
|
+
self.cookies = {
|
|
373
|
+
'cfzs_amplitude': uuid4().hex,
|
|
374
|
+
'cfz_amplitude': uuid4().hex,
|
|
375
|
+
'__cf_bm': uuid4().hex,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# Apply headers to session
|
|
379
|
+
self.session.headers.update(self.headers)
|
|
380
|
+
|
|
381
|
+
# Initialize chat interface
|
|
382
|
+
self.chat = Chat(self)
|
|
383
|
+
|
|
384
|
+
@property
|
|
385
|
+
def models(self):
|
|
386
|
+
class _ModelList:
|
|
387
|
+
def list(inner_self):
|
|
388
|
+
return type(self).AVAILABLE_MODELS
|
|
389
|
+
return _ModelList()
|
|
390
|
+
|
|
391
|
+
# @classmethod
|
|
392
|
+
# def models(cls):
|
|
393
|
+
# """Return the list of available models for Cloudflare."""
|
|
394
394
|
# return cls.AVAILABLE_MODELS
|