webscout 8.2.2__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 -143
- webscout/AIbase.py +247 -123
- webscout/AIutel.py +68 -132
- webscout/Bard.py +1072 -535
- webscout/Extra/GitToolkit/__init__.py +2 -2
- 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 -0
- 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 -0
- 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 -45
- 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 +189 -18
- webscout/Extra/__init__.py +2 -3
- webscout/Extra/gguf.py +1298 -682
- webscout/Extra/tempmail/README.md +488 -0
- 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 +237 -304
- webscout/Provider/AISEARCH/README.md +106 -0
- webscout/Provider/AISEARCH/__init__.py +16 -10
- webscout/Provider/AISEARCH/brave_search.py +298 -0
- webscout/Provider/AISEARCH/iask_search.py +130 -209
- webscout/Provider/AISEARCH/monica_search.py +200 -246
- webscout/Provider/AISEARCH/webpilotai_search.py +242 -281
- 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 -0
- webscout/Provider/ClaudeOnline.py +365 -0
- webscout/Provider/Cohere.py +232 -208
- webscout/Provider/DeepAI.py +367 -0
- webscout/Provider/Deepinfra.py +343 -173
- webscout/Provider/EssentialAI.py +217 -0
- webscout/Provider/ExaAI.py +274 -261
- webscout/Provider/Gemini.py +60 -54
- webscout/Provider/GithubChat.py +385 -367
- webscout/Provider/Gradient.py +286 -0
- webscout/Provider/Groq.py +556 -670
- webscout/Provider/HadadXYZ.py +323 -0
- webscout/Provider/HeckAI.py +392 -233
- webscout/Provider/HuggingFace.py +387 -0
- webscout/Provider/IBM.py +340 -0
- webscout/Provider/Jadve.py +317 -266
- webscout/Provider/K2Think.py +306 -0
- webscout/Provider/Koboldai.py +221 -381
- webscout/Provider/Netwrck.py +273 -228
- 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 -0
- webscout/Provider/OPENAI/TogetherAI.py +405 -0
- webscout/Provider/OPENAI/TwoAI.py +255 -0
- webscout/Provider/OPENAI/__init__.py +148 -25
- webscout/Provider/OPENAI/ai4chat.py +348 -0
- webscout/Provider/OPENAI/akashgpt.py +436 -0
- webscout/Provider/OPENAI/algion.py +303 -0
- webscout/Provider/OPENAI/ayle.py +365 -0
- webscout/Provider/OPENAI/base.py +253 -46
- webscout/Provider/OPENAI/cerebras.py +296 -0
- webscout/Provider/OPENAI/chatgpt.py +514 -193
- webscout/Provider/OPENAI/chatsandbox.py +233 -0
- webscout/Provider/OPENAI/deepinfra.py +403 -272
- webscout/Provider/OPENAI/e2b.py +2370 -1350
- webscout/Provider/OPENAI/elmo.py +278 -0
- webscout/Provider/OPENAI/exaai.py +186 -138
- webscout/Provider/OPENAI/freeassist.py +446 -0
- webscout/Provider/OPENAI/gradient.py +448 -0
- webscout/Provider/OPENAI/groq.py +380 -0
- webscout/Provider/OPENAI/hadadxyz.py +292 -0
- webscout/Provider/OPENAI/heckai.py +100 -104
- 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 -327
- webscout/Provider/OPENAI/meta.py +541 -0
- webscout/Provider/OPENAI/netwrck.py +110 -84
- webscout/Provider/OPENAI/nvidia.py +317 -0
- webscout/Provider/OPENAI/oivscode.py +348 -0
- webscout/Provider/OPENAI/openrouter.py +328 -0
- webscout/Provider/OPENAI/pydantic_imports.py +1 -0
- webscout/Provider/OPENAI/sambanova.py +397 -0
- webscout/Provider/OPENAI/sonus.py +126 -115
- webscout/Provider/OPENAI/textpollinations.py +218 -133
- webscout/Provider/OPENAI/toolbaz.py +136 -166
- webscout/Provider/OPENAI/typefully.py +419 -0
- webscout/Provider/OPENAI/typliai.py +279 -0
- webscout/Provider/OPENAI/utils.py +314 -211
- webscout/Provider/OPENAI/wisecat.py +103 -125
- webscout/Provider/OPENAI/writecream.py +185 -156
- webscout/Provider/OPENAI/x0gpt.py +227 -136
- webscout/Provider/OPENAI/zenmux.py +380 -0
- webscout/Provider/OpenRouter.py +386 -0
- webscout/Provider/Openai.py +337 -496
- webscout/Provider/PI.py +443 -344
- 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 -0
- webscout/Provider/TTI/__init__.py +37 -12
- webscout/Provider/TTI/base.py +147 -0
- webscout/Provider/TTI/claudeonline.py +393 -0
- webscout/Provider/TTI/magicstudio.py +292 -0
- webscout/Provider/TTI/miragic.py +180 -0
- webscout/Provider/TTI/pollinations.py +331 -0
- webscout/Provider/TTI/together.py +334 -0
- webscout/Provider/TTI/utils.py +14 -0
- webscout/Provider/TTS/README.md +186 -0
- webscout/Provider/TTS/__init__.py +43 -7
- webscout/Provider/TTS/base.py +523 -0
- 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 -0
- 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 -180
- webscout/Provider/TTS/streamElements.py +275 -333
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TextPollinationsAI.py +221 -121
- webscout/Provider/TogetherAI.py +450 -0
- webscout/Provider/TwoAI.py +309 -199
- webscout/Provider/TypliAI.py +311 -0
- webscout/Provider/UNFINISHED/ChatHub.py +219 -0
- webscout/Provider/{OPENAI/glider.py → UNFINISHED/ChutesAI.py} +160 -145
- webscout/Provider/UNFINISHED/GizAI.py +300 -0
- webscout/Provider/UNFINISHED/Marcus.py +218 -0
- webscout/Provider/UNFINISHED/Qodo.py +481 -0
- webscout/Provider/UNFINISHED/XenAI.py +330 -0
- webscout/Provider/{Youchat.py → UNFINISHED/Youchat.py} +64 -47
- 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 -0
- webscout/Provider/UNFINISHED/samurai.py +231 -0
- webscout/Provider/WiseCat.py +256 -196
- webscout/Provider/WrDoChat.py +390 -0
- webscout/Provider/__init__.py +115 -198
- webscout/Provider/ai4chat.py +181 -202
- webscout/Provider/akashgpt.py +330 -342
- webscout/Provider/cerebras.py +397 -242
- webscout/Provider/cleeai.py +236 -213
- webscout/Provider/elmo.py +291 -234
- webscout/Provider/geminiapi.py +343 -208
- webscout/Provider/julius.py +245 -223
- webscout/Provider/learnfastai.py +333 -266
- webscout/Provider/llama3mitril.py +230 -180
- webscout/Provider/llmchat.py +308 -213
- webscout/Provider/llmchatco.py +321 -311
- webscout/Provider/meta.py +996 -794
- webscout/Provider/oivscode.py +332 -0
- webscout/Provider/searchchat.py +316 -293
- webscout/Provider/sonus.py +264 -208
- webscout/Provider/toolbaz.py +359 -320
- webscout/Provider/turboseek.py +332 -219
- webscout/Provider/typefully.py +262 -280
- webscout/Provider/x0gpt.py +332 -256
- webscout/__init__.py +31 -38
- webscout/__main__.py +5 -5
- webscout/cli.py +585 -293
- webscout/client.py +1497 -0
- webscout/conversation.py +140 -565
- webscout/exceptions.py +383 -339
- 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 +32 -378
- webscout/prompt_manager.py +376 -274
- webscout/sanitize.py +1514 -0
- webscout/scout/README.md +452 -0
- webscout/scout/__init__.py +8 -8
- webscout/scout/core/__init__.py +7 -7
- webscout/scout/core/crawler.py +330 -140
- webscout/scout/core/scout.py +800 -568
- 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 -460
- 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 -809
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +574 -0
- webscout/swiftcli/core/context.py +98 -0
- webscout/swiftcli/core/group.py +268 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +243 -0
- webscout/swiftcli/decorators/options.py +247 -0
- webscout/swiftcli/decorators/output.py +392 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +134 -0
- webscout/swiftcli/plugins/manager.py +269 -0
- webscout/swiftcli/utils/__init__.py +58 -0
- webscout/swiftcli/utils/formatting.py +251 -0
- webscout/swiftcli/utils/parsing.py +368 -0
- 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 -55
- webscout/zeroart/base.py +70 -60
- webscout/zeroart/effects.py +155 -99
- webscout/zeroart/fonts.py +1799 -816
- webscout-2026.1.19.dist-info/METADATA +638 -0
- webscout-2026.1.19.dist-info/RECORD +312 -0
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info}/WHEEL +1 -1
- webscout-2026.1.19.dist-info/entry_points.txt +4 -0
- webscout-2026.1.19.dist-info/top_level.txt +1 -0
- inferno/__init__.py +0 -6
- inferno/__main__.py +0 -9
- inferno/cli.py +0 -6
- webscout/DWEBS.py +0 -477
- webscout/Extra/autocoder/__init__.py +0 -9
- webscout/Extra/autocoder/autocoder.py +0 -849
- webscout/Extra/autocoder/autocoder_utiles.py +0 -332
- webscout/LLM.py +0 -442
- webscout/Litlogger/__init__.py +0 -67
- webscout/Litlogger/core/__init__.py +0 -6
- webscout/Litlogger/core/level.py +0 -23
- webscout/Litlogger/core/logger.py +0 -165
- webscout/Litlogger/handlers/__init__.py +0 -12
- webscout/Litlogger/handlers/console.py +0 -33
- webscout/Litlogger/handlers/file.py +0 -143
- webscout/Litlogger/handlers/network.py +0 -173
- webscout/Litlogger/styles/__init__.py +0 -7
- webscout/Litlogger/styles/colors.py +0 -249
- webscout/Litlogger/styles/formats.py +0 -458
- webscout/Litlogger/styles/text.py +0 -87
- webscout/Litlogger/utils/__init__.py +0 -6
- webscout/Litlogger/utils/detectors.py +0 -153
- webscout/Litlogger/utils/formatters.py +0 -200
- webscout/Local/__init__.py +0 -12
- webscout/Local/__main__.py +0 -9
- webscout/Local/api.py +0 -576
- webscout/Local/cli.py +0 -516
- webscout/Local/config.py +0 -75
- webscout/Local/llm.py +0 -287
- webscout/Local/model_manager.py +0 -253
- webscout/Local/server.py +0 -721
- webscout/Local/utils.py +0 -93
- webscout/Provider/AI21.py +0 -177
- webscout/Provider/AISEARCH/DeepFind.py +0 -250
- webscout/Provider/AISEARCH/ISou.py +0 -256
- webscout/Provider/AISEARCH/felo_search.py +0 -228
- webscout/Provider/AISEARCH/genspark_search.py +0 -208
- webscout/Provider/AISEARCH/hika_search.py +0 -194
- webscout/Provider/AISEARCH/scira_search.py +0 -324
- webscout/Provider/Aitopia.py +0 -292
- webscout/Provider/AllenAI.py +0 -413
- webscout/Provider/Blackboxai.py +0 -229
- webscout/Provider/C4ai.py +0 -432
- webscout/Provider/ChatGPTClone.py +0 -226
- webscout/Provider/ChatGPTES.py +0 -237
- webscout/Provider/ChatGPTGratis.py +0 -194
- webscout/Provider/Chatify.py +0 -175
- webscout/Provider/Cloudflare.py +0 -273
- webscout/Provider/DeepSeek.py +0 -196
- webscout/Provider/ElectronHub.py +0 -709
- webscout/Provider/ExaChat.py +0 -342
- webscout/Provider/Free2GPT.py +0 -241
- webscout/Provider/GPTWeb.py +0 -193
- webscout/Provider/Glider.py +0 -211
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/HuggingFaceChat.py +0 -462
- webscout/Provider/Hunyuan.py +0 -272
- webscout/Provider/LambdaChat.py +0 -392
- webscout/Provider/Llama.py +0 -200
- webscout/Provider/Llama3.py +0 -204
- webscout/Provider/Marcus.py +0 -148
- webscout/Provider/OLLAMA.py +0 -396
- webscout/Provider/OPENAI/c4ai.py +0 -367
- webscout/Provider/OPENAI/chatgptclone.py +0 -460
- webscout/Provider/OPENAI/exachat.py +0 -433
- webscout/Provider/OPENAI/freeaichat.py +0 -352
- webscout/Provider/OPENAI/opkfc.py +0 -488
- webscout/Provider/OPENAI/scirachat.py +0 -463
- webscout/Provider/OPENAI/standardinput.py +0 -425
- webscout/Provider/OPENAI/typegpt.py +0 -346
- webscout/Provider/OPENAI/uncovrAI.py +0 -455
- webscout/Provider/OPENAI/venice.py +0 -413
- webscout/Provider/OPENAI/yep.py +0 -327
- webscout/Provider/OpenGPT.py +0 -199
- webscout/Provider/Perplexitylabs.py +0 -415
- webscout/Provider/Phind.py +0 -535
- webscout/Provider/PizzaGPT.py +0 -198
- webscout/Provider/Reka.py +0 -214
- webscout/Provider/StandardInput.py +0 -278
- webscout/Provider/TTI/AiForce/__init__.py +0 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +0 -224
- webscout/Provider/TTI/AiForce/sync_aiforce.py +0 -245
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +0 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +0 -181
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +0 -180
- webscout/Provider/TTI/ImgSys/__init__.py +0 -23
- webscout/Provider/TTI/ImgSys/async_imgsys.py +0 -202
- webscout/Provider/TTI/ImgSys/sync_imgsys.py +0 -195
- webscout/Provider/TTI/MagicStudio/__init__.py +0 -2
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +0 -111
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +0 -109
- webscout/Provider/TTI/Nexra/__init__.py +0 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +0 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +0 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +0 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +0 -311
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +0 -265
- webscout/Provider/TTI/aiarta/__init__.py +0 -2
- webscout/Provider/TTI/aiarta/async_aiarta.py +0 -482
- webscout/Provider/TTI/aiarta/sync_aiarta.py +0 -440
- webscout/Provider/TTI/artbit/__init__.py +0 -22
- webscout/Provider/TTI/artbit/async_artbit.py +0 -155
- webscout/Provider/TTI/artbit/sync_artbit.py +0 -148
- webscout/Provider/TTI/fastflux/__init__.py +0 -22
- webscout/Provider/TTI/fastflux/async_fastflux.py +0 -261
- webscout/Provider/TTI/fastflux/sync_fastflux.py +0 -252
- webscout/Provider/TTI/huggingface/__init__.py +0 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +0 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +0 -195
- webscout/Provider/TTI/piclumen/__init__.py +0 -23
- webscout/Provider/TTI/piclumen/async_piclumen.py +0 -268
- webscout/Provider/TTI/piclumen/sync_piclumen.py +0 -233
- webscout/Provider/TTI/pixelmuse/__init__.py +0 -4
- webscout/Provider/TTI/pixelmuse/async_pixelmuse.py +0 -249
- webscout/Provider/TTI/pixelmuse/sync_pixelmuse.py +0 -182
- webscout/Provider/TTI/talkai/__init__.py +0 -4
- webscout/Provider/TTI/talkai/async_talkai.py +0 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +0 -207
- webscout/Provider/TTS/gesserit.py +0 -127
- webscout/Provider/TeachAnything.py +0 -187
- webscout/Provider/Venice.py +0 -219
- webscout/Provider/VercelAI.py +0 -234
- webscout/Provider/WebSim.py +0 -228
- webscout/Provider/Writecream.py +0 -211
- webscout/Provider/WritingMate.py +0 -197
- webscout/Provider/aimathgpt.py +0 -189
- webscout/Provider/askmyai.py +0 -158
- webscout/Provider/asksteve.py +0 -203
- webscout/Provider/bagoodex.py +0 -145
- webscout/Provider/chatglm.py +0 -205
- webscout/Provider/copilot.py +0 -428
- webscout/Provider/freeaichat.py +0 -271
- webscout/Provider/gaurish.py +0 -244
- webscout/Provider/geminiprorealtime.py +0 -160
- webscout/Provider/granite.py +0 -187
- webscout/Provider/hermes.py +0 -219
- webscout/Provider/koala.py +0 -268
- webscout/Provider/labyrinth.py +0 -340
- webscout/Provider/lepton.py +0 -194
- webscout/Provider/llamatutor.py +0 -192
- webscout/Provider/multichat.py +0 -325
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/scira_chat.py +0 -277
- webscout/Provider/scnet.py +0 -187
- webscout/Provider/talkai.py +0 -194
- webscout/Provider/tutorai.py +0 -252
- webscout/Provider/typegpt.py +0 -232
- webscout/Provider/uncovr.py +0 -312
- webscout/Provider/yep.py +0 -376
- webscout/litprinter/__init__.py +0 -59
- webscout/scout/core.py +0 -881
- webscout/tempid.py +0 -128
- webscout/webscout_search.py +0 -1346
- webscout/webscout_search_async.py +0 -877
- webscout/yep_search.py +0 -297
- webscout-8.2.2.dist-info/METADATA +0 -734
- webscout-8.2.2.dist-info/RECORD +0 -309
- webscout-8.2.2.dist-info/entry_points.txt +0 -5
- webscout-8.2.2.dist-info/top_level.txt +0 -3
- webstoken/__init__.py +0 -30
- webstoken/classifier.py +0 -189
- webstoken/keywords.py +0 -216
- webstoken/language.py +0 -128
- webstoken/ner.py +0 -164
- webstoken/normalizer.py +0 -35
- webstoken/processor.py +0 -77
- webstoken/sentiment.py +0 -206
- webstoken/stemmer.py +0 -73
- webstoken/tagger.py +0 -60
- webstoken/tokenizer.py +0 -158
- {webscout-8.2.2.dist-info → webscout-2026.1.19.dist-info/licenses}/LICENSE.md +0 -0
webscout/conversation.py
CHANGED
|
@@ -1,565 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class
|
|
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
|
-
filepath:
|
|
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
|
-
Examples:
|
|
143
|
-
>>> chat = Conversation(max_tokens=500)
|
|
144
|
-
>>> trimmed = chat._Conversation__trim_chat_history("Hello! Hi!", "Intro")
|
|
145
|
-
"""
|
|
146
|
-
len_of_intro = len(intro)
|
|
147
|
-
len_of_chat_history = len(chat_history)
|
|
148
|
-
total = self.max_tokens_to_sample + len_of_intro + len_of_chat_history
|
|
149
|
-
|
|
150
|
-
if total > self.history_offset:
|
|
151
|
-
truncate_at = (total - self.history_offset) + self.prompt_allowance
|
|
152
|
-
trimmed_chat_history = chat_history[truncate_at:]
|
|
153
|
-
return "... " + trimmed_chat_history
|
|
154
|
-
return chat_history
|
|
155
|
-
|
|
156
|
-
def gen_complete_prompt(self, prompt: str, intro: Optional[str] = None) -> str:
|
|
157
|
-
"""Generate a complete prompt that's ready to go!
|
|
158
|
-
|
|
159
|
-
This method:
|
|
160
|
-
- Combines the intro, history, and new prompt
|
|
161
|
-
- Adds tools information if available
|
|
162
|
-
- Trims history if needed
|
|
163
|
-
- Keeps everything organized and flowing
|
|
164
|
-
|
|
165
|
-
Args:
|
|
166
|
-
prompt (str): Your message to add to the chat
|
|
167
|
-
intro (str, optional): Custom intro to use. Default: None (uses class intro)
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
str: The complete conversation prompt, ready for the LLM!
|
|
171
|
-
|
|
172
|
-
Examples:
|
|
173
|
-
>>> chat = Conversation()
|
|
174
|
-
>>> prompt = chat.gen_complete_prompt("What's good?")
|
|
175
|
-
"""
|
|
176
|
-
if not self.status:
|
|
177
|
-
return prompt
|
|
178
|
-
|
|
179
|
-
intro = intro or self.intro or (
|
|
180
|
-
'''You are a helpful and versatile AI assistant. Your goal is to provide concise and informative responses directly to user queries. Use available tools in correct format to enhance responses or execute actions as needed.
|
|
181
|
-
''')
|
|
182
|
-
|
|
183
|
-
# Add tool information if tools are available
|
|
184
|
-
tools_description = self.get_tools_description()
|
|
185
|
-
if tools_description:
|
|
186
|
-
try:
|
|
187
|
-
from datetime import datetime
|
|
188
|
-
date_str = f"Current date: {datetime.now().strftime('%d %b %Y')}"
|
|
189
|
-
except:
|
|
190
|
-
date_str = ""
|
|
191
|
-
|
|
192
|
-
intro = (f'''
|
|
193
|
-
{intro}
|
|
194
|
-
|
|
195
|
-
{date_str}
|
|
196
|
-
|
|
197
|
-
**CORE PROTOCOL:**
|
|
198
|
-
|
|
199
|
-
Your goal is to assist the user effectively. Analyze each query and choose one of two response modes:
|
|
200
|
-
|
|
201
|
-
**1. Tool Mode:**
|
|
202
|
-
- **When:** If the query requires external data, calculations, or functions listed under AVAILABLE TOOLS (e.g., web search, current info).
|
|
203
|
-
- **Action:** Output *ONLY* the complete JSON tool call, exactly matching the TOOL FORMAT below, enclosed in `<tool_call>` and `</tool_call>` tags.
|
|
204
|
-
- **CRITICAL:** Absolutely NO text, whitespace, or characters before `<tool_call>` or after `</tool_call>`. The output *must* start with `<tool_call>` and end with `</tool_call>`.
|
|
205
|
-
- **Example (Output is *only* this block):**
|
|
206
|
-
```json
|
|
207
|
-
<tool_call>
|
|
208
|
-
{{
|
|
209
|
-
"name": "search",
|
|
210
|
-
"arguments": {{ "query": "latest population of Tokyo" }}
|
|
211
|
-
}}
|
|
212
|
-
</tool_call>
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
**2. Conversational Mode:**
|
|
216
|
-
- **When:** If the query can be answered using your internal knowledge, is creative, or conversational.
|
|
217
|
-
- **Action:** Respond directly, clearly, and concisely.
|
|
218
|
-
- **Example:** *User:* "Explain photosynthesis." *Assistant:* "Photosynthesis is how plants use sunlight, water, and carbon dioxide to create their food (glucose) and release oxygen."
|
|
219
|
-
|
|
220
|
-
**ABSOLUTE PROHIBITIONS:**
|
|
221
|
-
- **NEVER Explain Tool Use:** Don't say you're using a tool, which one, or why.
|
|
222
|
-
- **NEVER Describe JSON/Tags:** Do not mention `tool_call`, JSON structure, or parameters.
|
|
223
|
-
- **NEVER Apologize for Tools:** No need to say sorry for lacking direct info.
|
|
224
|
-
- **NEVER Mix Text and Tool Calls:** Tool calls must be standalone.
|
|
225
|
-
|
|
226
|
-
**Be concise and relevant in all responses.**
|
|
227
|
-
|
|
228
|
-
**AVAILABLE TOOLS:**
|
|
229
|
-
{tools_description}
|
|
230
|
-
|
|
231
|
-
**TOOL FORMAT (Use Exactly):**
|
|
232
|
-
<tool_call>
|
|
233
|
-
{{
|
|
234
|
-
"name": "tool_name",
|
|
235
|
-
"arguments": {{
|
|
236
|
-
"param": "value"
|
|
237
|
-
/* Add other parameters as needed */
|
|
238
|
-
}}
|
|
239
|
-
}}
|
|
240
|
-
</tool_call>
|
|
241
|
-
|
|
242
|
-
**Summary Check:**
|
|
243
|
-
1. Tool needed? -> Output *only* the JSON in tags.
|
|
244
|
-
2. No tool needed? -> Respond directly and conversationally.
|
|
245
|
-
3. Avoid *all* prohibited explanations/text.
|
|
246
|
-
''')
|
|
247
|
-
|
|
248
|
-
incomplete_chat_history = self.chat_history + self.history_format % {
|
|
249
|
-
"user": prompt,
|
|
250
|
-
"llm": ""
|
|
251
|
-
}
|
|
252
|
-
complete_prompt = intro + self.__trim_chat_history(incomplete_chat_history, intro)
|
|
253
|
-
return complete_prompt
|
|
254
|
-
|
|
255
|
-
def update_chat_history(
|
|
256
|
-
self, prompt: str, response: str, force: bool = False
|
|
257
|
-
) -> None:
|
|
258
|
-
"""Keep the conversation flowing by updating the chat history!
|
|
259
|
-
|
|
260
|
-
This method:
|
|
261
|
-
- Adds new messages to the history
|
|
262
|
-
- Updates the file if needed
|
|
263
|
-
- Keeps everything organized
|
|
264
|
-
|
|
265
|
-
Args:
|
|
266
|
-
prompt (str): Your message to add
|
|
267
|
-
response (str): The LLM's response
|
|
268
|
-
force (bool): Force update even if history is off. Default: False
|
|
269
|
-
|
|
270
|
-
Examples:
|
|
271
|
-
>>> chat = Conversation()
|
|
272
|
-
>>> chat.update_chat_history("Hi!", "Hello there!")
|
|
273
|
-
"""
|
|
274
|
-
if not self.status and not force:
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
new_history = self.history_format % {"user": prompt, "llm": response}
|
|
278
|
-
|
|
279
|
-
if self.file and self.update_file:
|
|
280
|
-
# Create file if it doesn't exist
|
|
281
|
-
if not os.path.exists(self.file):
|
|
282
|
-
with open(self.file, "w", encoding="utf-8") as fh:
|
|
283
|
-
fh.write(self.intro + "\n")
|
|
284
|
-
|
|
285
|
-
# Append new history
|
|
286
|
-
with open(self.file, "a", encoding="utf-8") as fh:
|
|
287
|
-
fh.write(new_history)
|
|
288
|
-
|
|
289
|
-
self.chat_history += new_history
|
|
290
|
-
# logger.info(f"Chat history updated with prompt: {prompt}")
|
|
291
|
-
|
|
292
|
-
def update_chat_history_with_tool(
|
|
293
|
-
self, prompt: str, tool_name: str, tool_result: str, force: bool = False
|
|
294
|
-
) -> None:
|
|
295
|
-
"""Update chat history with a tool call and its result.
|
|
296
|
-
|
|
297
|
-
This method:
|
|
298
|
-
- Adds tool call interaction to the history
|
|
299
|
-
- Updates the file if needed
|
|
300
|
-
- Maintains the conversation flow with tools
|
|
301
|
-
|
|
302
|
-
Args:
|
|
303
|
-
prompt (str): The user's message that triggered the tool call
|
|
304
|
-
tool_name (str): Name of the tool that was called
|
|
305
|
-
tool_result (str): Result returned by the tool
|
|
306
|
-
force (bool): Force update even if history is off. Default: False
|
|
307
|
-
|
|
308
|
-
Examples:
|
|
309
|
-
>>> chat = Conversation()
|
|
310
|
-
>>> chat.update_chat_history_with_tool("What's the weather?", "weather_tool", "It's sunny, 75°F")
|
|
311
|
-
"""
|
|
312
|
-
if not self.status and not force:
|
|
313
|
-
return
|
|
314
|
-
|
|
315
|
-
new_history = self.tool_history_format % {
|
|
316
|
-
"user": prompt,
|
|
317
|
-
"tool": tool_name,
|
|
318
|
-
"result": tool_result
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if self.file and self.update_file:
|
|
322
|
-
# Create file if it doesn't exist
|
|
323
|
-
if not os.path.exists(self.file):
|
|
324
|
-
with open(self.file, "w", encoding="utf-8") as fh:
|
|
325
|
-
fh.write(self.intro + "\n")
|
|
326
|
-
|
|
327
|
-
# Append new history
|
|
328
|
-
with open(self.file, "a", encoding="utf-8") as fh:
|
|
329
|
-
fh.write(new_history)
|
|
330
|
-
|
|
331
|
-
self.chat_history += new_history
|
|
332
|
-
|
|
333
|
-
def add_message(self, role: str, content: str) -> None:
|
|
334
|
-
"""Add a new message to the chat - simple and clean!
|
|
335
|
-
|
|
336
|
-
This method:
|
|
337
|
-
- Validates the message role
|
|
338
|
-
- Adds the message to history
|
|
339
|
-
- Updates file if needed
|
|
340
|
-
|
|
341
|
-
Args:
|
|
342
|
-
role (str): Who's sending? ('user', 'llm', 'tool', or 'reasoning')
|
|
343
|
-
content (str): What's the message?
|
|
344
|
-
|
|
345
|
-
Examples:
|
|
346
|
-
>>> chat = Conversation()
|
|
347
|
-
>>> chat.add_message("user", "Hey there!")
|
|
348
|
-
>>> chat.add_message("llm", "Hi! How can I help?")
|
|
349
|
-
"""
|
|
350
|
-
if not self.validate_message(role, content):
|
|
351
|
-
raise ValueError("Invalid message role or content")
|
|
352
|
-
|
|
353
|
-
role_formats = {
|
|
354
|
-
"user": "User",
|
|
355
|
-
"llm": "LLM",
|
|
356
|
-
"tool": "Tool",
|
|
357
|
-
"reasoning": "Reasoning"
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if role in role_formats:
|
|
361
|
-
self.chat_history += f"\n{role_formats[role]} : {content}"
|
|
362
|
-
else:
|
|
363
|
-
raise ValueError(f"Invalid role: {role}. Must be one of {list(role_formats.keys())}")
|
|
364
|
-
|
|
365
|
-
# # Enhanced logging for message addition
|
|
366
|
-
# logger.info(f"Added message from {role}: {content}")
|
|
367
|
-
# logging.info(f"Message added: {role}: {content}")
|
|
368
|
-
|
|
369
|
-
def validate_message(self, role: str, content: str) -> bool:
|
|
370
|
-
"""Validate the message role and content."""
|
|
371
|
-
valid_roles = { 'user', 'llm', 'tool', 'reasoning', 'function_call' }
|
|
372
|
-
if role not in valid_roles:
|
|
373
|
-
logging.error(f"Invalid role: {role}")
|
|
374
|
-
return False
|
|
375
|
-
if not content:
|
|
376
|
-
logging.error("Content cannot be empty.")
|
|
377
|
-
return False
|
|
378
|
-
return True
|
|
379
|
-
|
|
380
|
-
def _parse_function_call(self, response: str) -> FunctionCallData:
|
|
381
|
-
"""Parse a function call from the LLM's response.
|
|
382
|
-
|
|
383
|
-
Args:
|
|
384
|
-
response (str): The LLM's response containing a function call
|
|
385
|
-
|
|
386
|
-
Returns:
|
|
387
|
-
FunctionCallData: Parsed function call data or error
|
|
388
|
-
"""
|
|
389
|
-
try:
|
|
390
|
-
# First try the standard format with square brackets: <tool_call>[...]</tool_call>
|
|
391
|
-
start_tag: str = "<tool_call>["
|
|
392
|
-
end_tag: str = "]</tool_call>"
|
|
393
|
-
start_idx: int = response.find(start_tag)
|
|
394
|
-
end_idx: int = response.rfind(end_tag)
|
|
395
|
-
|
|
396
|
-
# If not found, try the alternate format: <tool_call>\n{...}\n</tool_call>
|
|
397
|
-
if start_idx == -1 or end_idx == -1 or end_idx <= start_idx:
|
|
398
|
-
start_tag = "<tool_call>"
|
|
399
|
-
end_tag = "</tool_call>"
|
|
400
|
-
start_idx = response.find(start_tag)
|
|
401
|
-
end_idx = response.rfind(end_tag)
|
|
402
|
-
|
|
403
|
-
if start_idx == -1 or end_idx == -1 or end_idx <= start_idx:
|
|
404
|
-
raise ValueError("No valid <tool_call> JSON structure found in the response.")
|
|
405
|
-
|
|
406
|
-
# Extract JSON content - for the format without brackets
|
|
407
|
-
json_str: str = response[start_idx + len(start_tag):end_idx].strip()
|
|
408
|
-
|
|
409
|
-
# Try to parse the JSON directly
|
|
410
|
-
try:
|
|
411
|
-
parsed_response: Any = json.loads(json_str)
|
|
412
|
-
if isinstance(parsed_response, dict):
|
|
413
|
-
return {"tool_calls": [parsed_response]}
|
|
414
|
-
else:
|
|
415
|
-
raise ValueError("Invalid JSON structure in tool call.")
|
|
416
|
-
except json.JSONDecodeError:
|
|
417
|
-
# If direct parsing failed, try to extract just the JSON object
|
|
418
|
-
import re
|
|
419
|
-
json_pattern = re.search(r'\{[\s\S]*\}', json_str)
|
|
420
|
-
if json_pattern:
|
|
421
|
-
parsed_response = json.loads(json_pattern.group(0))
|
|
422
|
-
return {"tool_calls": [parsed_response]}
|
|
423
|
-
raise
|
|
424
|
-
else:
|
|
425
|
-
# Extract JSON content - for the format with brackets
|
|
426
|
-
json_str: str = response[start_idx + len(start_tag):end_idx].strip()
|
|
427
|
-
parsed_response: Any = json.loads(json_str)
|
|
428
|
-
|
|
429
|
-
if isinstance(parsed_response, list):
|
|
430
|
-
return {"tool_calls": parsed_response}
|
|
431
|
-
elif isinstance(parsed_response, dict):
|
|
432
|
-
return {"tool_calls": [parsed_response]}
|
|
433
|
-
else:
|
|
434
|
-
raise ValueError("<tool_call> should contain a list or a dictionary of tool calls.")
|
|
435
|
-
|
|
436
|
-
except (ValueError, json.JSONDecodeError) as e:
|
|
437
|
-
logging.error(f"Error parsing function call: %s", e)
|
|
438
|
-
return {"error": str(e)}
|
|
439
|
-
|
|
440
|
-
def execute_function(self, function_call_data: FunctionCallData) -> str:
|
|
441
|
-
"""Execute a function call and return the result.
|
|
442
|
-
|
|
443
|
-
Args:
|
|
444
|
-
function_call_data (FunctionCallData): The function call data
|
|
445
|
-
|
|
446
|
-
Returns:
|
|
447
|
-
str: Result of the function execution
|
|
448
|
-
"""
|
|
449
|
-
tool_calls: Optional[List[FunctionCall]] = function_call_data.get("tool_calls")
|
|
450
|
-
|
|
451
|
-
if not tool_calls or not isinstance(tool_calls, list):
|
|
452
|
-
return "Invalid tool_calls format."
|
|
453
|
-
|
|
454
|
-
results: List[str] = []
|
|
455
|
-
for tool_call in tool_calls:
|
|
456
|
-
function_name: str = tool_call.get("name")
|
|
457
|
-
arguments: Dict[str, Any] = tool_call.get("arguments", {})
|
|
458
|
-
|
|
459
|
-
if not function_name or not isinstance(arguments, dict):
|
|
460
|
-
results.append(f"Invalid tool call: {tool_call}")
|
|
461
|
-
continue
|
|
462
|
-
|
|
463
|
-
# Here you would implement the actual execution logic for each tool
|
|
464
|
-
# For demonstration, we'll return a placeholder response
|
|
465
|
-
results.append(f"Executed {function_name} with arguments {arguments}")
|
|
466
|
-
|
|
467
|
-
return "; ".join(results)
|
|
468
|
-
|
|
469
|
-
def _convert_fns_to_tools(self, fns: Optional[List[Fn]]) -> List[ToolDefinition]:
|
|
470
|
-
"""Convert functions to tool definitions for the LLM.
|
|
471
|
-
|
|
472
|
-
Args:
|
|
473
|
-
fns (Optional[List[Fn]]): List of function definitions
|
|
474
|
-
|
|
475
|
-
Returns:
|
|
476
|
-
List[ToolDefinition]: List of tool definitions
|
|
477
|
-
"""
|
|
478
|
-
if not fns:
|
|
479
|
-
return []
|
|
480
|
-
|
|
481
|
-
tools: List[ToolDefinition] = []
|
|
482
|
-
for fn in fns:
|
|
483
|
-
tool: ToolDefinition = {
|
|
484
|
-
"type": "function",
|
|
485
|
-
"function": {
|
|
486
|
-
"name": fn.name,
|
|
487
|
-
"description": fn.description,
|
|
488
|
-
"parameters": {
|
|
489
|
-
"type": "object",
|
|
490
|
-
"properties": {
|
|
491
|
-
param_name: {
|
|
492
|
-
"type": param_type,
|
|
493
|
-
"description": f"The {param_name} parameter"
|
|
494
|
-
} for param_name, param_type in fn.parameters.items()
|
|
495
|
-
},
|
|
496
|
-
"required": list(fn.parameters.keys())
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
tools.append(tool)
|
|
501
|
-
return tools
|
|
502
|
-
|
|
503
|
-
def get_tools_description(self) -> str:
|
|
504
|
-
"""Get a formatted string of available tools for the intro prompt.
|
|
505
|
-
|
|
506
|
-
Returns:
|
|
507
|
-
str: Formatted tools description
|
|
508
|
-
"""
|
|
509
|
-
if not self.tools:
|
|
510
|
-
return ""
|
|
511
|
-
|
|
512
|
-
tools_desc = []
|
|
513
|
-
for fn in self.tools:
|
|
514
|
-
params_desc = ", ".join([f"{name}: {typ}" for name, typ in fn.parameters.items()])
|
|
515
|
-
tools_desc.append(f"- {fn.name}: {fn.description} (Parameters: {params_desc})")
|
|
516
|
-
|
|
517
|
-
return "\n".join(tools_desc)
|
|
518
|
-
|
|
519
|
-
def handle_tool_response(self, response: str) -> Dict[str, Any]:
|
|
520
|
-
"""Process a response that might contain a tool call.
|
|
521
|
-
|
|
522
|
-
This method:
|
|
523
|
-
- Checks if the response contains a tool call
|
|
524
|
-
- Parses and executes the tool call if present
|
|
525
|
-
- Returns the appropriate result
|
|
526
|
-
|
|
527
|
-
Args:
|
|
528
|
-
response (str): The LLM's response
|
|
529
|
-
|
|
530
|
-
Returns:
|
|
531
|
-
Dict[str, Any]: Result containing 'is_tool_call', 'result', and 'original_response'
|
|
532
|
-
"""
|
|
533
|
-
# Check if response contains a tool call
|
|
534
|
-
if "<tool_call>" in response:
|
|
535
|
-
function_call_data = self._parse_function_call(response)
|
|
536
|
-
|
|
537
|
-
if "error" in function_call_data:
|
|
538
|
-
return {
|
|
539
|
-
"is_tool_call": True,
|
|
540
|
-
"success": False,
|
|
541
|
-
"result": function_call_data["error"],
|
|
542
|
-
"original_response": response
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
# Execute the function call
|
|
546
|
-
result = self.execute_function(function_call_data)
|
|
547
|
-
|
|
548
|
-
# Add the result to chat history as a tool message
|
|
549
|
-
self.add_message("tool", result)
|
|
550
|
-
|
|
551
|
-
return {
|
|
552
|
-
"is_tool_call": True,
|
|
553
|
-
"success": True,
|
|
554
|
-
"result": result,
|
|
555
|
-
"tool_calls": function_call_data.get("tool_calls", []),
|
|
556
|
-
"original_response": response
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
return {
|
|
560
|
-
"is_tool_call": False,
|
|
561
|
-
"result": response,
|
|
562
|
-
"original_response": response
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
|
|
1
|
+
"""
|
|
2
|
+
conversation.py
|
|
3
|
+
|
|
4
|
+
This module provides a modern conversation manager for handling chat-based interactions, message history, and robust error handling. It defines the Conversation class for managing conversational state.
|
|
5
|
+
|
|
6
|
+
Classes:
|
|
7
|
+
Conversation: Main conversation manager class.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from litprinter import ic
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Conversation:
|
|
16
|
+
"""Handles prompt generation based on history"""
|
|
17
|
+
intro: str
|
|
18
|
+
status: bool
|
|
19
|
+
max_tokens_to_sample: int
|
|
20
|
+
chat_history: str
|
|
21
|
+
history_format: str
|
|
22
|
+
file: Optional[str]
|
|
23
|
+
update_file: bool
|
|
24
|
+
history_offset: int
|
|
25
|
+
prompt_allowance: int
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
status: bool = True,
|
|
30
|
+
max_tokens: int = 600,
|
|
31
|
+
filepath: Optional[str] = None,
|
|
32
|
+
update_file: bool = True,
|
|
33
|
+
):
|
|
34
|
+
"""Initializes Conversation
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
status (bool, optional): Flag to control history. Defaults to True.
|
|
38
|
+
max_tokens (int, optional): Maximum number of tokens to be generated upon completion. Defaults to 600.
|
|
39
|
+
filepath (str, optional): Path to file containing conversation history. Defaults to None.
|
|
40
|
+
update_file (bool, optional): Add new prompts and responses to the file. Defaults to True.
|
|
41
|
+
"""
|
|
42
|
+
self.intro = (
|
|
43
|
+
"You're a Large Language Model for chatting with people. "
|
|
44
|
+
"Assume role of the LLM and give your response."
|
|
45
|
+
)
|
|
46
|
+
self.status = status
|
|
47
|
+
self.max_tokens_to_sample = max_tokens
|
|
48
|
+
self.chat_history = ""
|
|
49
|
+
self.history_format = "\nUser : %(user)s\nLLM :%(llm)s"
|
|
50
|
+
self.file = filepath
|
|
51
|
+
self.update_file = update_file
|
|
52
|
+
self.history_offset = 10250
|
|
53
|
+
self.prompt_allowance = 10
|
|
54
|
+
self.load_conversation(filepath, False) if filepath else None
|
|
55
|
+
|
|
56
|
+
def load_conversation(self, filepath: str, exists: bool = True) -> None:
|
|
57
|
+
"""Load conversation into chat's history from .txt file
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
filepath (str): Path to .txt file
|
|
61
|
+
exists (bool, optional): Flag for file availability. Defaults to True.
|
|
62
|
+
"""
|
|
63
|
+
assert isinstance(
|
|
64
|
+
filepath, str
|
|
65
|
+
), f"Filepath needs to be of str datatype not {type(filepath)}"
|
|
66
|
+
assert (
|
|
67
|
+
os.path.isfile(filepath) if exists else True
|
|
68
|
+
), f"File '{filepath}' does not exist"
|
|
69
|
+
if not os.path.isfile(filepath):
|
|
70
|
+
ic(f"Creating new chat-history file - '{filepath}'")
|
|
71
|
+
with open(filepath, "w") as fh: # Try creating new file
|
|
72
|
+
# lets add intro here
|
|
73
|
+
fh.write(self.intro)
|
|
74
|
+
else:
|
|
75
|
+
ic(f"Loading conversation from '{filepath}'")
|
|
76
|
+
with open(filepath) as fh:
|
|
77
|
+
file_contents = fh.readlines()
|
|
78
|
+
if file_contents:
|
|
79
|
+
self.intro = file_contents[0] # Presume first line is the intro.
|
|
80
|
+
self.chat_history = "\n".join(file_contents[1:])
|
|
81
|
+
|
|
82
|
+
def __trim_chat_history(self, chat_history: str, intro: str) -> str:
|
|
83
|
+
"""Ensures the len(prompt) and max_tokens_to_sample is not > 4096"""
|
|
84
|
+
len_of_intro = len(intro)
|
|
85
|
+
len_of_chat_history = len(chat_history)
|
|
86
|
+
total = (
|
|
87
|
+
self.max_tokens_to_sample + len_of_intro + len_of_chat_history
|
|
88
|
+
) # + self.max_tokens_to_sample
|
|
89
|
+
if total > self.history_offset:
|
|
90
|
+
truncate_at = (total - self.history_offset) + self.prompt_allowance
|
|
91
|
+
# Remove head of total (n) of chat_history
|
|
92
|
+
trimmed_chat_history = chat_history[truncate_at:]
|
|
93
|
+
return "... " + trimmed_chat_history
|
|
94
|
+
# print(len(self.chat_history))
|
|
95
|
+
else:
|
|
96
|
+
return chat_history
|
|
97
|
+
|
|
98
|
+
def gen_complete_prompt(self, prompt: str, intro: Optional[str] = None) -> str:
|
|
99
|
+
"""Generates a kinda like incomplete conversation
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
prompt (str): Chat prompt
|
|
103
|
+
intro (str): Override class' intro. Defaults to None.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
str: Updated incomplete chat_history
|
|
107
|
+
"""
|
|
108
|
+
if self.status:
|
|
109
|
+
intro = self.intro if intro is None else intro
|
|
110
|
+
incomplete_chat_history = self.chat_history + self.history_format % dict(
|
|
111
|
+
user=prompt, llm=""
|
|
112
|
+
)
|
|
113
|
+
return intro + self.__trim_chat_history(incomplete_chat_history, intro)
|
|
114
|
+
|
|
115
|
+
return prompt
|
|
116
|
+
|
|
117
|
+
def update_chat_history(
|
|
118
|
+
self, prompt: str, response: str, force: bool = False
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Updates chat history
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
prompt (str): user prompt
|
|
124
|
+
response (str): LLM response
|
|
125
|
+
force (bool, optional): Force update
|
|
126
|
+
"""
|
|
127
|
+
if not self.status and not force:
|
|
128
|
+
return
|
|
129
|
+
new_history = self.history_format % dict(user=prompt, llm=response)
|
|
130
|
+
if self.file and self.update_file:
|
|
131
|
+
if os.path.exists(self.file):
|
|
132
|
+
# Append new history to existing file
|
|
133
|
+
with open(self.file, "a") as fh:
|
|
134
|
+
fh.write(new_history)
|
|
135
|
+
else:
|
|
136
|
+
# Create new file with intro and new history
|
|
137
|
+
with open(self.file, "w") as fh:
|
|
138
|
+
fh.write(self.intro + "\n" + new_history)
|
|
139
|
+
self.chat_history += new_history
|
|
140
|
+
|