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
webscout/conversation.py
CHANGED
|
@@ -1,436 +1,140 @@
|
|
|
1
|
-
"""
|
|
2
|
-
conversation.py
|
|
3
|
-
|
|
4
|
-
This module provides a modern conversation manager for handling chat-based interactions, message history,
|
|
5
|
-
|
|
6
|
-
Classes:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
self.
|
|
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
|
-
with open(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def _process_history_from_file(self, lines: List[str]) -> None:
|
|
142
|
-
"""Process and structure conversation history from file."""
|
|
143
|
-
current_role = None
|
|
144
|
-
current_content = []
|
|
145
|
-
|
|
146
|
-
for line in lines:
|
|
147
|
-
line = line.strip()
|
|
148
|
-
if line.startswith(("User:", "Assistant:", "Tool:")):
|
|
149
|
-
if current_role and current_content:
|
|
150
|
-
self.messages.append(Message(
|
|
151
|
-
role=current_role,
|
|
152
|
-
content="\n".join(current_content)
|
|
153
|
-
))
|
|
154
|
-
current_content = []
|
|
155
|
-
current_role = line.split(":")[0].lower()
|
|
156
|
-
content = ":".join(line.split(":")[1:]).strip()
|
|
157
|
-
current_content.append(content)
|
|
158
|
-
elif line:
|
|
159
|
-
current_content.append(line)
|
|
160
|
-
|
|
161
|
-
if current_role and current_content:
|
|
162
|
-
self.messages.append(Message(
|
|
163
|
-
role=current_role,
|
|
164
|
-
content="\n".join(current_content)
|
|
165
|
-
))
|
|
166
|
-
|
|
167
|
-
def _compress_history(self) -> None:
|
|
168
|
-
"""Compress history when it exceeds threshold."""
|
|
169
|
-
if len(self.messages) > self.compression_threshold:
|
|
170
|
-
# Keep recent messages and summarize older ones
|
|
171
|
-
keep_recent = 100 # Adjust based on needs
|
|
172
|
-
self.messages = (
|
|
173
|
-
[self._summarize_messages(self.messages[:-keep_recent])] +
|
|
174
|
-
self.messages[-keep_recent:]
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
def _summarize_messages(self, messages: List[Message]) -> Message:
|
|
178
|
-
"""Create a summary message from older messages."""
|
|
179
|
-
return Message(
|
|
180
|
-
role="system",
|
|
181
|
-
content="[History Summary] Previous conversation summarized for context",
|
|
182
|
-
metadata={"summarized_count": len(messages)}
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
def gen_complete_prompt(self, prompt: str, intro: Optional[str] = None) -> str:
|
|
186
|
-
"""Generate complete prompt with enhanced context management."""
|
|
187
|
-
if not self.status:
|
|
188
|
-
return prompt
|
|
189
|
-
|
|
190
|
-
intro = intro or self.intro
|
|
191
|
-
|
|
192
|
-
# Add tool information if available
|
|
193
|
-
tools_description = self.get_tools_description()
|
|
194
|
-
if tools_description:
|
|
195
|
-
try:
|
|
196
|
-
date_str = f"Current date: {datetime.now().strftime('%d %b %Y')}"
|
|
197
|
-
except:
|
|
198
|
-
date_str = ""
|
|
199
|
-
|
|
200
|
-
intro = self._generate_enhanced_intro(intro, tools_description, date_str)
|
|
201
|
-
|
|
202
|
-
# Generate history string with proper formatting
|
|
203
|
-
history = self._generate_history_string()
|
|
204
|
-
|
|
205
|
-
# Combine and trim if needed
|
|
206
|
-
complete_prompt = intro + self._trim_chat_history(
|
|
207
|
-
history + "\nUser: " + prompt + "\nAssistant:",
|
|
208
|
-
intro
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
return complete_prompt
|
|
212
|
-
|
|
213
|
-
def _generate_enhanced_intro(self, intro: str, tools_description: str, date_str: str) -> str:
|
|
214
|
-
"""Generate enhanced introduction with tools and guidelines."""
|
|
215
|
-
return f'''
|
|
216
|
-
{intro}
|
|
217
|
-
|
|
218
|
-
{date_str}
|
|
219
|
-
|
|
220
|
-
**CORE PROTOCOL:**
|
|
221
|
-
|
|
222
|
-
Your goal is to assist the user effectively. Analyze each query and choose one of two response modes:
|
|
223
|
-
|
|
224
|
-
**1. Tool Mode:**
|
|
225
|
-
- **When:** If the query requires external data, calculations, or functions listed under AVAILABLE TOOLS.
|
|
226
|
-
- **Action:** Output *ONLY* the complete JSON tool call within tags.
|
|
227
|
-
- **Format:** Must start with `<tool_call>` and end with `</tool_call>`.
|
|
228
|
-
|
|
229
|
-
**2. Conversational Mode:**
|
|
230
|
-
- **When:** For queries answerable with internal knowledge.
|
|
231
|
-
- **Action:** Respond directly and concisely.
|
|
232
|
-
|
|
233
|
-
**AVAILABLE TOOLS:**
|
|
234
|
-
{tools_description}
|
|
235
|
-
|
|
236
|
-
**TOOL FORMAT:**
|
|
237
|
-
<tool_call>
|
|
238
|
-
{{
|
|
239
|
-
"name": "tool_name",
|
|
240
|
-
"arguments": {{
|
|
241
|
-
"param": "value"
|
|
242
|
-
}}
|
|
243
|
-
}}
|
|
244
|
-
</tool_call>
|
|
245
|
-
'''
|
|
246
|
-
|
|
247
|
-
def _generate_history_string(self) -> str:
|
|
248
|
-
"""Generate formatted history string from messages."""
|
|
249
|
-
history_parts = []
|
|
250
|
-
for msg in self.messages:
|
|
251
|
-
if msg.role == "system" and msg.metadata.get("summarized_count"):
|
|
252
|
-
history_parts.append(f"[Previous messages summarized: {msg.metadata['summarized_count']}]")
|
|
253
|
-
else:
|
|
254
|
-
role_display = msg.role.capitalize()
|
|
255
|
-
if "<tool_call>" in msg.content:
|
|
256
|
-
history_parts.append(f"{role_display}: {msg.content}")
|
|
257
|
-
else:
|
|
258
|
-
history_parts.append(f"{role_display}: {msg.content}")
|
|
259
|
-
return "\n".join(history_parts)
|
|
260
|
-
|
|
261
|
-
def _trim_chat_history(self, chat_history: str, intro: str) -> str:
|
|
262
|
-
"""Trim chat history with improved token management."""
|
|
263
|
-
total_length = len(intro) + len(chat_history)
|
|
264
|
-
|
|
265
|
-
if total_length > self.history_offset:
|
|
266
|
-
truncate_at = (total_length - self.history_offset) + self.prompt_allowance
|
|
267
|
-
# Try to truncate at a message boundary
|
|
268
|
-
lines = chat_history[truncate_at:].split('\n')
|
|
269
|
-
for i, line in enumerate(lines):
|
|
270
|
-
if line.startswith(("User:", "Assistant:", "Tool:")):
|
|
271
|
-
return "... " + "\n".join(lines[i:])
|
|
272
|
-
return "... " + chat_history[truncate_at:]
|
|
273
|
-
return chat_history
|
|
274
|
-
|
|
275
|
-
def add_message(self, role: str, content: str, metadata: Optional[Dict[str, Any]] = None) -> None:
|
|
276
|
-
"""Add a message with enhanced validation and metadata support."""
|
|
277
|
-
try:
|
|
278
|
-
role = role.lower() # Normalize role to lowercase
|
|
279
|
-
if not self.validate_message(role, content):
|
|
280
|
-
raise MessageValidationError("Invalid message role or content")
|
|
281
|
-
|
|
282
|
-
message = Message(role=role, content=content, metadata=metadata or {})
|
|
283
|
-
self.messages.append(message)
|
|
284
|
-
|
|
285
|
-
if self.file and self.update_file:
|
|
286
|
-
self._append_to_file(message)
|
|
287
|
-
|
|
288
|
-
self._compress_history()
|
|
289
|
-
|
|
290
|
-
except Exception as e:
|
|
291
|
-
raise ConversationError(f"Failed to add message: {str(e)}") from e
|
|
292
|
-
|
|
293
|
-
def _append_to_file(self, message: Message) -> None:
|
|
294
|
-
"""Append message to file with error handling."""
|
|
295
|
-
try:
|
|
296
|
-
if not os.path.exists(self.file):
|
|
297
|
-
with open(self.file, "w", encoding="utf-8") as fh:
|
|
298
|
-
fh.write(self.intro + "\n")
|
|
299
|
-
|
|
300
|
-
with open(self.file, "a", encoding="utf-8") as fh:
|
|
301
|
-
role_display = message.role.capitalize()
|
|
302
|
-
fh.write(f"\n{role_display}: {message.content}")
|
|
303
|
-
|
|
304
|
-
except Exception as e:
|
|
305
|
-
raise ConversationError(f"Failed to write to file: {str(e)}") from e
|
|
306
|
-
|
|
307
|
-
def validate_message(self, role: str, content: str) -> bool:
|
|
308
|
-
"""Validate message with enhanced role checking."""
|
|
309
|
-
valid_roles = {'user', 'assistant', 'tool', 'system'}
|
|
310
|
-
if role not in valid_roles:
|
|
311
|
-
return False
|
|
312
|
-
if not isinstance(content, str):
|
|
313
|
-
return False
|
|
314
|
-
# Allow empty content for assistant (needed for streaming)
|
|
315
|
-
if not content and role != 'assistant':
|
|
316
|
-
return False
|
|
317
|
-
return True
|
|
318
|
-
|
|
319
|
-
def handle_tool_response(self, response: str) -> Dict[str, Any]:
|
|
320
|
-
"""Process tool responses with enhanced error handling."""
|
|
321
|
-
try:
|
|
322
|
-
if "<tool_call>" in response:
|
|
323
|
-
function_call_data = self._parse_function_call(response)
|
|
324
|
-
|
|
325
|
-
if "error" in function_call_data:
|
|
326
|
-
return {
|
|
327
|
-
"is_tool_call": True,
|
|
328
|
-
"success": False,
|
|
329
|
-
"result": function_call_data["error"],
|
|
330
|
-
"original_response": response
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
result = self.execute_function(function_call_data)
|
|
334
|
-
self.add_message("tool", result)
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
"is_tool_call": True,
|
|
338
|
-
"success": True,
|
|
339
|
-
"result": result,
|
|
340
|
-
"tool_calls": function_call_data.get("tool_calls", []),
|
|
341
|
-
"original_response": response
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
"is_tool_call": False,
|
|
346
|
-
"result": response,
|
|
347
|
-
"original_response": response
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
except Exception as e:
|
|
351
|
-
raise ToolCallError(f"Failed to handle tool response: {str(e)}") from e
|
|
352
|
-
|
|
353
|
-
def _parse_function_call(self, response: str) -> FunctionCallData:
|
|
354
|
-
"""Parse function calls with improved JSON handling."""
|
|
355
|
-
try:
|
|
356
|
-
# Extract content between tool call tags
|
|
357
|
-
start_tag = "<tool_call>"
|
|
358
|
-
end_tag = "</tool_call>"
|
|
359
|
-
start_idx = response.find(start_tag)
|
|
360
|
-
end_idx = response.rfind(end_tag)
|
|
361
|
-
|
|
362
|
-
if start_idx == -1 or end_idx == -1:
|
|
363
|
-
raise ValueError("No valid tool call tags found")
|
|
364
|
-
|
|
365
|
-
json_str = response[start_idx + len(start_tag):end_idx].strip()
|
|
366
|
-
|
|
367
|
-
# Handle both single and multiple tool calls
|
|
368
|
-
try:
|
|
369
|
-
parsed = json.loads(json_str)
|
|
370
|
-
if isinstance(parsed, dict):
|
|
371
|
-
return {"tool_calls": [parsed]}
|
|
372
|
-
elif isinstance(parsed, list):
|
|
373
|
-
return {"tool_calls": parsed}
|
|
374
|
-
else:
|
|
375
|
-
raise ValueError("Invalid tool call structure")
|
|
376
|
-
except json.JSONDecodeError:
|
|
377
|
-
# Try to extract valid JSON if embedded in other content
|
|
378
|
-
import re
|
|
379
|
-
json_pattern = re.search(r'\{[\s\S]*\}', json_str)
|
|
380
|
-
if json_pattern:
|
|
381
|
-
parsed = json.loads(json_pattern.group(0))
|
|
382
|
-
return {"tool_calls": [parsed]}
|
|
383
|
-
raise
|
|
384
|
-
|
|
385
|
-
except Exception as e:
|
|
386
|
-
return {"error": str(e)}
|
|
387
|
-
|
|
388
|
-
def execute_function(self, function_call_data: FunctionCallData) -> str:
|
|
389
|
-
"""Execute functions with enhanced error handling."""
|
|
390
|
-
try:
|
|
391
|
-
tool_calls = function_call_data.get("tool_calls", [])
|
|
392
|
-
if not tool_calls:
|
|
393
|
-
raise ValueError("No tool calls provided")
|
|
394
|
-
|
|
395
|
-
results = []
|
|
396
|
-
for tool_call in tool_calls:
|
|
397
|
-
name = tool_call.get("name")
|
|
398
|
-
arguments = tool_call.get("arguments", {})
|
|
399
|
-
|
|
400
|
-
if not name or not isinstance(arguments, dict):
|
|
401
|
-
raise ValueError(f"Invalid tool call format: {tool_call}")
|
|
402
|
-
|
|
403
|
-
# Execute the tool (implement actual logic here)
|
|
404
|
-
results.append(f"Executed {name} with arguments {arguments}")
|
|
405
|
-
|
|
406
|
-
return "; ".join(results)
|
|
407
|
-
|
|
408
|
-
except Exception as e:
|
|
409
|
-
raise ToolCallError(f"Failed to execute function: {str(e)}") from e
|
|
410
|
-
|
|
411
|
-
def get_tools_description(self) -> str:
|
|
412
|
-
"""Get formatted tools description."""
|
|
413
|
-
if not self.tools:
|
|
414
|
-
return ""
|
|
415
|
-
|
|
416
|
-
return "\n".join(
|
|
417
|
-
f"- {fn.name}: {fn.description} (Parameters: {', '.join(f'{name}: {typ}' for name, typ in fn.parameters.items())})"
|
|
418
|
-
for fn in self.tools
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
def update_chat_history(self, prompt: str, response: str) -> None:
|
|
422
|
-
"""Update chat history with a new prompt-response pair.
|
|
423
|
-
|
|
424
|
-
Args:
|
|
425
|
-
prompt: The user's prompt/question
|
|
426
|
-
response: The assistant's response
|
|
427
|
-
|
|
428
|
-
This method adds both the user's prompt and the assistant's response
|
|
429
|
-
to the conversation history as separate messages.
|
|
430
|
-
"""
|
|
431
|
-
# Add user's message (normalize role)
|
|
432
|
-
self.add_message("user", prompt)
|
|
433
|
-
|
|
434
|
-
# Add assistant's response (normalize role)
|
|
435
|
-
self.add_message("assistant", response)
|
|
436
|
-
|
|
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
|
+
|