llms-py 3.0.0b10__tar.gz → 3.0.2__tar.gz
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.
- {llms_py-3.0.0b10 → llms_py-3.0.2}/PKG-INFO +1 -1
- llms_py-3.0.0b10/llms/extensions/app/db_manager.py → llms_py-3.0.2/llms/db.py +170 -15
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/__init__.py +95 -39
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/db.py +16 -124
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/ui/threadStore.mjs +20 -2
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/__init__.py +37 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/gallery/__init__.py +15 -13
- llms_py-3.0.2/llms/extensions/gallery/db.py +243 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/gallery/ui/index.mjs +1 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/__init__.py +3 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/anthropic.py +7 -3
- llms_py-3.0.2/llms/extensions/providers/cerebras.py +37 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/chutes.py +1 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/google.py +131 -28
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/nvidia.py +2 -2
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/openai.py +2 -2
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/providers/openrouter.py +4 -2
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/system_prompts/ui/index.mjs +21 -26
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/system_prompts/ui/prompts.json +5 -5
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/llms.json +3 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/main.py +81 -34
- llms_py-3.0.2/llms/providers.json +1 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/ai.mjs +1 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/app.css +96 -3
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/ctx.mjs +24 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/index.mjs +2 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/modules/chat/ChatBody.mjs +1 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/modules/chat/index.mjs +19 -1
- llms_py-3.0.2/llms/ui/modules/icons.mjs +46 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/modules/layout.mjs +28 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/modules/model-selector.mjs +0 -40
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/utils.mjs +9 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/PKG-INFO +1 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/SOURCES.txt +4 -31
- {llms_py-3.0.0b10 → llms_py-3.0.2}/pyproject.toml +1 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/setup.py +1 -1
- llms_py-3.0.2/tests/test_gemini_upload.py +71 -0
- llms_py-3.0.0b10/llms/__pycache__/__init__.cpython-312.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/__init__.cpython-313.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/__main__.cpython-312.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/__main__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/llms.cpython-312.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/main.cpython-312.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/main.cpython-313.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/main.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/__pycache__/plugins.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/app/requests.json +0 -9073
- llms_py-3.0.0b10/llms/extensions/app/threads.json +0 -15290
- llms_py-3.0.0b10/llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
- llms_py-3.0.0b10/llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
- llms_py-3.0.0b10/llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/gallery/db.py +0 -298
- llms_py-3.0.0b10/llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- llms_py-3.0.0b10/llms/providers.json +0 -1
- {llms_py-3.0.0b10 → llms_py-3.0.2}/LICENSE +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/MANIFEST.in +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/__init__.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/__main__.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/analytics/ui/index.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/ui/Recents.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/app/ui/index.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/CALCULATOR.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/core_tools/ui/index.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/gallery/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/__init__.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/index.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex-swap.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex-swap.min.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/system_prompts/README.md +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/system_prompts/__init__.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/tools/__init__.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/extensions/tools/ui/index.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/index.html +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/providers-extra.json +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/App.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/fav.svg +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/chart.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/charts.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/color.js +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/highlight.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/idb.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/marked.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/servicestack-vue.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/vue.min.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/lib/vue.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/markdown.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/tailwind.input.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms/ui/typography.css +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/requirements.txt +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/setup.cfg +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_async.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_config.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_extensions.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_integration.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_provider_checks.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_provider_config.py +0 -0
- {llms_py-3.0.0b10 → llms_py-3.0.2}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llms-py
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers
|
|
5
5
|
Home-page: https://github.com/ServiceStack/llms
|
|
6
6
|
Author: ServiceStack
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import sqlite3
|
|
3
|
+
import threading
|
|
3
4
|
from queue import Empty, Queue
|
|
4
5
|
from threading import Event, Thread
|
|
5
6
|
|
|
7
|
+
POOL = True
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
def create_reader_connection(db_path):
|
|
8
|
-
|
|
11
|
+
# isolation_level=None leaves the connection in autocommit mode
|
|
12
|
+
conn = sqlite3.connect(
|
|
13
|
+
db_path, timeout=1.0, check_same_thread=False, isolation_level=None
|
|
14
|
+
) # Lower - reads should be fast
|
|
9
15
|
conn.execute("PRAGMA query_only=1") # Read-only optimization
|
|
10
16
|
return conn
|
|
11
17
|
|
|
@@ -33,7 +39,7 @@ def writer_thread(ctx, db_path, task_queue, stop_event):
|
|
|
33
39
|
sql, args, callback = task # Optional callback for results
|
|
34
40
|
|
|
35
41
|
try:
|
|
36
|
-
ctx.dbg("SQL>" + ("\n" if "\n" in sql else " ") + sql)
|
|
42
|
+
ctx.dbg("SQL>" + ("\n" if "\n" in sql else " ") + sql + ("\n" if args else "") + str(args))
|
|
37
43
|
cursor = conn.execute(sql, args)
|
|
38
44
|
conn.commit()
|
|
39
45
|
ctx.dbg(f"lastrowid {cursor.lastrowid}, rowcount {cursor.rowcount}")
|
|
@@ -53,17 +59,79 @@ def writer_thread(ctx, db_path, task_queue, stop_event):
|
|
|
53
59
|
conn.close()
|
|
54
60
|
|
|
55
61
|
|
|
62
|
+
def to_dto(ctx, row, json_columns):
|
|
63
|
+
# as=column -> [0,1,2]
|
|
64
|
+
if not isinstance(row, dict):
|
|
65
|
+
return row
|
|
66
|
+
|
|
67
|
+
to = {}
|
|
68
|
+
for k, v in row.items():
|
|
69
|
+
if k in json_columns and v is not None and isinstance(v, str):
|
|
70
|
+
try:
|
|
71
|
+
to[k] = json.loads(v)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f"Failed to parse JSON for {k}: {v} ({type(v)})", e)
|
|
74
|
+
to[k] = v
|
|
75
|
+
else:
|
|
76
|
+
to[k] = v
|
|
77
|
+
return to
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def valid_columns(all_columns, fields):
|
|
81
|
+
if fields:
|
|
82
|
+
if not isinstance(fields, list):
|
|
83
|
+
fields = fields.split(",")
|
|
84
|
+
cols = []
|
|
85
|
+
for k in fields:
|
|
86
|
+
k = k.strip()
|
|
87
|
+
if k in all_columns:
|
|
88
|
+
cols.append(k)
|
|
89
|
+
return cols
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def table_columns(all_columns, fields):
|
|
94
|
+
cols = valid_columns(all_columns, fields)
|
|
95
|
+
return ", ".join(cols) if len(cols) > 0 else ", ".join(all_columns)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def select_columns(all_columns, fields, select=None):
|
|
99
|
+
columns = table_columns(all_columns, fields)
|
|
100
|
+
if select == "distinct":
|
|
101
|
+
return f"SELECT DISTINCT {columns}"
|
|
102
|
+
return f"SELECT {columns}"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def order_by(all_columns, sort):
|
|
106
|
+
cols = []
|
|
107
|
+
for k in sort.split(","):
|
|
108
|
+
k = k.strip()
|
|
109
|
+
by = ""
|
|
110
|
+
if k[0] == "-":
|
|
111
|
+
by = " DESC"
|
|
112
|
+
k = k[1:]
|
|
113
|
+
if k in all_columns:
|
|
114
|
+
cols.append(f"{k}{by}")
|
|
115
|
+
return f"ORDER BY {', '.join(cols)} " if len(cols) > 0 else ""
|
|
116
|
+
|
|
117
|
+
|
|
56
118
|
class DbManager:
|
|
57
|
-
def __init__(self, ctx, db_path):
|
|
119
|
+
def __init__(self, ctx, db_path, clone=None):
|
|
58
120
|
if db_path is None:
|
|
59
121
|
raise ValueError("db_path is required")
|
|
60
122
|
self.ctx = ctx
|
|
61
123
|
self.db_path = db_path
|
|
62
|
-
self.task_queue = Queue()
|
|
63
|
-
self.stop_event = Event()
|
|
64
|
-
self.writer_thread = Thread(target=writer_thread, args=(ctx, db_path, self.task_queue, self.stop_event))
|
|
65
|
-
self.writer_thread.start()
|
|
66
124
|
self.read_only_pool = Queue()
|
|
125
|
+
if not clone:
|
|
126
|
+
self.task_queue = Queue()
|
|
127
|
+
self.stop_event = Event()
|
|
128
|
+
self.writer_thread = Thread(target=writer_thread, args=(ctx, db_path, self.task_queue, self.stop_event))
|
|
129
|
+
self.writer_thread.start()
|
|
130
|
+
else:
|
|
131
|
+
# share singleton writer thread in clones
|
|
132
|
+
self.task_queue = clone.task_queue
|
|
133
|
+
self.stop_event = clone.stop_event
|
|
134
|
+
self.writer_thread = clone.writer_thread
|
|
67
135
|
|
|
68
136
|
def create_reader_connection(self):
|
|
69
137
|
return create_reader_connection(self.db_path)
|
|
@@ -72,11 +140,21 @@ class DbManager:
|
|
|
72
140
|
return create_writer_connection(self.db_path)
|
|
73
141
|
|
|
74
142
|
def resolve_connection(self):
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
143
|
+
if POOL:
|
|
144
|
+
try:
|
|
145
|
+
return self.read_only_pool.get_nowait()
|
|
146
|
+
except Empty:
|
|
147
|
+
return self.create_reader_connection()
|
|
148
|
+
else:
|
|
78
149
|
return self.create_reader_connection()
|
|
79
150
|
|
|
151
|
+
def release_connection(self, conn):
|
|
152
|
+
if POOL:
|
|
153
|
+
conn.rollback()
|
|
154
|
+
self.read_only_pool.put(conn)
|
|
155
|
+
else:
|
|
156
|
+
conn.close()
|
|
157
|
+
|
|
80
158
|
def write(self, query, args=None, callback=None):
|
|
81
159
|
"""
|
|
82
160
|
Execute a write operation asynchronously.
|
|
@@ -101,6 +179,9 @@ class DbManager:
|
|
|
101
179
|
return connection.execute(sql, parameters or ())
|
|
102
180
|
|
|
103
181
|
def all(self, sql, parameters=None, connection=None):
|
|
182
|
+
"""
|
|
183
|
+
Execute a query and return all rows as a list of dictionaries.
|
|
184
|
+
"""
|
|
104
185
|
conn = self.resolve_connection() if connection is None else connection
|
|
105
186
|
|
|
106
187
|
try:
|
|
@@ -112,9 +193,12 @@ class DbManager:
|
|
|
112
193
|
finally:
|
|
113
194
|
if connection is None:
|
|
114
195
|
conn.row_factory = None
|
|
115
|
-
self.
|
|
196
|
+
self.release_connection(conn)
|
|
116
197
|
|
|
117
198
|
def one(self, sql, parameters=None, connection=None):
|
|
199
|
+
"""
|
|
200
|
+
Execute a query and return the first row as a dictionary.
|
|
201
|
+
"""
|
|
118
202
|
conn = self.resolve_connection() if connection is None else connection
|
|
119
203
|
|
|
120
204
|
try:
|
|
@@ -126,9 +210,12 @@ class DbManager:
|
|
|
126
210
|
finally:
|
|
127
211
|
if connection is None:
|
|
128
212
|
conn.row_factory = None
|
|
129
|
-
self.
|
|
213
|
+
self.release_connection(conn)
|
|
130
214
|
|
|
131
215
|
def scalar(self, sql, parameters=None, connection=None):
|
|
216
|
+
"""
|
|
217
|
+
Execute a scalar query and return the first column of the first row.
|
|
218
|
+
"""
|
|
132
219
|
conn = self.resolve_connection() if connection is None else connection
|
|
133
220
|
|
|
134
221
|
try:
|
|
@@ -140,7 +227,7 @@ class DbManager:
|
|
|
140
227
|
finally:
|
|
141
228
|
if connection is None:
|
|
142
229
|
conn.row_factory = None
|
|
143
|
-
self.
|
|
230
|
+
self.release_connection(conn)
|
|
144
231
|
|
|
145
232
|
def column(self, sql, parameters=None, connection=None):
|
|
146
233
|
"""
|
|
@@ -154,7 +241,7 @@ class DbManager:
|
|
|
154
241
|
return [row[0] for row in cursor.fetchall()]
|
|
155
242
|
finally:
|
|
156
243
|
if connection is None:
|
|
157
|
-
self.
|
|
244
|
+
self.release_connection(conn)
|
|
158
245
|
|
|
159
246
|
def dict(self, sql, parameters=None, connection=None):
|
|
160
247
|
"""
|
|
@@ -171,7 +258,7 @@ class DbManager:
|
|
|
171
258
|
finally:
|
|
172
259
|
if connection is None:
|
|
173
260
|
conn.row_factory = None
|
|
174
|
-
self.
|
|
261
|
+
self.release_connection(conn)
|
|
175
262
|
|
|
176
263
|
# Helper to safely dump JSON if value exists
|
|
177
264
|
def value(self, val):
|
|
@@ -181,6 +268,74 @@ class DbManager:
|
|
|
181
268
|
return json.dumps(val)
|
|
182
269
|
return val
|
|
183
270
|
|
|
271
|
+
def insert(self, table, columns, info, callback=None):
|
|
272
|
+
if not info:
|
|
273
|
+
raise Exception("info is required")
|
|
274
|
+
|
|
275
|
+
args = {}
|
|
276
|
+
known_columns = columns.keys()
|
|
277
|
+
for k, val in info.items():
|
|
278
|
+
if k in known_columns and k != "id":
|
|
279
|
+
args[k] = self.value(val)
|
|
280
|
+
|
|
281
|
+
insert_keys = list(args.keys())
|
|
282
|
+
insert_body = ", ".join(insert_keys)
|
|
283
|
+
insert_values = ", ".join(["?" for _ in insert_keys])
|
|
284
|
+
|
|
285
|
+
sql = f"INSERT INTO {table} ({insert_body}) VALUES ({insert_values})"
|
|
286
|
+
|
|
287
|
+
self.write(sql, tuple(args[k] for k in insert_keys), callback)
|
|
288
|
+
|
|
289
|
+
async def insert_async(self, table, columns, info):
|
|
290
|
+
event = threading.Event()
|
|
291
|
+
|
|
292
|
+
ret = [None]
|
|
293
|
+
|
|
294
|
+
def cb(lastrowid, rowcount, error=None):
|
|
295
|
+
nonlocal ret
|
|
296
|
+
if error:
|
|
297
|
+
raise error
|
|
298
|
+
ret[0] = lastrowid
|
|
299
|
+
event.set()
|
|
300
|
+
|
|
301
|
+
self.insert(table, columns, info, cb)
|
|
302
|
+
event.wait()
|
|
303
|
+
return ret[0]
|
|
304
|
+
|
|
305
|
+
def update(self, table, columns, info, callback=None):
|
|
306
|
+
if not info:
|
|
307
|
+
raise Exception("info is required")
|
|
308
|
+
|
|
309
|
+
args = {}
|
|
310
|
+
known_columns = columns.keys()
|
|
311
|
+
for k, val in info.items():
|
|
312
|
+
if k in known_columns and k != "id":
|
|
313
|
+
args[k] = self.value(val)
|
|
314
|
+
|
|
315
|
+
update_keys = list(args.keys())
|
|
316
|
+
update_body = ", ".join([f"{k} = :{k}" for k in update_keys])
|
|
317
|
+
|
|
318
|
+
args["id"] = info["id"]
|
|
319
|
+
sql = f"UPDATE {table} SET {update_body} WHERE id = :id"
|
|
320
|
+
|
|
321
|
+
self.write(sql, args, callback)
|
|
322
|
+
|
|
323
|
+
async def update_async(self, table, columns, info):
|
|
324
|
+
event = threading.Event()
|
|
325
|
+
|
|
326
|
+
ret = [None]
|
|
327
|
+
|
|
328
|
+
def cb(lastrowid, rowcount, error=None):
|
|
329
|
+
nonlocal ret
|
|
330
|
+
if error:
|
|
331
|
+
raise error
|
|
332
|
+
ret[0] = rowcount
|
|
333
|
+
event.set()
|
|
334
|
+
|
|
335
|
+
self.update(table, columns, info, cb)
|
|
336
|
+
event.wait()
|
|
337
|
+
return ret[0]
|
|
338
|
+
|
|
184
339
|
def close(self):
|
|
185
340
|
self.ctx.dbg("Closing database")
|
|
186
341
|
self.stop_event.set()
|
|
@@ -7,13 +7,9 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from aiohttp import web
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
from .db import AppDB
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
from llms.extensions.app.db import AppDB
|
|
14
|
-
except ImportError as e:
|
|
15
|
-
print(f"Failed to import AppDB: {e}")
|
|
16
|
-
AppDB = None
|
|
12
|
+
g_db = None
|
|
17
13
|
|
|
18
14
|
|
|
19
15
|
def install(ctx):
|
|
@@ -25,16 +21,6 @@ def install(ctx):
|
|
|
25
21
|
g_db = AppDB(ctx, db_path)
|
|
26
22
|
ctx.register_shutdown_handler(g_db.close)
|
|
27
23
|
|
|
28
|
-
threads_json = "/home/mythz/src/ServiceStack/llms/llms/extensions/app/threads.json"
|
|
29
|
-
requests_json = "/home/mythz/src/ServiceStack/llms/llms/extensions/app/requests.json"
|
|
30
|
-
with open(threads_json) as f:
|
|
31
|
-
threads = json.load(f)["threads"]
|
|
32
|
-
threads.reverse()
|
|
33
|
-
with open(requests_json) as f:
|
|
34
|
-
requests = json.load(f)["requests"]
|
|
35
|
-
requests.reverse()
|
|
36
|
-
# g_db.import_db(threads, requests)
|
|
37
|
-
|
|
38
24
|
except Exception as e:
|
|
39
25
|
ctx.err("Failed to init AppDB", e)
|
|
40
26
|
return g_db
|
|
@@ -42,28 +28,49 @@ def install(ctx):
|
|
|
42
28
|
if not get_db():
|
|
43
29
|
return
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
31
|
+
thread_fields = [
|
|
32
|
+
"id",
|
|
33
|
+
"threadId",
|
|
34
|
+
"createdAt",
|
|
35
|
+
"updatedAt",
|
|
36
|
+
"title",
|
|
37
|
+
"model",
|
|
38
|
+
"modelInfo",
|
|
39
|
+
"modalities",
|
|
40
|
+
"messages",
|
|
41
|
+
"args",
|
|
42
|
+
"cost",
|
|
43
|
+
"inputTokens",
|
|
44
|
+
"outputTokens",
|
|
45
|
+
"stats",
|
|
46
|
+
"provider",
|
|
47
|
+
"providerModel",
|
|
48
|
+
"publishedAt",
|
|
49
|
+
"startedAt",
|
|
50
|
+
"completedAt",
|
|
51
|
+
"metadata",
|
|
52
|
+
"error",
|
|
53
|
+
"ref",
|
|
54
|
+
]
|
|
61
55
|
|
|
62
56
|
def thread_dto(row):
|
|
63
|
-
return row and to_dto(
|
|
57
|
+
return row and g_db.to_dto(
|
|
58
|
+
row,
|
|
59
|
+
[
|
|
60
|
+
"messages",
|
|
61
|
+
"tools",
|
|
62
|
+
"toolHistory",
|
|
63
|
+
"modalities",
|
|
64
|
+
"args",
|
|
65
|
+
"modelInfo",
|
|
66
|
+
"stats",
|
|
67
|
+
"metadata",
|
|
68
|
+
"providerResponse",
|
|
69
|
+
],
|
|
70
|
+
)
|
|
64
71
|
|
|
65
72
|
def request_dto(row):
|
|
66
|
-
return row and to_dto(row, ["usage"])
|
|
73
|
+
return row and g_db.to_dto(row, ["usage"])
|
|
67
74
|
|
|
68
75
|
def prompt_to_title(prompt):
|
|
69
76
|
return prompt[:100] + ("..." if len(prompt) > 100 else "") if prompt else None
|
|
@@ -77,7 +84,10 @@ def install(ctx):
|
|
|
77
84
|
return messages
|
|
78
85
|
|
|
79
86
|
async def query_threads(request):
|
|
80
|
-
|
|
87
|
+
query = request.query.copy()
|
|
88
|
+
if "fields" not in query:
|
|
89
|
+
query["fields"] = thread_fields
|
|
90
|
+
rows = g_db.query_threads(query, user=ctx.get_username(request))
|
|
81
91
|
dtos = [thread_dto(row) for row in rows]
|
|
82
92
|
return web.json_response(dtos)
|
|
83
93
|
|
|
@@ -91,6 +101,13 @@ def install(ctx):
|
|
|
91
101
|
|
|
92
102
|
ctx.add_post("threads", create_thread)
|
|
93
103
|
|
|
104
|
+
async def get_thread(request):
|
|
105
|
+
id = request.match_info["id"]
|
|
106
|
+
row = g_db.get_thread(id, user=ctx.get_username(request))
|
|
107
|
+
return web.json_response(thread_dto(row) if row else "")
|
|
108
|
+
|
|
109
|
+
ctx.add_get("threads/{id}", get_thread)
|
|
110
|
+
|
|
94
111
|
async def update_thread(request):
|
|
95
112
|
thread = await request.json()
|
|
96
113
|
id = request.match_info["id"]
|
|
@@ -129,8 +146,10 @@ def install(ctx):
|
|
|
129
146
|
if not thread:
|
|
130
147
|
raise Exception("Thread not found")
|
|
131
148
|
|
|
149
|
+
tools = chat.get("tools", thread.get("tools", []))
|
|
132
150
|
update_thread = {
|
|
133
151
|
"messages": messages,
|
|
152
|
+
"tools": tools,
|
|
134
153
|
"startedAt": datetime.now(),
|
|
135
154
|
"completedAt": None,
|
|
136
155
|
"error": None,
|
|
@@ -174,24 +193,28 @@ def install(ctx):
|
|
|
174
193
|
if not thread:
|
|
175
194
|
raise Exception("Thread not found")
|
|
176
195
|
|
|
177
|
-
metadata = thread.get("metadata"
|
|
196
|
+
metadata = thread.get("metadata") or {}
|
|
178
197
|
chat = {
|
|
179
198
|
"model": thread.get("model"),
|
|
180
199
|
"messages": thread.get("messages"),
|
|
181
200
|
"modalities": thread.get("modalities"),
|
|
182
201
|
"systemPrompt": thread.get("systemPrompt"),
|
|
202
|
+
"tools": thread.get("tools"), # tools request
|
|
183
203
|
"metadata": metadata,
|
|
184
204
|
}
|
|
185
|
-
|
|
205
|
+
args = thread.get("args") or {}
|
|
206
|
+
for k, v in args.items():
|
|
186
207
|
if k in ctx.request_args:
|
|
187
208
|
chat[k] = v
|
|
188
209
|
|
|
210
|
+
ctx.dbg("CHAT\n" + json.dumps(chat, indent=2))
|
|
211
|
+
|
|
189
212
|
context = {
|
|
190
213
|
"chat": chat,
|
|
191
214
|
"user": user,
|
|
192
215
|
"threadId": id,
|
|
193
216
|
"metadata": metadata,
|
|
194
|
-
"tools": metadata.get("tools", "all"),
|
|
217
|
+
"tools": metadata.get("tools", "all"), # only tools: all|none|<tool1>,<tool2>,...
|
|
195
218
|
}
|
|
196
219
|
|
|
197
220
|
# execute chat in background thread
|
|
@@ -305,6 +328,7 @@ def install(ctx):
|
|
|
305
328
|
metadata = chat.get("metadata", {})
|
|
306
329
|
model = chat.get("model", None)
|
|
307
330
|
messages = timestamp_messages(chat.get("messages", []))
|
|
331
|
+
tools = chat.get("tools", [])
|
|
308
332
|
title = context.get("title") or prompt_to_title(ctx.last_user_prompt(chat) if chat else None)
|
|
309
333
|
started_at = context.get("startedAt")
|
|
310
334
|
if not started_at:
|
|
@@ -317,6 +341,7 @@ def install(ctx):
|
|
|
317
341
|
"modelInfo": model_info,
|
|
318
342
|
"title": title,
|
|
319
343
|
"messages": messages,
|
|
344
|
+
"tools": tools,
|
|
320
345
|
"systemPrompt": ctx.chat_to_system_prompt(chat),
|
|
321
346
|
"modalities": chat.get("modalities", ["text"]),
|
|
322
347
|
"startedAt": started_at,
|
|
@@ -331,6 +356,7 @@ def install(ctx):
|
|
|
331
356
|
"modelInfo": model_info,
|
|
332
357
|
"startedAt": started_at,
|
|
333
358
|
"messages": messages,
|
|
359
|
+
"tools": tools,
|
|
334
360
|
"completedAt": None,
|
|
335
361
|
"error": None,
|
|
336
362
|
"metadata": metadata,
|
|
@@ -365,6 +391,29 @@ def install(ctx):
|
|
|
365
391
|
|
|
366
392
|
ctx.register_chat_tool_filter(tool_request)
|
|
367
393
|
|
|
394
|
+
def truncate_long_strings(obj, max_length=10000):
|
|
395
|
+
"""
|
|
396
|
+
Recursively traverse a dictionary/list structure and replace
|
|
397
|
+
string values longer than max_length with their length indicator.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
obj: The object to process (dict, list, or other value)
|
|
401
|
+
max_length: Maximum string length before truncation (default 10000)
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
A new object with long strings replaced by "({length})"
|
|
405
|
+
"""
|
|
406
|
+
if isinstance(obj, dict):
|
|
407
|
+
return {key: truncate_long_strings(value, max_length) for key, value in obj.items()}
|
|
408
|
+
elif isinstance(obj, list):
|
|
409
|
+
return [truncate_long_strings(item, max_length) for item in obj]
|
|
410
|
+
elif isinstance(obj, str):
|
|
411
|
+
if len(obj) > max_length:
|
|
412
|
+
return f"({len(obj)})"
|
|
413
|
+
return obj
|
|
414
|
+
else:
|
|
415
|
+
return obj
|
|
416
|
+
|
|
368
417
|
async def chat_response(openai_response, context):
|
|
369
418
|
ctx.dbg("create_response")
|
|
370
419
|
o = openai_response
|
|
@@ -416,7 +465,6 @@ def install(ctx):
|
|
|
416
465
|
"totalTokens": total_tokens,
|
|
417
466
|
"usage": usage,
|
|
418
467
|
"completedAt": completed_at,
|
|
419
|
-
"toolHistory": o.get("tool_history", None),
|
|
420
468
|
"ref": o.get("id", None),
|
|
421
469
|
}
|
|
422
470
|
tasks.append(g_db.create_request_async(request, user=user))
|
|
@@ -446,15 +494,23 @@ def install(ctx):
|
|
|
446
494
|
}
|
|
447
495
|
messages.append(assistant_message)
|
|
448
496
|
|
|
497
|
+
tools = chat.get("tools", [])
|
|
449
498
|
update_thread = {
|
|
450
499
|
"model": model,
|
|
451
500
|
"providerModel": o.get("model"),
|
|
452
501
|
"modelInfo": model_info,
|
|
453
502
|
"messages": messages,
|
|
503
|
+
"tools": tools,
|
|
454
504
|
"completedAt": completed_at,
|
|
455
505
|
}
|
|
506
|
+
tool_history = o.get("tool_history", None)
|
|
507
|
+
if tool_history:
|
|
508
|
+
update_thread["toolHistory"] = tool_history
|
|
456
509
|
if "error" in metadata:
|
|
457
510
|
update_thread["error"] = metadata["error"]
|
|
511
|
+
provider_response = context.get("providerResponse", None)
|
|
512
|
+
if provider_response:
|
|
513
|
+
update_thread["providerResponse"] = truncate_long_strings(provider_response)
|
|
458
514
|
tasks.append(g_db.update_thread_async(thread_id, update_thread, user=user))
|
|
459
515
|
else:
|
|
460
516
|
ctx.dbg("Missing thread_id")
|