llms-py 3.0.1__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.1 → llms_py-3.0.2}/PKG-INFO +1 -1
- llms_py-3.0.1/llms/extensions/app/db_manager.py → llms_py-3.0.2/llms/db.py +170 -15
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/__init__.py +95 -29
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/db.py +16 -124
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/ui/threadStore.mjs +20 -2
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/__init__.py +37 -0
- {llms_py-3.0.1 → 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.1 → llms_py-3.0.2}/llms/extensions/gallery/ui/index.mjs +1 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/providers/__init__.py +3 -1
- {llms_py-3.0.1 → 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.1 → llms_py-3.0.2}/llms/extensions/providers/chutes.py +1 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/providers/google.py +131 -28
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/providers/nvidia.py +2 -2
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/providers/openai.py +2 -2
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/providers/openrouter.py +4 -2
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/llms.json +3 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/main.py +81 -34
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/providers.json +1 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/ai.mjs +1 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/app.css +96 -3
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/ctx.mjs +21 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/index.mjs +2 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/modules/chat/ChatBody.mjs +1 -0
- {llms_py-3.0.1 → 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.1 → llms_py-3.0.2}/llms/ui/modules/layout.mjs +28 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/modules/model-selector.mjs +0 -40
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/utils.mjs +9 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/PKG-INFO +1 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/SOURCES.txt +4 -1
- {llms_py-3.0.1 → llms_py-3.0.2}/pyproject.toml +1 -1
- {llms_py-3.0.1 → 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.1/llms/extensions/gallery/db.py +0 -298
- {llms_py-3.0.1 → llms_py-3.0.2}/LICENSE +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/MANIFEST.in +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/__init__.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/__main__.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/analytics/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/ui/Recents.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/app/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/CALCULATOR.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/core_tools/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/gallery/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/__init__.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/auto-render.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/mhchem.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex-swap.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex-swap.min.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/katex/ui/katex.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/system_prompts/README.md +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/system_prompts/__init__.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/system_prompts/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/system_prompts/ui/prompts.json +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/tools/__init__.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/extensions/tools/ui/index.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/index.html +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/providers-extra.json +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/App.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/fav.svg +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/chart.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/charts.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/color.js +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/highlight.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/idb.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/marked.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/servicestack-vue.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/vue.min.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/lib/vue.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/markdown.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/tailwind.input.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms/ui/typography.css +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/requirements.txt +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/setup.cfg +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_async.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_config.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_extensions.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_integration.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_provider_checks.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_provider_config.py +0 -0
- {llms_py-3.0.1 → llms_py-3.0.2}/tests/test_utils.py +0 -0
|
@@ -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):
|
|
@@ -32,28 +28,49 @@ def install(ctx):
|
|
|
32
28
|
if not get_db():
|
|
33
29
|
return
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
]
|
|
51
55
|
|
|
52
56
|
def thread_dto(row):
|
|
53
|
-
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
|
+
)
|
|
54
71
|
|
|
55
72
|
def request_dto(row):
|
|
56
|
-
return row and to_dto(row, ["usage"])
|
|
73
|
+
return row and g_db.to_dto(row, ["usage"])
|
|
57
74
|
|
|
58
75
|
def prompt_to_title(prompt):
|
|
59
76
|
return prompt[:100] + ("..." if len(prompt) > 100 else "") if prompt else None
|
|
@@ -67,7 +84,10 @@ def install(ctx):
|
|
|
67
84
|
return messages
|
|
68
85
|
|
|
69
86
|
async def query_threads(request):
|
|
70
|
-
|
|
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))
|
|
71
91
|
dtos = [thread_dto(row) for row in rows]
|
|
72
92
|
return web.json_response(dtos)
|
|
73
93
|
|
|
@@ -81,6 +101,13 @@ def install(ctx):
|
|
|
81
101
|
|
|
82
102
|
ctx.add_post("threads", create_thread)
|
|
83
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
|
+
|
|
84
111
|
async def update_thread(request):
|
|
85
112
|
thread = await request.json()
|
|
86
113
|
id = request.match_info["id"]
|
|
@@ -119,8 +146,10 @@ def install(ctx):
|
|
|
119
146
|
if not thread:
|
|
120
147
|
raise Exception("Thread not found")
|
|
121
148
|
|
|
149
|
+
tools = chat.get("tools", thread.get("tools", []))
|
|
122
150
|
update_thread = {
|
|
123
151
|
"messages": messages,
|
|
152
|
+
"tools": tools,
|
|
124
153
|
"startedAt": datetime.now(),
|
|
125
154
|
"completedAt": None,
|
|
126
155
|
"error": None,
|
|
@@ -164,24 +193,28 @@ def install(ctx):
|
|
|
164
193
|
if not thread:
|
|
165
194
|
raise Exception("Thread not found")
|
|
166
195
|
|
|
167
|
-
metadata = thread.get("metadata"
|
|
196
|
+
metadata = thread.get("metadata") or {}
|
|
168
197
|
chat = {
|
|
169
198
|
"model": thread.get("model"),
|
|
170
199
|
"messages": thread.get("messages"),
|
|
171
200
|
"modalities": thread.get("modalities"),
|
|
172
201
|
"systemPrompt": thread.get("systemPrompt"),
|
|
202
|
+
"tools": thread.get("tools"), # tools request
|
|
173
203
|
"metadata": metadata,
|
|
174
204
|
}
|
|
175
|
-
|
|
205
|
+
args = thread.get("args") or {}
|
|
206
|
+
for k, v in args.items():
|
|
176
207
|
if k in ctx.request_args:
|
|
177
208
|
chat[k] = v
|
|
178
209
|
|
|
210
|
+
ctx.dbg("CHAT\n" + json.dumps(chat, indent=2))
|
|
211
|
+
|
|
179
212
|
context = {
|
|
180
213
|
"chat": chat,
|
|
181
214
|
"user": user,
|
|
182
215
|
"threadId": id,
|
|
183
216
|
"metadata": metadata,
|
|
184
|
-
"tools": metadata.get("tools", "all"),
|
|
217
|
+
"tools": metadata.get("tools", "all"), # only tools: all|none|<tool1>,<tool2>,...
|
|
185
218
|
}
|
|
186
219
|
|
|
187
220
|
# execute chat in background thread
|
|
@@ -295,6 +328,7 @@ def install(ctx):
|
|
|
295
328
|
metadata = chat.get("metadata", {})
|
|
296
329
|
model = chat.get("model", None)
|
|
297
330
|
messages = timestamp_messages(chat.get("messages", []))
|
|
331
|
+
tools = chat.get("tools", [])
|
|
298
332
|
title = context.get("title") or prompt_to_title(ctx.last_user_prompt(chat) if chat else None)
|
|
299
333
|
started_at = context.get("startedAt")
|
|
300
334
|
if not started_at:
|
|
@@ -307,6 +341,7 @@ def install(ctx):
|
|
|
307
341
|
"modelInfo": model_info,
|
|
308
342
|
"title": title,
|
|
309
343
|
"messages": messages,
|
|
344
|
+
"tools": tools,
|
|
310
345
|
"systemPrompt": ctx.chat_to_system_prompt(chat),
|
|
311
346
|
"modalities": chat.get("modalities", ["text"]),
|
|
312
347
|
"startedAt": started_at,
|
|
@@ -321,6 +356,7 @@ def install(ctx):
|
|
|
321
356
|
"modelInfo": model_info,
|
|
322
357
|
"startedAt": started_at,
|
|
323
358
|
"messages": messages,
|
|
359
|
+
"tools": tools,
|
|
324
360
|
"completedAt": None,
|
|
325
361
|
"error": None,
|
|
326
362
|
"metadata": metadata,
|
|
@@ -355,6 +391,29 @@ def install(ctx):
|
|
|
355
391
|
|
|
356
392
|
ctx.register_chat_tool_filter(tool_request)
|
|
357
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
|
+
|
|
358
417
|
async def chat_response(openai_response, context):
|
|
359
418
|
ctx.dbg("create_response")
|
|
360
419
|
o = openai_response
|
|
@@ -406,7 +465,6 @@ def install(ctx):
|
|
|
406
465
|
"totalTokens": total_tokens,
|
|
407
466
|
"usage": usage,
|
|
408
467
|
"completedAt": completed_at,
|
|
409
|
-
"toolHistory": o.get("tool_history", None),
|
|
410
468
|
"ref": o.get("id", None),
|
|
411
469
|
}
|
|
412
470
|
tasks.append(g_db.create_request_async(request, user=user))
|
|
@@ -436,15 +494,23 @@ def install(ctx):
|
|
|
436
494
|
}
|
|
437
495
|
messages.append(assistant_message)
|
|
438
496
|
|
|
497
|
+
tools = chat.get("tools", [])
|
|
439
498
|
update_thread = {
|
|
440
499
|
"model": model,
|
|
441
500
|
"providerModel": o.get("model"),
|
|
442
501
|
"modelInfo": model_info,
|
|
443
502
|
"messages": messages,
|
|
503
|
+
"tools": tools,
|
|
444
504
|
"completedAt": completed_at,
|
|
445
505
|
}
|
|
506
|
+
tool_history = o.get("tool_history", None)
|
|
507
|
+
if tool_history:
|
|
508
|
+
update_thread["toolHistory"] = tool_history
|
|
446
509
|
if "error" in metadata:
|
|
447
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)
|
|
448
514
|
tasks.append(g_db.update_thread_async(thread_id, update_thread, user=user))
|
|
449
515
|
else:
|
|
450
516
|
ctx.dbg("Missing thread_id")
|