webscout 8.2.9__py3-none-any.whl → 2026.1.19__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.
- webscout/AIauto.py +524 -251
- webscout/AIbase.py +247 -319
- webscout/AIutel.py +68 -703
- webscout/Bard.py +1072 -1026
- webscout/Extra/GitToolkit/__init__.py +10 -10
- webscout/Extra/GitToolkit/gitapi/__init__.py +20 -12
- webscout/Extra/GitToolkit/gitapi/gist.py +142 -0
- webscout/Extra/GitToolkit/gitapi/organization.py +91 -0
- webscout/Extra/GitToolkit/gitapi/repository.py +308 -195
- webscout/Extra/GitToolkit/gitapi/search.py +162 -0
- webscout/Extra/GitToolkit/gitapi/trending.py +236 -0
- webscout/Extra/GitToolkit/gitapi/user.py +128 -96
- webscout/Extra/GitToolkit/gitapi/utils.py +82 -62
- webscout/Extra/YTToolkit/README.md +443 -375
- webscout/Extra/YTToolkit/YTdownloader.py +953 -957
- webscout/Extra/YTToolkit/__init__.py +3 -3
- webscout/Extra/YTToolkit/transcriber.py +595 -476
- webscout/Extra/YTToolkit/ytapi/README.md +230 -44
- webscout/Extra/YTToolkit/ytapi/__init__.py +22 -6
- webscout/Extra/YTToolkit/ytapi/captions.py +190 -0
- webscout/Extra/YTToolkit/ytapi/channel.py +302 -307
- webscout/Extra/YTToolkit/ytapi/errors.py +13 -13
- webscout/Extra/YTToolkit/ytapi/extras.py +178 -118
- webscout/Extra/YTToolkit/ytapi/hashtag.py +120 -0
- webscout/Extra/YTToolkit/ytapi/https.py +89 -88
- webscout/Extra/YTToolkit/ytapi/patterns.py +61 -61
- webscout/Extra/YTToolkit/ytapi/playlist.py +59 -59
- webscout/Extra/YTToolkit/ytapi/pool.py +8 -8
- webscout/Extra/YTToolkit/ytapi/query.py +143 -40
- webscout/Extra/YTToolkit/ytapi/shorts.py +122 -0
- webscout/Extra/YTToolkit/ytapi/stream.py +68 -63
- webscout/Extra/YTToolkit/ytapi/suggestions.py +97 -0
- webscout/Extra/YTToolkit/ytapi/utils.py +66 -62
- webscout/Extra/YTToolkit/ytapi/video.py +403 -232
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -684
- webscout/Extra/tempmail/README.md +487 -487
- webscout/Extra/tempmail/__init__.py +28 -28
- webscout/Extra/tempmail/async_utils.py +143 -141
- webscout/Extra/tempmail/base.py +172 -161
- webscout/Extra/tempmail/cli.py +191 -187
- webscout/Extra/tempmail/emailnator.py +88 -84
- webscout/Extra/tempmail/mail_tm.py +378 -361
- webscout/Extra/tempmail/temp_mail_io.py +304 -292
- webscout/Extra/weather.py +196 -194
- webscout/Extra/weather_ascii.py +17 -15
- webscout/Provider/AISEARCH/PERPLEXED_search.py +175 -0
- webscout/Provider/AISEARCH/Perplexity.py +292 -333
- webscout/Provider/AISEARCH/README.md +106 -279
- webscout/Provider/AISEARCH/__init__.py +16 -9
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +357 -410
- webscout/Provider/AISEARCH/monica_search.py +200 -220
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -255
- webscout/Provider/Algion.py +413 -0
- webscout/Provider/Andi.py +74 -69
- webscout/Provider/Apriel.py +313 -0
- webscout/Provider/Ayle.py +323 -0
- webscout/Provider/ChatSandbox.py +329 -342
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +467 -340
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +175 -169
- webscout/Provider/GithubChat.py +385 -369
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -801
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -375
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -291
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -384
- webscout/Provider/Netwrck.py +273 -270
- webscout/Provider/Nvidia.py +310 -0
- webscout/Provider/OPENAI/DeepAI.py +489 -0
- webscout/Provider/OPENAI/K2Think.py +423 -0
- webscout/Provider/OPENAI/PI.py +463 -0
- webscout/Provider/OPENAI/README.md +890 -952
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -357
- webscout/Provider/OPENAI/__init__.py +148 -40
- webscout/Provider/OPENAI/ai4chat.py +348 -293
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/{exachat.py → ayle.py} +365 -444
- webscout/Provider/OPENAI/base.py +253 -249
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +870 -556
- webscout/Provider/OPENAI/chatsandbox.py +233 -173
- webscout/Provider/OPENAI/deepinfra.py +403 -322
- webscout/Provider/OPENAI/e2b.py +2370 -1414
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +452 -417
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -364
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +333 -308
- webscout/Provider/OPENAI/huggingface.py +321 -0
- webscout/Provider/OPENAI/ibm.py +425 -0
- webscout/Provider/OPENAI/llmchat.py +253 -0
- webscout/Provider/OPENAI/llmchatco.py +378 -335
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +374 -357
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -287
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -172
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +305 -304
- webscout/Provider/OPENAI/textpollinations.py +370 -339
- webscout/Provider/OPENAI/toolbaz.py +375 -413
- webscout/Provider/OPENAI/typefully.py +419 -355
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -318
- webscout/Provider/OPENAI/wisecat.py +359 -387
- webscout/Provider/OPENAI/writecream.py +185 -163
- webscout/Provider/OPENAI/x0gpt.py +462 -365
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -429
- webscout/Provider/QwenLM.py +346 -254
- webscout/Provider/STT/__init__.py +28 -0
- webscout/Provider/STT/base.py +303 -0
- webscout/Provider/STT/elevenlabs.py +264 -0
- webscout/Provider/Sambanova.py +317 -0
- webscout/Provider/TTI/README.md +69 -82
- webscout/Provider/TTI/__init__.py +37 -7
- webscout/Provider/TTI/base.py +147 -64
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -201
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -221
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -11
- webscout/Provider/TTS/README.md +186 -192
- webscout/Provider/TTS/__init__.py +43 -10
- webscout/Provider/TTS/base.py +523 -159
- webscout/Provider/TTS/deepgram.py +286 -156
- webscout/Provider/TTS/elevenlabs.py +189 -111
- webscout/Provider/TTS/freetts.py +218 -0
- webscout/Provider/TTS/murfai.py +288 -113
- webscout/Provider/TTS/openai_fm.py +364 -129
- webscout/Provider/TTS/parler.py +203 -111
- webscout/Provider/TTS/qwen.py +334 -0
- webscout/Provider/TTS/sherpa.py +286 -0
- webscout/Provider/TTS/speechma.py +693 -580
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +331 -308
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -475
- webscout/Provider/TypliAI.py +311 -305
- webscout/Provider/UNFINISHED/ChatHub.py +219 -209
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +331 -326
- webscout/Provider/{GizAI.py → UNFINISHED/GizAI.py} +300 -295
- webscout/Provider/{Marcus.py → UNFINISHED/Marcus.py} +218 -198
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/{MCPCore.py → UNFINISHED/XenAI.py} +330 -315
- webscout/Provider/UNFINISHED/Youchat.py +347 -330
- webscout/Provider/UNFINISHED/aihumanizer.py +41 -0
- webscout/Provider/UNFINISHED/grammerchecker.py +37 -0
- webscout/Provider/UNFINISHED/liner.py +342 -0
- webscout/Provider/UNFINISHED/liner_api_request.py +246 -263
- webscout/Provider/{samurai.py → UNFINISHED/samurai.py} +231 -224
- webscout/Provider/WiseCat.py +256 -233
- webscout/Provider/WrDoChat.py +390 -370
- webscout/Provider/__init__.py +115 -174
- webscout/Provider/ai4chat.py +181 -174
- webscout/Provider/akashgpt.py +330 -335
- webscout/Provider/cerebras.py +397 -290
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -283
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -325
- webscout/Provider/llama3mitril.py +230 -215
- webscout/Provider/llmchat.py +308 -258
- webscout/Provider/llmchatco.py +321 -306
- webscout/Provider/meta.py +996 -801
- webscout/Provider/oivscode.py +332 -309
- webscout/Provider/searchchat.py +316 -292
- webscout/Provider/sonus.py +264 -258
- webscout/Provider/toolbaz.py +359 -353
- webscout/Provider/turboseek.py +332 -266
- webscout/Provider/typefully.py +262 -202
- webscout/Provider/x0gpt.py +332 -299
- webscout/__init__.py +31 -39
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -524
- webscout/client.py +1497 -70
- webscout/conversation.py +140 -436
- webscout/exceptions.py +383 -362
- webscout/litagent/__init__.py +29 -29
- webscout/litagent/agent.py +492 -455
- webscout/litagent/constants.py +60 -60
- webscout/models.py +505 -181
- webscout/optimizers.py +74 -420
- webscout/prompt_manager.py +376 -288
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -404
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -210
- webscout/scout/core/scout.py +800 -607
- webscout/scout/core/search_result.py +51 -96
- webscout/scout/core/text_analyzer.py +64 -63
- webscout/scout/core/text_utils.py +412 -277
- webscout/scout/core/web_analyzer.py +54 -52
- webscout/scout/element.py +872 -478
- webscout/scout/parsers/__init__.py +70 -69
- webscout/scout/parsers/html5lib_parser.py +182 -172
- webscout/scout/parsers/html_parser.py +238 -236
- webscout/scout/parsers/lxml_parser.py +203 -178
- webscout/scout/utils.py +38 -37
- webscout/search/__init__.py +47 -0
- webscout/search/base.py +201 -0
- webscout/search/bing_main.py +45 -0
- webscout/search/brave_main.py +92 -0
- webscout/search/duckduckgo_main.py +57 -0
- webscout/search/engines/__init__.py +127 -0
- webscout/search/engines/bing/__init__.py +15 -0
- webscout/search/engines/bing/base.py +35 -0
- webscout/search/engines/bing/images.py +114 -0
- webscout/search/engines/bing/news.py +96 -0
- webscout/search/engines/bing/suggestions.py +36 -0
- webscout/search/engines/bing/text.py +109 -0
- webscout/search/engines/brave/__init__.py +19 -0
- webscout/search/engines/brave/base.py +47 -0
- webscout/search/engines/brave/images.py +213 -0
- webscout/search/engines/brave/news.py +353 -0
- webscout/search/engines/brave/suggestions.py +318 -0
- webscout/search/engines/brave/text.py +167 -0
- webscout/search/engines/brave/videos.py +364 -0
- webscout/search/engines/duckduckgo/__init__.py +25 -0
- webscout/search/engines/duckduckgo/answers.py +80 -0
- webscout/search/engines/duckduckgo/base.py +189 -0
- webscout/search/engines/duckduckgo/images.py +100 -0
- webscout/search/engines/duckduckgo/maps.py +183 -0
- webscout/search/engines/duckduckgo/news.py +70 -0
- webscout/search/engines/duckduckgo/suggestions.py +22 -0
- webscout/search/engines/duckduckgo/text.py +221 -0
- webscout/search/engines/duckduckgo/translate.py +48 -0
- webscout/search/engines/duckduckgo/videos.py +80 -0
- webscout/search/engines/duckduckgo/weather.py +84 -0
- webscout/search/engines/mojeek.py +61 -0
- webscout/search/engines/wikipedia.py +77 -0
- webscout/search/engines/yahoo/__init__.py +41 -0
- webscout/search/engines/yahoo/answers.py +19 -0
- webscout/search/engines/yahoo/base.py +34 -0
- webscout/search/engines/yahoo/images.py +323 -0
- webscout/search/engines/yahoo/maps.py +19 -0
- webscout/search/engines/yahoo/news.py +258 -0
- webscout/search/engines/yahoo/suggestions.py +140 -0
- webscout/search/engines/yahoo/text.py +273 -0
- webscout/search/engines/yahoo/translate.py +19 -0
- webscout/search/engines/yahoo/videos.py +302 -0
- webscout/search/engines/yahoo/weather.py +220 -0
- webscout/search/engines/yandex.py +67 -0
- webscout/search/engines/yep/__init__.py +13 -0
- webscout/search/engines/yep/base.py +34 -0
- webscout/search/engines/yep/images.py +101 -0
- webscout/search/engines/yep/suggestions.py +38 -0
- webscout/search/engines/yep/text.py +99 -0
- webscout/search/http_client.py +172 -0
- webscout/search/results.py +141 -0
- webscout/search/yahoo_main.py +57 -0
- webscout/search/yep_main.py +48 -0
- webscout/server/__init__.py +48 -0
- webscout/server/config.py +78 -0
- webscout/server/exceptions.py +69 -0
- webscout/server/providers.py +286 -0
- webscout/server/request_models.py +131 -0
- webscout/server/request_processing.py +404 -0
- webscout/server/routes.py +642 -0
- webscout/server/server.py +351 -0
- webscout/server/ui_templates.py +1171 -0
- webscout/swiftcli/__init__.py +79 -95
- webscout/swiftcli/core/__init__.py +7 -7
- webscout/swiftcli/core/cli.py +574 -297
- webscout/swiftcli/core/context.py +98 -104
- webscout/swiftcli/core/group.py +268 -241
- webscout/swiftcli/decorators/__init__.py +28 -28
- webscout/swiftcli/decorators/command.py +243 -221
- webscout/swiftcli/decorators/options.py +247 -220
- webscout/swiftcli/decorators/output.py +392 -252
- webscout/swiftcli/exceptions.py +21 -21
- webscout/swiftcli/plugins/__init__.py +9 -9
- webscout/swiftcli/plugins/base.py +134 -135
- webscout/swiftcli/plugins/manager.py +269 -269
- webscout/swiftcli/utils/__init__.py +58 -59
- webscout/swiftcli/utils/formatting.py +251 -252
- webscout/swiftcli/utils/parsing.py +368 -267
- webscout/update_checker.py +280 -136
- webscout/utils.py +28 -14
- webscout/version.py +2 -1
- webscout/version.py.bak +3 -0
- webscout/zeroart/__init__.py +218 -135
- webscout/zeroart/base.py +70 -66
- webscout/zeroart/effects.py +155 -101
- webscout/zeroart/fonts.py +1799 -1239
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/entry_points.txt +1 -1
- webscout/DWEBS.py +0 -520
- webscout/Extra/Act.md +0 -309
- webscout/Extra/GitToolkit/gitapi/README.md +0 -110
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -1105
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/Extra/gguf.md +0 -430
- webscout/Extra/weather.md +0 -281
- webscout/Litlogger/README.md +0 -10
- webscout/Litlogger/__init__.py +0 -15
- webscout/Litlogger/formats.py +0 -4
- webscout/Litlogger/handlers.py +0 -103
- webscout/Litlogger/levels.py +0 -13
- webscout/Litlogger/logger.py +0 -92
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -254
- webscout/Provider/AISEARCH/felo_search.py +0 -202
- webscout/Provider/AISEARCH/genspark_search.py +0 -324
- webscout/Provider/AISEARCH/hika_search.py +0 -186
- webscout/Provider/AISEARCH/scira_search.py +0 -298
- webscout/Provider/Aitopia.py +0 -316
- webscout/Provider/AllenAI.py +0 -440
- webscout/Provider/Blackboxai.py +0 -791
- webscout/Provider/ChatGPTClone.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Cloudflare.py +0 -324
- webscout/Provider/ExaChat.py +0 -358
- webscout/Provider/Flowith.py +0 -217
- webscout/Provider/FreeGemini.py +0 -250
- webscout/Provider/Glider.py +0 -225
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -469
- webscout/Provider/Hunyuan.py +0 -283
- webscout/Provider/LambdaChat.py +0 -411
- webscout/Provider/Llama3.py +0 -259
- webscout/Provider/Nemotron.py +0 -218
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/BLACKBOXAI.py +0 -766
- webscout/Provider/OPENAI/Cloudflare.py +0 -378
- webscout/Provider/OPENAI/FreeGemini.py +0 -283
- webscout/Provider/OPENAI/NEMOTRON.py +0 -232
- webscout/Provider/OPENAI/Qwen3.py +0 -283
- webscout/Provider/OPENAI/api.py +0 -969
- webscout/Provider/OPENAI/c4ai.py +0 -373
- webscout/Provider/OPENAI/chatgptclone.py +0 -494
- webscout/Provider/OPENAI/copilot.py +0 -242
- webscout/Provider/OPENAI/flowith.py +0 -162
- webscout/Provider/OPENAI/freeaichat.py +0 -359
- webscout/Provider/OPENAI/mcpcore.py +0 -389
- webscout/Provider/OPENAI/multichat.py +0 -376
- webscout/Provider/OPENAI/opkfc.py +0 -496
- webscout/Provider/OPENAI/scirachat.py +0 -477
- webscout/Provider/OPENAI/standardinput.py +0 -433
- webscout/Provider/OPENAI/typegpt.py +0 -364
- webscout/Provider/OPENAI/uncovrAI.py +0 -463
- webscout/Provider/OPENAI/venice.py +0 -431
- webscout/Provider/OPENAI/yep.py +0 -382
- webscout/Provider/OpenGPT.py +0 -209
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -290
- webscout/Provider/TTI/aiarta.py +0 -365
- webscout/Provider/TTI/artbit.py +0 -0
- webscout/Provider/TTI/fastflux.py +0 -200
- webscout/Provider/TTI/piclumen.py +0 -203
- webscout/Provider/TTI/pixelmuse.py +0 -225
- webscout/Provider/TTS/gesserit.py +0 -128
- webscout/Provider/TTS/sthir.py +0 -94
- webscout/Provider/TeachAnything.py +0 -229
- webscout/Provider/UNFINISHED/puterjs.py +0 -635
- webscout/Provider/UNFINISHED/test_lmarena.py +0 -119
- webscout/Provider/Venice.py +0 -258
- webscout/Provider/VercelAI.py +0 -253
- webscout/Provider/Writecream.py +0 -246
- webscout/Provider/WritingMate.py +0 -269
- webscout/Provider/asksteve.py +0 -220
- webscout/Provider/chatglm.py +0 -215
- webscout/Provider/copilot.py +0 -425
- webscout/Provider/freeaichat.py +0 -285
- webscout/Provider/granite.py +0 -235
- webscout/Provider/hermes.py +0 -266
- webscout/Provider/koala.py +0 -170
- webscout/Provider/lmarena.py +0 -198
- webscout/Provider/multichat.py +0 -364
- webscout/Provider/scira_chat.py +0 -299
- webscout/Provider/scnet.py +0 -243
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/typegpt.py +0 -289
- webscout/Provider/uncovr.py +0 -368
- webscout/Provider/yep.py +0 -389
- webscout/litagent/Readme.md +0 -276
- webscout/litprinter/__init__.py +0 -59
- webscout/swiftcli/Readme.md +0 -323
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1184
- webscout/webscout_search_async.py +0 -654
- webscout/yep_search.py +0 -347
- webscout/zeroart/README.md +0 -89
- webscout-8.2.9.dist-info/METADATA +0 -1033
- webscout-8.2.9.dist-info/RECORD +0 -289
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.9.dist-info → webscout-2026.1.19.dist-info}/top_level.txt +0 -0
|
@@ -1,355 +1,419 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from .
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
#
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Any, Dict, Generator, List, Optional, Union, cast
|
|
5
|
+
|
|
6
|
+
from curl_cffi import CurlError
|
|
7
|
+
|
|
8
|
+
# Import curl_cffi for better request handling
|
|
9
|
+
from curl_cffi.requests import Session
|
|
10
|
+
|
|
11
|
+
# Import base classes and utility structures
|
|
12
|
+
from webscout.Provider.OPENAI.base import (
|
|
13
|
+
BaseChat,
|
|
14
|
+
BaseCompletions,
|
|
15
|
+
OpenAICompatibleProvider,
|
|
16
|
+
SimpleModelList,
|
|
17
|
+
)
|
|
18
|
+
from webscout.Provider.OPENAI.utils import (
|
|
19
|
+
ChatCompletion,
|
|
20
|
+
ChatCompletionChunk,
|
|
21
|
+
ChatCompletionMessage,
|
|
22
|
+
Choice,
|
|
23
|
+
ChoiceDelta,
|
|
24
|
+
CompletionUsage,
|
|
25
|
+
count_tokens, # Import format_prompt, get_system_prompt and count_tokens
|
|
26
|
+
format_prompt,
|
|
27
|
+
get_system_prompt,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Import LitAgent for browser fingerprinting
|
|
31
|
+
from ...litagent import LitAgent
|
|
32
|
+
|
|
33
|
+
# ANSI escape codes for formatting
|
|
34
|
+
BOLD = "\033[1m"
|
|
35
|
+
RED = "\033[91m"
|
|
36
|
+
RESET = "\033[0m"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Completions(BaseCompletions):
|
|
40
|
+
def __init__(self, client: "TypefullyAI"):
|
|
41
|
+
self._client = client
|
|
42
|
+
|
|
43
|
+
def create(
|
|
44
|
+
self,
|
|
45
|
+
*,
|
|
46
|
+
model: str,
|
|
47
|
+
messages: List[Dict[str, str]],
|
|
48
|
+
max_tokens: Optional[int] = None,
|
|
49
|
+
stream: bool = False,
|
|
50
|
+
temperature: Optional[float] = None,
|
|
51
|
+
timeout: Optional[int] = None,
|
|
52
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
53
|
+
**kwargs: Any,
|
|
54
|
+
) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
|
|
55
|
+
"""
|
|
56
|
+
Creates a model response for the given chat conversation.
|
|
57
|
+
Mimics openai.chat.completions.create
|
|
58
|
+
"""
|
|
59
|
+
# Extract system message using get_system_prompt utility
|
|
60
|
+
system_prompt = get_system_prompt(messages) or getattr(
|
|
61
|
+
self._client, "system_prompt", "You are a helpful assistant."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Format the conversation using format_prompt utility
|
|
65
|
+
# Use add_special_tokens=True to format as "User: ... Assistant: ..."
|
|
66
|
+
# Use do_continue=True to ensure it ends with "Assistant: " for model to continue
|
|
67
|
+
conversation_prompt = format_prompt(
|
|
68
|
+
messages,
|
|
69
|
+
add_special_tokens=True,
|
|
70
|
+
do_continue=True,
|
|
71
|
+
include_system=False, # System prompt is sent separately
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Prepare the payload for Typefully API
|
|
75
|
+
payload = {
|
|
76
|
+
"prompt": conversation_prompt,
|
|
77
|
+
"systemPrompt": system_prompt,
|
|
78
|
+
"modelIdentifier": self._client.convert_model_name(model),
|
|
79
|
+
"outputLength": max_tokens if max_tokens is not None else self._client.output_length,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
request_id = f"chatcmpl-{uuid.uuid4()}"
|
|
83
|
+
created_time = int(time.time())
|
|
84
|
+
|
|
85
|
+
if stream:
|
|
86
|
+
return self._create_streaming(
|
|
87
|
+
request_id, created_time, model, payload, timeout, proxies
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
return self._create_non_streaming(
|
|
91
|
+
request_id, created_time, model, payload, timeout, proxies
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _create_streaming(
|
|
95
|
+
self,
|
|
96
|
+
request_id: str,
|
|
97
|
+
created_time: int,
|
|
98
|
+
model: str,
|
|
99
|
+
payload: Dict[str, Any],
|
|
100
|
+
timeout: Optional[int] = None,
|
|
101
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
102
|
+
) -> Generator[ChatCompletionChunk, None, None]:
|
|
103
|
+
"""Implementation for streaming chat completions."""
|
|
104
|
+
try:
|
|
105
|
+
from webscout.sanitize import sanitize_stream
|
|
106
|
+
|
|
107
|
+
# Make the streaming request
|
|
108
|
+
response = self._client.session.post(
|
|
109
|
+
self._client.api_endpoint,
|
|
110
|
+
headers=self._client.headers,
|
|
111
|
+
json=payload,
|
|
112
|
+
stream=True,
|
|
113
|
+
timeout=timeout or self._client.timeout,
|
|
114
|
+
proxies=proxies or getattr(self._client, "proxies", None),
|
|
115
|
+
impersonate="chrome120",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if not response.ok:
|
|
119
|
+
raise IOError(
|
|
120
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
streaming_text = ""
|
|
124
|
+
|
|
125
|
+
processed_stream = sanitize_stream(
|
|
126
|
+
data=response.iter_lines(),
|
|
127
|
+
intro_value="data:",
|
|
128
|
+
to_json=True,
|
|
129
|
+
skip_markers=["[DONE]"],
|
|
130
|
+
content_extractor=self._client._typefully_extractor,
|
|
131
|
+
yield_raw_on_error=False,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
for content in processed_stream:
|
|
135
|
+
if content and isinstance(content, str):
|
|
136
|
+
streaming_text += content
|
|
137
|
+
|
|
138
|
+
# Create the delta object
|
|
139
|
+
delta = ChoiceDelta(content=content, role="assistant")
|
|
140
|
+
|
|
141
|
+
# Create the choice object
|
|
142
|
+
choice = Choice(index=0, delta=delta, finish_reason=None)
|
|
143
|
+
|
|
144
|
+
# Create the chunk object
|
|
145
|
+
chunk = ChatCompletionChunk(
|
|
146
|
+
id=request_id, choices=[choice], created=created_time, model=model
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
yield chunk
|
|
150
|
+
|
|
151
|
+
# Final chunk with finish_reason="stop"
|
|
152
|
+
delta = ChoiceDelta(content=None, role=None)
|
|
153
|
+
|
|
154
|
+
choice = Choice(index=0, delta=delta, finish_reason="stop")
|
|
155
|
+
|
|
156
|
+
chunk = ChatCompletionChunk(
|
|
157
|
+
id=request_id, choices=[choice], created=created_time, model=model
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
yield chunk
|
|
161
|
+
|
|
162
|
+
except CurlError as e:
|
|
163
|
+
raise IOError(f"Typefully streaming request failed (CurlError): {e}") from e
|
|
164
|
+
except Exception as e:
|
|
165
|
+
raise IOError(f"Typefully streaming request failed: {e}") from e
|
|
166
|
+
|
|
167
|
+
def _create_non_streaming(
|
|
168
|
+
self,
|
|
169
|
+
request_id: str,
|
|
170
|
+
created_time: int,
|
|
171
|
+
model: str,
|
|
172
|
+
payload: Dict[str, Any],
|
|
173
|
+
timeout: Optional[int] = None,
|
|
174
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
175
|
+
) -> ChatCompletion:
|
|
176
|
+
"""Implementation for non-streaming chat completions."""
|
|
177
|
+
try:
|
|
178
|
+
from webscout.sanitize import sanitize_stream
|
|
179
|
+
|
|
180
|
+
# Make the non-streaming request
|
|
181
|
+
response = self._client.session.post(
|
|
182
|
+
self._client.api_endpoint,
|
|
183
|
+
headers=self._client.headers,
|
|
184
|
+
json=payload,
|
|
185
|
+
stream=True,
|
|
186
|
+
timeout=timeout or self._client.timeout,
|
|
187
|
+
proxies=proxies or getattr(self._client, "proxies", None),
|
|
188
|
+
impersonate="chrome120",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if not response.ok:
|
|
192
|
+
raise IOError(
|
|
193
|
+
f"Failed to generate response - ({response.status_code}, {response.reason}) - {response.text}"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Collect the full response using sanitize_stream
|
|
197
|
+
full_text = ""
|
|
198
|
+
processed_stream = sanitize_stream(
|
|
199
|
+
data=response.iter_lines(),
|
|
200
|
+
intro_value="data:",
|
|
201
|
+
to_json=True,
|
|
202
|
+
skip_markers=["[DONE]"],
|
|
203
|
+
content_extractor=self._client._typefully_extractor,
|
|
204
|
+
yield_raw_on_error=False,
|
|
205
|
+
)
|
|
206
|
+
for content in processed_stream:
|
|
207
|
+
if content and isinstance(content, str):
|
|
208
|
+
full_text += content
|
|
209
|
+
|
|
210
|
+
# Format the text (replace escaped newlines)
|
|
211
|
+
full_text = full_text.replace("\\n", "\n").replace("\\n\\n", "\n\n")
|
|
212
|
+
|
|
213
|
+
# Estimate token counts
|
|
214
|
+
prompt_tokens = count_tokens(payload.get("prompt", "")) + count_tokens(
|
|
215
|
+
payload.get("systemPrompt", "")
|
|
216
|
+
)
|
|
217
|
+
completion_tokens = count_tokens(full_text)
|
|
218
|
+
total_tokens = prompt_tokens + completion_tokens
|
|
219
|
+
|
|
220
|
+
# Create the message object
|
|
221
|
+
message = ChatCompletionMessage(role="assistant", content=full_text)
|
|
222
|
+
|
|
223
|
+
# Create the choice object
|
|
224
|
+
choice = Choice(index=0, message=message, finish_reason="stop")
|
|
225
|
+
|
|
226
|
+
# Create the usage object
|
|
227
|
+
usage = CompletionUsage(
|
|
228
|
+
prompt_tokens=prompt_tokens,
|
|
229
|
+
completion_tokens=completion_tokens,
|
|
230
|
+
total_tokens=total_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
|
+
print(f"{RED}Error during Typefully non-streaming request (CurlError): {e}{RESET}")
|
|
246
|
+
raise IOError(f"Typefully request failed (CurlError): {e}") from e
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(f"{RED}Error during Typefully non-streaming request: {e}{RESET}")
|
|
249
|
+
raise IOError(f"Typefully request failed: {e}") from e
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class Chat(BaseChat):
|
|
253
|
+
def __init__(self, client: "TypefullyAI"):
|
|
254
|
+
self.completions = Completions(client)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class TypefullyAI(OpenAICompatibleProvider):
|
|
258
|
+
"""
|
|
259
|
+
OpenAI-compatible client for Typefully AI API.
|
|
260
|
+
|
|
261
|
+
Usage:
|
|
262
|
+
client = TypefullyAI()
|
|
263
|
+
response = client.chat.completions.create(
|
|
264
|
+
model="openai:gpt-4o-mini",
|
|
265
|
+
messages=[{"role": "user", "content": "Hello!"}]
|
|
266
|
+
)
|
|
267
|
+
print(response.choices[0].message.content)
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
required_auth = False
|
|
271
|
+
AVAILABLE_MODELS = [
|
|
272
|
+
"openai:gpt-4o-mini",
|
|
273
|
+
"openai:gpt-4o",
|
|
274
|
+
"anthropic:claude-haiku-4-5-20251001",
|
|
275
|
+
"groq:llama-3.3-70b-versatile",
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
def __init__(
|
|
279
|
+
self,
|
|
280
|
+
timeout: int = 30,
|
|
281
|
+
system_prompt: str = "You are a helpful assistant.",
|
|
282
|
+
output_length: int = 2048,
|
|
283
|
+
proxies: Optional[Dict[str, str]] = None,
|
|
284
|
+
):
|
|
285
|
+
"""
|
|
286
|
+
Initialize the TypefullyAI client.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
timeout: Request timeout in seconds
|
|
290
|
+
proxies: Optional proxy configuration
|
|
291
|
+
system_prompt: Default system prompt
|
|
292
|
+
output_length: Maximum length of the generated output
|
|
293
|
+
"""
|
|
294
|
+
self.timeout = timeout
|
|
295
|
+
self.system_prompt = system_prompt
|
|
296
|
+
self.output_length = output_length
|
|
297
|
+
self.proxies = proxies
|
|
298
|
+
self.api_endpoint = "https://typefully.com/tools/ai/api/completion"
|
|
299
|
+
|
|
300
|
+
# Initialize curl_cffi Session
|
|
301
|
+
self.session = Session()
|
|
302
|
+
|
|
303
|
+
# Initialize LitAgent for user agent generation
|
|
304
|
+
agent = LitAgent()
|
|
305
|
+
self.user_agent = agent.random()
|
|
306
|
+
|
|
307
|
+
# Set headers
|
|
308
|
+
self.headers = {
|
|
309
|
+
"authority": "typefully.com",
|
|
310
|
+
"accept": "*/*",
|
|
311
|
+
"accept-encoding": "gzip, deflate, br, zstd",
|
|
312
|
+
"accept-language": "en-US,en;q=0.9",
|
|
313
|
+
"content-type": "application/json",
|
|
314
|
+
"dnt": "1",
|
|
315
|
+
"origin": "https://typefully.com",
|
|
316
|
+
"referer": "https://typefully.com/tools/ai/chat-gpt-alternative",
|
|
317
|
+
"sec-ch-ua": '"Microsoft Edge";v="135", "Not-A.Brand";v="8", "Chromium";v="135"',
|
|
318
|
+
"sec-ch-ua-mobile": "?0",
|
|
319
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
320
|
+
"user-agent": self.user_agent,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# Update curl_cffi session headers and proxies
|
|
324
|
+
self.session.headers.update(self.headers)
|
|
325
|
+
|
|
326
|
+
# Initialize chat interface
|
|
327
|
+
self.chat = Chat(self)
|
|
328
|
+
|
|
329
|
+
@staticmethod
|
|
330
|
+
def _typefully_extractor(chunk) -> str:
|
|
331
|
+
"""Extracts content from Typefully AI SSE format."""
|
|
332
|
+
|
|
333
|
+
# Handle parsed JSON objects (when to_json=True)
|
|
334
|
+
if isinstance(chunk, dict):
|
|
335
|
+
data = chunk
|
|
336
|
+
elif isinstance(chunk, str):
|
|
337
|
+
# Handle raw strings (when to_json=False or direct strings)
|
|
338
|
+
line = chunk.strip()
|
|
339
|
+
if line.startswith("data: "):
|
|
340
|
+
line = line[6:] # Remove 'data: ' prefix
|
|
341
|
+
|
|
342
|
+
try:
|
|
343
|
+
# Parse JSON content
|
|
344
|
+
data = json.loads(line)
|
|
345
|
+
except json.JSONDecodeError:
|
|
346
|
+
# If JSON parsing fails, return empty for non-JSON content
|
|
347
|
+
return ""
|
|
348
|
+
else:
|
|
349
|
+
# For non-SSE lines, return empty
|
|
350
|
+
return ""
|
|
351
|
+
else:
|
|
352
|
+
# For other types (bytes, etc.), return empty
|
|
353
|
+
return ""
|
|
354
|
+
|
|
355
|
+
# Extract delta content for text chunks
|
|
356
|
+
if data.get("type") == "text-delta" and "delta" in data:
|
|
357
|
+
return data["delta"]
|
|
358
|
+
elif data.get("type") == "text-start":
|
|
359
|
+
# Return empty for text-start to avoid duplication
|
|
360
|
+
return ""
|
|
361
|
+
elif data.get("type") == "text-end":
|
|
362
|
+
# Return empty for text-end to avoid duplication
|
|
363
|
+
return ""
|
|
364
|
+
else:
|
|
365
|
+
# Return empty for other event types
|
|
366
|
+
return ""
|
|
367
|
+
|
|
368
|
+
def convert_model_name(self, model: str) -> str:
|
|
369
|
+
"""
|
|
370
|
+
Convert model names to ones supported by Typefully.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
model: Model name to convert
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
Typefully model name
|
|
377
|
+
"""
|
|
378
|
+
# If the model is already a valid Typefully model, return it
|
|
379
|
+
if model in self.AVAILABLE_MODELS:
|
|
380
|
+
return model
|
|
381
|
+
|
|
382
|
+
# Map common OpenAI model names to Typefully models
|
|
383
|
+
model_mapping = {
|
|
384
|
+
"gpt-4o-mini": "openai:gpt-4o-mini",
|
|
385
|
+
"gpt-4o": "openai:gpt-4o",
|
|
386
|
+
"claude-3-5-haiku": "anthropic:claude-3-5-haiku-20241022",
|
|
387
|
+
"llama-3.3-70b": "groq:llama-3.3-70b-versatile",
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if model in model_mapping:
|
|
391
|
+
return model_mapping[model]
|
|
392
|
+
|
|
393
|
+
# Default to the most capable model
|
|
394
|
+
print(f"{RED}Warning: Unknown model '{model}'. Using 'openai:gpt-4o-mini' instead.{RESET}")
|
|
395
|
+
return "openai:gpt-4o-mini"
|
|
396
|
+
|
|
397
|
+
@property
|
|
398
|
+
def models(self) -> SimpleModelList:
|
|
399
|
+
return SimpleModelList(type(self).AVAILABLE_MODELS)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if __name__ == "__main__":
|
|
403
|
+
# Example usage
|
|
404
|
+
client = TypefullyAI()
|
|
405
|
+
|
|
406
|
+
response = client.chat.completions.create(
|
|
407
|
+
model="gpt-4o-mini",
|
|
408
|
+
messages=[
|
|
409
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
|
410
|
+
{"role": "user", "content": "Explain the theory of relativity in simple terms."},
|
|
411
|
+
],
|
|
412
|
+
max_tokens=150,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
if isinstance(response, ChatCompletion):
|
|
416
|
+
message = response.choices[0].message if response.choices else None
|
|
417
|
+
print(f"{BOLD}Response:{RESET} {message.content if message else ''}")
|
|
418
|
+
else:
|
|
419
|
+
print(f"{BOLD}Response:{RESET} {response}")
|