lollms-client 0.33.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lollms-client might be problematic. Click here for more details.

Files changed (73) hide show
  1. lollms_client/__init__.py +1 -1
  2. lollms_client/llm_bindings/azure_openai/__init__.py +6 -10
  3. lollms_client/llm_bindings/claude/__init__.py +4 -7
  4. lollms_client/llm_bindings/gemini/__init__.py +3 -7
  5. lollms_client/llm_bindings/grok/__init__.py +3 -7
  6. lollms_client/llm_bindings/groq/__init__.py +4 -6
  7. lollms_client/llm_bindings/hugging_face_inference_api/__init__.py +4 -6
  8. lollms_client/llm_bindings/litellm/__init__.py +15 -6
  9. lollms_client/llm_bindings/llamacpp/__init__.py +27 -9
  10. lollms_client/llm_bindings/lollms/__init__.py +24 -14
  11. lollms_client/llm_bindings/lollms_webui/__init__.py +6 -12
  12. lollms_client/llm_bindings/mistral/__init__.py +3 -5
  13. lollms_client/llm_bindings/ollama/__init__.py +6 -11
  14. lollms_client/llm_bindings/open_router/__init__.py +4 -6
  15. lollms_client/llm_bindings/openai/__init__.py +7 -14
  16. lollms_client/llm_bindings/openllm/__init__.py +12 -12
  17. lollms_client/llm_bindings/pythonllamacpp/__init__.py +1 -1
  18. lollms_client/llm_bindings/tensor_rt/__init__.py +8 -13
  19. lollms_client/llm_bindings/transformers/__init__.py +14 -6
  20. lollms_client/llm_bindings/vllm/__init__.py +16 -12
  21. lollms_client/lollms_core.py +296 -487
  22. lollms_client/lollms_discussion.py +431 -78
  23. lollms_client/lollms_llm_binding.py +191 -380
  24. lollms_client/lollms_mcp_binding.py +33 -2
  25. lollms_client/mcp_bindings/local_mcp/__init__.py +3 -2
  26. lollms_client/mcp_bindings/remote_mcp/__init__.py +6 -5
  27. lollms_client/mcp_bindings/standard_mcp/__init__.py +3 -5
  28. lollms_client/stt_bindings/lollms/__init__.py +6 -8
  29. lollms_client/stt_bindings/whisper/__init__.py +2 -4
  30. lollms_client/stt_bindings/whispercpp/__init__.py +15 -16
  31. lollms_client/tti_bindings/dalle/__init__.py +29 -28
  32. lollms_client/tti_bindings/diffusers/__init__.py +25 -21
  33. lollms_client/tti_bindings/gemini/__init__.py +215 -0
  34. lollms_client/tti_bindings/lollms/__init__.py +8 -9
  35. lollms_client-1.0.0.dist-info/METADATA +1214 -0
  36. lollms_client-1.0.0.dist-info/RECORD +69 -0
  37. {lollms_client-0.33.0.dist-info → lollms_client-1.0.0.dist-info}/top_level.txt +0 -2
  38. examples/article_summary/article_summary.py +0 -58
  39. examples/console_discussion/console_app.py +0 -266
  40. examples/console_discussion.py +0 -448
  41. examples/deep_analyze/deep_analyse.py +0 -30
  42. examples/deep_analyze/deep_analyze_multiple_files.py +0 -32
  43. examples/function_calling_with_local_custom_mcp.py +0 -250
  44. examples/generate_a_benchmark_for_safe_store.py +0 -89
  45. examples/generate_and_speak/generate_and_speak.py +0 -251
  46. examples/generate_game_sfx/generate_game_fx.py +0 -240
  47. examples/generate_text_with_multihop_rag_example.py +0 -210
  48. examples/gradio_chat_app.py +0 -228
  49. examples/gradio_lollms_chat.py +0 -259
  50. examples/internet_search_with_rag.py +0 -226
  51. examples/lollms_chat/calculator.py +0 -59
  52. examples/lollms_chat/derivative.py +0 -48
  53. examples/lollms_chat/test_openai_compatible_with_lollms_chat.py +0 -12
  54. examples/lollms_discussions_test.py +0 -155
  55. examples/mcp_examples/external_mcp.py +0 -267
  56. examples/mcp_examples/local_mcp.py +0 -171
  57. examples/mcp_examples/openai_mcp.py +0 -203
  58. examples/mcp_examples/run_remote_mcp_example_v2.py +0 -290
  59. examples/mcp_examples/run_standard_mcp_example.py +0 -204
  60. examples/simple_text_gen_test.py +0 -173
  61. examples/simple_text_gen_with_image_test.py +0 -178
  62. examples/test_local_models/local_chat.py +0 -9
  63. examples/text_2_audio.py +0 -77
  64. examples/text_2_image.py +0 -144
  65. examples/text_2_image_diffusers.py +0 -274
  66. examples/text_and_image_2_audio.py +0 -59
  67. examples/text_gen.py +0 -30
  68. examples/text_gen_system_prompt.py +0 -29
  69. lollms_client-0.33.0.dist-info/METADATA +0 -854
  70. lollms_client-0.33.0.dist-info/RECORD +0 -101
  71. test/test_lollms_discussion.py +0 -368
  72. {lollms_client-0.33.0.dist-info → lollms_client-1.0.0.dist-info}/WHEEL +0 -0
  73. {lollms_client-0.33.0.dist-info → lollms_client-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,101 +0,0 @@
1
- examples/console_discussion.py,sha256=JxjVaAxtt1WEXLN8vCJosu-cYNgfIX2JGO25kg1FFNY,20490
2
- examples/function_calling_with_local_custom_mcp.py,sha256=g6wOFRB8-p9Cv7hKmQaGzPvtMX3H77gas01QVNEOduM,12407
3
- examples/generate_a_benchmark_for_safe_store.py,sha256=bkSt0mrpNsN0krZAUShm0jgVM1ukrPpjI7VwSgcNdSA,3974
4
- examples/generate_text_with_multihop_rag_example.py,sha256=riEyVYo97r6ZYdySL-NJkRhE4MnpwbZku1sN8RNvbvs,11519
5
- examples/gradio_chat_app.py,sha256=ZZ_D1U0wvvwE9THmAPXUvNKkFG2gi7tQq1f2pQx_2ug,15315
6
- examples/gradio_lollms_chat.py,sha256=z5FDE62dmPU3nb16zbZX6jkVitML1PMfPxYyWr8VLz8,10135
7
- examples/internet_search_with_rag.py,sha256=ioTb_WI2M6kFeh1Dg-EGcKjccphnCsIGD_e9PZgZshw,12314
8
- examples/lollms_discussions_test.py,sha256=Jk1cCUDBBhTcK5glI50jAgzfB3IOiiUlnK3q7RYfMkA,6796
9
- examples/simple_text_gen_test.py,sha256=RoX9ZKJjGMujeep60wh5WT_GoBn0O9YKJY6WOy-ZmOc,8710
10
- examples/simple_text_gen_with_image_test.py,sha256=rR1O5Prcb52UHtJ3c6bv7VuTd1cvbkr5aNZU-v-Rs3Y,9263
11
- examples/text_2_audio.py,sha256=MfL4AH_NNwl6m0I0ywl4BXRZJ0b9Y_9fRqDIe6O-Sbw,3523
12
- examples/text_2_image.py,sha256=CwRdB9K-38Ghsezg3B7_daPtFtsvDcV2hovaMRUjueg,6495
13
- examples/text_2_image_diffusers.py,sha256=oIVS--ovy3FQ8qwynY3bnoDRkQPDzVXwyozbZnrQ4fc,14398
14
- examples/text_and_image_2_audio.py,sha256=QLvSsLff8VZZa7k7K1EFGlPpQWZy07zM4Fnli5btAl0,2074
15
- examples/text_gen.py,sha256=_feyK65OQKG5j3htEXXFvC1bYTamZSOhkrz4yOn9PK4,1053
16
- examples/text_gen_system_prompt.py,sha256=jRQeGe1IVu_zRHX09CFiDYi7WrK9Zd5FlMqC_gnVH-g,1018
17
- examples/article_summary/article_summary.py,sha256=CR8mCBNcZEVCR-q34uOmrJyMlG-xk4HkMbsV-TOZEnk,1978
18
- examples/console_discussion/console_app.py,sha256=56ar4EKczbVTXMhTLUet5mj5WCY77pm_egZnKGQeXjs,11509
19
- examples/deep_analyze/deep_analyse.py,sha256=fZNmDrfEAuxEAfdbjAgJYIh1k6wbiuZ4RvwHRvtyUs8,971
20
- examples/deep_analyze/deep_analyze_multiple_files.py,sha256=fOryShA33P4IFxcxUDe-nJ2kW0v9w9yW8KsToS3ETl8,1032
21
- examples/generate_and_speak/generate_and_speak.py,sha256=RAlvRwtEKXCh894l9M3iQbADe8CvF5N442jtRurK02I,13908
22
- examples/generate_game_sfx/generate_game_fx.py,sha256=MgLNGi4hGBRoyr4bqYuCUdCSqd-ldDVfF0VSDUjgzsg,10467
23
- examples/lollms_chat/calculator.py,sha256=0mIakM3XehyUaoCsBS0l3eDTrqc81E88itfdHmdMYYc,2484
24
- examples/lollms_chat/derivative.py,sha256=1a4LKx_mD6Mq9oh-iGON0nfINP3Ul_TUSeDjEX7vSzE,1896
25
- examples/lollms_chat/test_openai_compatible_with_lollms_chat.py,sha256=JsooHpu7Hk4HSZNXvlL6N76-K6eFyq4gOH1lcsv_BcA,419
26
- examples/mcp_examples/external_mcp.py,sha256=swx1KCOz6jk8jGTAycq-xu7GXPAhRMDe1x--SKocugE,13371
27
- examples/mcp_examples/local_mcp.py,sha256=w40dgayvHYe01yvekEE0LjcbkpwKjWwJ-9v4_wGYsUk,9113
28
- examples/mcp_examples/openai_mcp.py,sha256=7IEnPGPXZgYZyiES_VaUbQ6viQjenpcUxGiHE-pGeFY,11060
29
- examples/mcp_examples/run_remote_mcp_example_v2.py,sha256=bbNn93NO_lKcFzfIsdvJJijGx2ePFTYfknofqZxMuRM,14626
30
- examples/mcp_examples/run_standard_mcp_example.py,sha256=GSZpaACPf3mDPsjA8esBQVUsIi7owI39ca5avsmvCxA,9419
31
- examples/test_local_models/local_chat.py,sha256=slakja2zaHOEAUsn2tn_VmI4kLx6luLBrPqAeaNsix8,456
32
- lollms_client/__init__.py,sha256=47yhAwjjK7irs9nhUU68Bk1NDWYRMIP23TCDociofi8,1147
33
- lollms_client/lollms_config.py,sha256=goEseDwDxYJf3WkYJ4IrLXwg3Tfw73CXV2Avg45M_hE,21876
34
- lollms_client/lollms_core.py,sha256=gDhpB62AluEmbVFvPm7vdnZgP2hGBymDLun57K1jrOM,177352
35
- lollms_client/lollms_discussion.py,sha256=RDX4V5R4sKCxuTjL2ILyGrDlHlyefFPf3x98_mnra3M,85608
36
- lollms_client/lollms_js_analyzer.py,sha256=01zUvuO2F_lnUe_0NLxe1MF5aHE1hO8RZi48mNPv-aw,8361
37
- lollms_client/lollms_llm_binding.py,sha256=8jJ8TPtkxh6TSKaYdrUrEI9ulY9gvN1RGHIrbPOWsDs,35963
38
- lollms_client/lollms_mcp_binding.py,sha256=0rK9HQCBEGryNc8ApBmtOlhKE1Yfn7X7xIQssXxS2Zc,8933
39
- lollms_client/lollms_mcp_security.py,sha256=FhVTDhSBjksGEZnopVnjFmEF5dv7D8bBTqoaj4BiF0E,3562
40
- lollms_client/lollms_personality.py,sha256=O-9nqZhazcITOkxjT24ENTxTmIoZLgqIsQ9WtWs0Id0,8719
41
- lollms_client/lollms_python_analyzer.py,sha256=7gf1fdYgXCOkPUkBAPNmr6S-66hMH4_KonOMsADASxc,10246
42
- lollms_client/lollms_stt_binding.py,sha256=jAUhLouEhh2hmm1bK76ianfw_6B59EHfY3FmLv6DU-g,5111
43
- lollms_client/lollms_tti_binding.py,sha256=afO0-d-Kqsmh8UHTijTvy6dZAt-XDB6R-IHmdbf-_fs,5928
44
- lollms_client/lollms_ttm_binding.py,sha256=FjVVSNXOZXK1qvcKEfxdiX6l2b4XdGOSNnZ0utAsbDg,4167
45
- lollms_client/lollms_tts_binding.py,sha256=5cJYECj8PYLJAyB6SEH7_fhHYK3Om-Y3arkygCnZ24o,4342
46
- lollms_client/lollms_ttv_binding.py,sha256=KkTaHLBhEEdt4sSVBlbwr5i_g_TlhcrwrT-7DjOsjWQ,4131
47
- lollms_client/lollms_types.py,sha256=0iSH1QHRRD-ddBqoL9EEKJ8wWCuwDUlN_FrfbCdg7Lw,3522
48
- lollms_client/lollms_utilities.py,sha256=3DAsII2X9uhRzRL-D0QlALcEdRg82y7OIL4yHVF32gY,19446
49
- lollms_client/llm_bindings/__init__.py,sha256=9sWGpmWSSj6KQ8H4lKGCjpLYwhnVdL_2N7gXCphPqh4,14
50
- lollms_client/llm_bindings/azure_openai/__init__.py,sha256=8C-gXoVa-OI9FmFM3PaMgrTfzqCLbs4f7CHJHxKuAR8,16675
51
- lollms_client/llm_bindings/claude/__init__.py,sha256=CsWILXAFytXtxp1ZAoNwq8KycW0POQ2MCmpT6Bz0Hd0,24877
52
- lollms_client/llm_bindings/gemini/__init__.py,sha256=ZflZVwAkAa-GfctuehOWIav977oTCdXUisQy253PFsk,21611
53
- lollms_client/llm_bindings/grok/__init__.py,sha256=5tIf3348RgAEaSp6FdG-LM9N8R7aR0t7OFspHf3XATs,23141
54
- lollms_client/llm_bindings/groq/__init__.py,sha256=qDCZXIzldblNHyb3LfcZkM0a8m1hSWilb2m9l-xvkZ8,12180
55
- lollms_client/llm_bindings/hugging_face_inference_api/__init__.py,sha256=PxgeRqT8dpa9GZoXwtSncy9AUgAN2cDKrvp_nbaWq0E,14027
56
- lollms_client/llm_bindings/litellm/__init__.py,sha256=pNkwyRPeENvTM4CDh6Pj3kQfxHfhX2pvXhGJDjKjp30,12340
57
- lollms_client/llm_bindings/llamacpp/__init__.py,sha256=6LHrsSD_4Xdq57sJaGf8gSndmG4hHcOS9wlsfa62TPQ,58650
58
- lollms_client/llm_bindings/lollms/__init__.py,sha256=scGHEKzlGX5fw2XwefVicsf28GrwgN3wU5nl4EPJ_Sk,24424
59
- lollms_client/llm_bindings/lollms_webui/__init__.py,sha256=Thoq3PJR2e03Y2Kd_FBb-DULJK0zT5-2ID1YIJLcPlw,17864
60
- lollms_client/llm_bindings/mistral/__init__.py,sha256=DFQAcrUR69oF1FhtKi1glMXqCF4jhqN2ctRN19tmrcM,14193
61
- lollms_client/llm_bindings/ollama/__init__.py,sha256=dXKHIeQCS9pz5AS07GF1eWj3ieWiz3aFOtxOX7yojbs,41314
62
- lollms_client/llm_bindings/open_router/__init__.py,sha256=tcG8yWd4ULIhqrfMURqqJbnAEDw8H3zU500FSHkoXMM,14977
63
- lollms_client/llm_bindings/openai/__init__.py,sha256=Z0zNTfBgBGwkwArN375kBt4otrUTI_84pHgVuyuDy0c,26253
64
- lollms_client/llm_bindings/openllm/__init__.py,sha256=xv2XDhJNCYe6NPnWBboDs24AQ1VJBOzsTuMcmuQ6xYY,29864
65
- lollms_client/llm_bindings/pythonllamacpp/__init__.py,sha256=VNa6NXe7bY44Oev8r6q5XlQbxqWU2nBV62SFlsPPH78,31810
66
- lollms_client/llm_bindings/tensor_rt/__init__.py,sha256=nPaNhGRd-bsG0UlYwcEqjd_UagCMEf5VEbBUW-GWu6A,32203
67
- lollms_client/llm_bindings/transformers/__init__.py,sha256=9LkqEC5bp1zHgyeGEcPQ3_uqvEAEf_B4p9DztcBaC5w,37211
68
- lollms_client/llm_bindings/vllm/__init__.py,sha256=2NqeeqYWXNq1aNicdcAwN9DaoL4gq96GZ7hsKErfC6c,32187
69
- lollms_client/mcp_bindings/local_mcp/__init__.py,sha256=FxS-0PULS3TZScPLDbwHlfyW5joWucmMfn6b9ydyeM0,14341
70
- lollms_client/mcp_bindings/local_mcp/default_tools/file_writer/file_writer.py,sha256=2pkt1JcEKj61lIA5zuW3s6qkdpQN5rKfooo7bnebx24,3061
71
- lollms_client/mcp_bindings/local_mcp/default_tools/generate_image_from_prompt/generate_image_from_prompt.py,sha256=THtZsMxNnXZiBdkwoBlfbWY2C5hhDdmPtnM-8cSKN6s,9488
72
- lollms_client/mcp_bindings/local_mcp/default_tools/internet_search/internet_search.py,sha256=PLC31-D04QKTOTb1uuCHnrAlpysQjsk89yIJngK0VGc,4586
73
- lollms_client/mcp_bindings/local_mcp/default_tools/python_interpreter/python_interpreter.py,sha256=McDCBVoVrMDYgU7EYtyOY7mCk1uEeTea0PSD69QqDsQ,6228
74
- lollms_client/mcp_bindings/remote_mcp/__init__.py,sha256=6ebENOqO-oUk3IpitVyiMGRICSl_X5DKKaGG52BdiT8,20388
75
- lollms_client/mcp_bindings/standard_mcp/__init__.py,sha256=zpF4h8cTUxoERI-xcVjmS_V772LK0V4jegjz2k1PK98,31658
76
- lollms_client/stt_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- lollms_client/stt_bindings/lollms/__init__.py,sha256=jBz3285atdPRqQe9ZRrb-AvjqKRB4f8tjLXjma0DLfE,6082
78
- lollms_client/stt_bindings/whisper/__init__.py,sha256=vrua7fLwDId9_WiH4y2gXOE0hy3Gr2Ig-z5ScIT2bHI,15447
79
- lollms_client/stt_bindings/whispercpp/__init__.py,sha256=B45lOn5rSoHXJSG9duPzBEPBOoNTrDzpCUdOL8KHaDM,21874
80
- lollms_client/tti_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
- lollms_client/tti_bindings/dalle/__init__.py,sha256=V-rODzcwaN9eg0wX8DGOQXXcDtnYYDWYD6t6pFNTo0w,23628
82
- lollms_client/tti_bindings/diffusers/__init__.py,sha256=Hcu3F3KU8h6b_T4Dpl98OsQrsUoecd_z7xOVIEyYoDc,37679
83
- lollms_client/tti_bindings/gemini/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
- lollms_client/tti_bindings/lollms/__init__.py,sha256=GJShFW6Y8MrfM9PXaPrdAp8OUpD6rraSRFt8ZOnrauo,8735
85
- lollms_client/ttm_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
- lollms_client/ttm_bindings/audiocraft/__init__.py,sha256=a0k6wTrHth6GaVOiNnVboeFY3oKVvCQPbQlqO38XEyc,14328
87
- lollms_client/ttm_bindings/bark/__init__.py,sha256=Pr3ou2a-7hNYDqbkxrAbghZpO5HvGUhz7e-7VGXIHHA,18976
88
- lollms_client/ttm_bindings/lollms/__init__.py,sha256=DU3WLmJaWNM1NAMtJsnaFo4Y9wlfc675M8aUiaLnojA,3143
89
- lollms_client/tts_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- lollms_client/tts_bindings/bark/__init__.py,sha256=cpnmr6rmXcNdy4ib_5UHAbUP5oGoMJwB931_vU6VI14,19480
91
- lollms_client/tts_bindings/lollms/__init__.py,sha256=8x2_T9XscvISw2TiaLoFxvrS7TIsVLdqbwSc04cX-wc,7164
92
- lollms_client/tts_bindings/piper_tts/__init__.py,sha256=0IEWG4zH3_sOkSb9WbZzkeV5Lvhgp5Gs2-2GN51MTjA,18930
93
- lollms_client/tts_bindings/xtts/__init__.py,sha256=FgcdUH06X6ZR806WQe5ixaYx0QoxtAcOgYo87a2qxYc,18266
94
- lollms_client/ttv_bindings/__init__.py,sha256=UZ8o2izQOJLQgtZ1D1cXoNST7rzqW22rL2Vufc7ddRc,3141
95
- lollms_client/ttv_bindings/lollms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
- lollms_client-0.33.0.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
97
- test/test_lollms_discussion.py,sha256=KxTsV1bPdNz8QqZd7tIof9kTWkeXLUtAMU08BQmoY6U,16829
98
- lollms_client-0.33.0.dist-info/METADATA,sha256=kPveiEdFJW9h1_mj_DYJJ9Gq1-upw6YaFd2a70lLhWo,38717
99
- lollms_client-0.33.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
100
- lollms_client-0.33.0.dist-info/top_level.txt,sha256=1jIpjTnOSGEGtIW2rGAFM6tVRzgsDdMOiox_SmDH_zw,28
101
- lollms_client-0.33.0.dist-info/RECORD,,
@@ -1,368 +0,0 @@
1
- # test_lollms_discussion.py
2
-
3
- import pytest
4
- from unittest.mock import MagicMock, patch
5
- from pathlib import Path
6
- import base64
7
- import json
8
-
9
- # Assuming your class definitions are in a file named 'lollms_discussion_module.py'
10
- # Adjust the import path as necessary.
11
- from lollms_client import LollmsDataManager, LollmsDiscussion
12
-
13
- # --- Test Fixtures and Helpers ---
14
-
15
- @pytest.fixture
16
- def mock_lollms_client():
17
- """Creates a mock LollmsClient that simulates its core functions."""
18
- client = MagicMock()
19
- # Simulate token counting with a simple word count
20
- client.count_tokens.side_effect = lambda text: len(str(text).split())
21
- # Simulate image token counting with a fixed value
22
- client.count_image_tokens.return_value = 75
23
- # Simulate structured content generation
24
- client.generate_structured_content.return_value = {"title": "A Mocked Title"}
25
- # Simulate text generation for summary and memory
26
- client.generate_text.return_value = "This is a mock summary."
27
- # Simulate chat generation
28
- client.chat.return_value = "This is a mock chat response."
29
- client.remove_thinking_blocks.return_value = "This is a mock chat response."
30
- return client
31
-
32
- @pytest.fixture
33
- def db_manager(tmp_path):
34
- """Creates a LollmsDataManager with a temporary database."""
35
- db_file = tmp_path / "test_discussions.db"
36
- return LollmsDataManager(f'sqlite:///{db_file}')
37
-
38
- def create_dummy_image_b64(text="Test"):
39
- """Generates a valid base64 encoded dummy PNG image string."""
40
- try:
41
- from PIL import Image, ImageDraw
42
- img = Image.new('RGB', (60, 30), color = 'red')
43
- d = ImageDraw.Draw(img)
44
- d.text((10,10), text, fill='white')
45
-
46
- # In-memory saving to avoid disk I/O in test
47
- from io import BytesIO
48
- buffered = BytesIO()
49
- img.save(buffered, format="PNG")
50
- return base64.b64encode(buffered.getvalue()).decode('utf-8')
51
- except ImportError:
52
- # Fallback if Pillow is not installed
53
- # This is a 1x1 transparent pixel
54
- return "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
55
-
56
- @pytest.fixture
57
- def dummy_image_b64_1():
58
- return create_dummy_image_b64("Image 1")
59
-
60
- @pytest.fixture
61
- def dummy_image_b64_2():
62
- return create_dummy_image_b64("Image 2")
63
-
64
-
65
- # --- Test Class ---
66
-
67
- class TestLollmsDiscussion:
68
-
69
- def test_creation_in_memory(self, mock_lollms_client):
70
- """Tests that an in-memory discussion can be created."""
71
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
72
- assert disc.id is not None
73
- assert not disc._is_db_backed
74
-
75
- def test_creation_db_backed(self, mock_lollms_client, db_manager):
76
- """Tests that a database-backed discussion can be created."""
77
- metadata = {"title": "DB Test"}
78
- disc = LollmsDiscussion.create_new(
79
- lollms_client=mock_lollms_client,
80
- db_manager=db_manager,
81
- discussion_metadata=metadata
82
- )
83
- assert disc._is_db_backed
84
- assert disc.metadata["title"] == "DB Test"
85
-
86
- def test_add_and_get_message(self, mock_lollms_client):
87
- """Tests basic message addition and retrieval."""
88
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
89
- disc.add_message(sender="user", content="Hello, world!")
90
-
91
- assert disc.active_branch_id is not None
92
- messages = disc.get_messages()
93
- assert len(messages) == 1
94
- assert messages[0].sender == "user"
95
- assert messages[0].content == "Hello, world!"
96
-
97
- def test_data_zones_and_export(self, mock_lollms_client):
98
- """Tests that data zones are correctly included in exports."""
99
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
100
- disc.system_prompt = "You are a tester."
101
- disc.memory = "Remember this."
102
- disc.user_data_zone = "User is a dev."
103
- disc.add_message(sender="user", content="Test prompt.")
104
-
105
- exported_md = disc.export("markdown")
106
- assert "You are a tester." in exported_md
107
- assert "-- Memory --" in exported_md
108
- assert "Remember this." in exported_md
109
- assert "-- User Data Zone --" in exported_md
110
- assert "User is a dev." in exported_md
111
- assert "**User**: Test prompt." in exported_md
112
-
113
- def test_message_image_activation(self, mock_lollms_client, dummy_image_b64_1, dummy_image_b64_2):
114
- """Tests activating and deactivating images on a specific message."""
115
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
116
- msg = disc.add_message(sender="user", content="Look", images=[dummy_image_b64_1, dummy_image_b64_2])
117
-
118
- # Initially, both images should be active
119
- assert len(msg.get_active_images()) == 2
120
-
121
- # Deactivate the first image
122
- msg.toggle_image_activation(0, active=False)
123
- assert len(msg.get_active_images()) == 1
124
- all_imgs = msg.get_all_images()
125
- assert not all_imgs[0]["active"]
126
- assert all_imgs[1]["active"]
127
-
128
- # Toggle the first image back on
129
- msg.toggle_image_activation(0)
130
- assert len(msg.get_active_images()) == 2
131
- assert msg.get_all_images()[0]["active"]
132
-
133
- def test_discussion_image_management(self, mock_lollms_client, dummy_image_b64_1, dummy_image_b64_2):
134
- """Tests adding, toggling, and retrieving discussion-level images."""
135
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
136
-
137
- # Add two images
138
- disc.add_discussion_image(dummy_image_b64_1)
139
- disc.add_discussion_image(dummy_image_b64_2)
140
-
141
- # Check initial state
142
- assert len(disc.get_discussion_images()) == 2
143
- assert disc.get_discussion_images()[0]["active"]
144
- assert disc.get_discussion_images()[1]["active"]
145
-
146
- # Deactivate the second image
147
- disc.toggle_discussion_image_activation(1, active=False)
148
-
149
- # Verify changes
150
- all_disc_imgs = disc.get_discussion_images()
151
- assert len(all_disc_imgs) == 2
152
- assert all_disc_imgs[0]["active"]
153
- assert not all_disc_imgs[1]["active"]
154
-
155
- def test_export_with_multimodal_system_prompt(self, mock_lollms_client, dummy_image_b64_1):
156
- """Tests that active discussion images are included in the system prompt for export."""
157
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
158
- disc.system_prompt = "Analyze this image."
159
- disc.add_discussion_image(dummy_image_b64_1) # This one is active
160
- disc.add_discussion_image(create_dummy_image_b64("Inactive"))
161
- disc.toggle_discussion_image_activation(1, active=False) # This one is inactive
162
-
163
- disc.add_message(sender="user", content="What do you see?")
164
-
165
- openai_export = disc.export("openai_chat")
166
-
167
- system_message = openai_export[0]
168
- assert system_message["role"] == "system"
169
-
170
- content_parts = system_message["content"]
171
- assert len(content_parts) == 2
172
-
173
- text_part = next(p for p in content_parts if p["type"] == "text")
174
- image_part = next(p for p in content_parts if p["type"] == "image_url")
175
-
176
- assert text_part["text"] == "Analyze this image."
177
- assert image_part["image_url"]["url"].endswith(dummy_image_b64_1)
178
-
179
- def test_auto_title_updates_metadata(self, mock_lollms_client, db_manager):
180
- """Tests that auto_title calls the LLM and updates metadata."""
181
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client, db_manager=db_manager)
182
- disc.add_message(sender="user", content="This is a test to generate a title.")
183
-
184
- title = disc.auto_title()
185
-
186
- mock_lollms_client.generate_structured_content.assert_called_once()
187
- assert title == "A Mocked Title"
188
- assert disc.metadata['title'] == "A Mocked Title"
189
-
190
- def test_delete_branch(self, mock_lollms_client, db_manager):
191
- """Tests deleting a message and its descendants."""
192
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client, db_manager=db_manager)
193
-
194
- msg_a = disc.add_message(sender="user", content="A")
195
- msg_b = disc.add_message(sender="user", content="B", parent_id=msg_a.id)
196
- msg_c = disc.add_message(sender="user", content="C", parent_id=msg_b.id)
197
-
198
- assert disc.active_branch_id == msg_c.id
199
- assert len(disc.get_messages()) == 3
200
-
201
- disc.delete_branch(msg_b.id)
202
-
203
- assert disc.active_branch_id == msg_a.id
204
-
205
- remaining_messages = disc.get_messages()
206
- assert len(remaining_messages) == 1
207
- assert remaining_messages[0].id == msg_a.id
208
-
209
- def test_branching_and_switching(self, mock_lollms_client):
210
- """Tests creating and switching between conversational branches."""
211
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
212
-
213
- root_msg = disc.add_message(sender="user", content="Hello")
214
- ai_msg_1 = disc.add_message(sender="assistant", content="Hi there!")
215
-
216
- disc.switch_to_branch(root_msg.id)
217
- ai_msg_2 = disc.add_message(sender="assistant", content="Greetings!")
218
-
219
- assert len(disc._message_index) == 3
220
- assert disc.active_branch_id == ai_msg_2.id
221
-
222
- branch_1 = disc.get_branch(ai_msg_1.id)
223
- assert [m.id for m in branch_1] == [root_msg.id, ai_msg_1.id]
224
-
225
- branch_2 = disc.get_branch(ai_msg_2.id)
226
- assert [m.id for m in branch_2] == [root_msg.id, ai_msg_2.id]
227
-
228
- def test_regenerate_branch(self, mock_lollms_client, db_manager):
229
- """Tests that regeneration deletes the old AI message and creates a new one."""
230
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client, db_manager=db_manager)
231
-
232
- user_msg = disc.add_message(sender="user", content="Tell me a joke.")
233
- ai_msg = disc.add_message(sender="assistant", content="Why did the scarecrow win an award?")
234
-
235
- original_ai_id = ai_msg.id
236
-
237
- result = disc.regenerate_branch()
238
- new_ai_msg = result["ai_message"]
239
-
240
- assert new_ai_msg.id != original_ai_id
241
- assert new_ai_msg.content == "This is a mock chat response."
242
-
243
- messages = disc.get_messages(new_ai_msg.id)
244
- assert len(messages) == 2
245
- assert original_ai_id not in [m.id for m in messages]
246
-
247
- assert original_ai_id in disc._messages_to_delete_from_db
248
-
249
- def test_summarize_and_prune(self, mock_lollms_client):
250
- """Tests that the discussion is pruned when the token limit is exceeded."""
251
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
252
- disc.max_context_size = 20
253
-
254
- disc.add_message(sender="user", content="This is the first long message to establish history.")
255
- disc.add_message(sender="assistant", content="I see. This is the second long message.")
256
- m3 = disc.add_message(sender="user", content="Third message.")
257
- disc.add_message(sender="assistant", content="Fourth message.")
258
- disc.add_message(sender="user", content="Fifth message.")
259
- disc.add_message(sender="assistant", content="Sixth message.")
260
-
261
- disc.summarize_and_prune(max_tokens=20, preserve_last_n=4)
262
-
263
- mock_lollms_client.generate_text.assert_called_once()
264
- assert "This is a mock summary." in disc.pruning_summary
265
- assert disc.pruning_point_id == m3.id
266
-
267
- def test_memorize_updates_memory_zone(self, mock_lollms_client):
268
- """Tests that memorize calls the LLM and appends to the memory data zone."""
269
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
270
- disc.add_message(sender="user", content="My favorite color is blue.")
271
-
272
- disc.memorize()
273
-
274
- mock_lollms_client.generate_text.assert_called_once()
275
- assert "This is a mock summary." in disc.memory
276
- assert "Memory entry from" in disc.memory
277
-
278
- def test_get_context_status(self, mock_lollms_client, dummy_image_b64_1, dummy_image_b64_2):
279
- """Tests the token calculation in get_context_status."""
280
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
281
- disc.system_prompt = "You are a helpful AI."
282
- disc.add_discussion_image(dummy_image_b64_1) # 1 active discussion image
283
-
284
- msg = disc.add_message(sender="user", content="Hello there!", images=[dummy_image_b64_1, dummy_image_b64_2])
285
- msg.toggle_image_activation(1, active=False) # 1 active message image
286
-
287
- status = disc.get_context_status()
288
-
289
- # System context: "!@>system:\nYou are a helpful AI.\n" -> 6 tokens
290
- system_tokens = status["zones"]["system_context"]["tokens"]
291
- assert system_tokens == 6
292
-
293
- # Discussion Images: 1 active discussion image -> 75 tokens
294
- discussion_image_zone = status["zones"]["discussion_images"]
295
- assert discussion_image_zone["tokens"] == 75
296
-
297
- # History:
298
- # - Text: "!@>user:\nHello there!\n(2 image(s) attached)\n" -> 6 tokens
299
- # - Image: 1 active message image -> 75 tokens
300
- history_zone = status["zones"]["message_history"]
301
- assert history_zone["breakdown"]["text_tokens"] == 6
302
- assert history_zone["breakdown"]["image_tokens"] == 75
303
- assert history_zone["tokens"] == 81 # 6 + 75
304
-
305
- # Total should be sum of system, discussion images, and history
306
- assert status["current_tokens"] == 6 + 75 + 81 # 162
307
-
308
- # --- NEW PERSISTENCE AND SEPARATION TESTS ---
309
-
310
- def test_image_separation_and_context(self, mock_lollms_client, dummy_image_b64_1, dummy_image_b64_2):
311
- """Tests that discussion and message images are distinct but aggregated correctly."""
312
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client)
313
-
314
- # Add one image to the discussion, one to the message
315
- disc.add_discussion_image(dummy_image_b64_1)
316
- msg = disc.add_message(sender="user", content="Check these out", images=[dummy_image_b64_2])
317
-
318
- # Test separation
319
- assert len(disc.get_discussion_images()) == 1
320
- assert disc.get_discussion_images()[0]['data'] == dummy_image_b64_1
321
- assert len(msg.get_all_images()) == 1
322
- assert msg.get_all_images()[0]['data'] == dummy_image_b64_2
323
-
324
- # Test aggregation for context
325
- all_active_images = disc.get_active_images()
326
- assert len(all_active_images) == 2
327
- assert dummy_image_b64_1 in all_active_images
328
- assert dummy_image_b64_2 in all_active_images
329
-
330
- def test_full_multimodal_persistence(self, mock_lollms_client, db_manager, dummy_image_b64_1, dummy_image_b64_2):
331
- """End-to-end test for persisting both discussion and message images and their states."""
332
- disc_id = None
333
-
334
- # --- Scope 1: Create and Save ---
335
- with db_manager.get_session():
336
- disc = LollmsDiscussion.create_new(lollms_client=mock_lollms_client, db_manager=db_manager)
337
- disc_id = disc.id
338
-
339
- # Add and deactivate a discussion image
340
- disc.add_discussion_image(dummy_image_b64_1)
341
- disc.toggle_discussion_image_activation(0, active=False)
342
-
343
- # Add and deactivate a message image
344
- msg = disc.add_message(sender="user", content="Multimodal test", images=[dummy_image_b64_2])
345
- msg.toggle_image_activation(0, active=False)
346
-
347
- disc.commit()
348
- disc.close()
349
-
350
- # --- Scope 2: Reload and Verify ---
351
- reloaded_disc = db_manager.get_discussion(mock_lollms_client, disc_id)
352
- assert reloaded_disc is not None
353
-
354
- # Verify discussion image state
355
- reloaded_disc_imgs = reloaded_disc.get_discussion_images()
356
- assert len(reloaded_disc_imgs) == 1
357
- assert reloaded_disc_imgs[0]['data'] == dummy_image_b64_1
358
- assert not reloaded_disc_imgs[0]['active'] # Must be inactive
359
-
360
- # Verify message image state
361
- reloaded_msg = reloaded_disc.get_messages()[0]
362
- reloaded_msg_imgs = reloaded_msg.get_all_images()
363
- assert len(reloaded_msg_imgs) == 1
364
- assert reloaded_msg_imgs[0]['data'] == dummy_image_b64_2
365
- assert not reloaded_msg_imgs[0]['active'] # Must be inactive
366
-
367
- # Verify that get_active_images returns nothing
368
- assert len(reloaded_disc.get_active_images()) == 0