llms-py 2.0.21__tar.gz → 3.0.18__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-2.0.21 → llms_py-3.0.18}/LICENSE +1 -2
- llms_py-3.0.18/PKG-INFO +49 -0
- llms_py-3.0.18/README.md +9 -0
- llms_py-3.0.18/llms/__init__.py +4 -0
- llms_py-3.0.18/llms/db.py +359 -0
- llms_py-2.0.21/llms/ui/Analytics.mjs → llms_py-3.0.18/llms/extensions/analytics/ui/index.mjs +254 -327
- llms_py-3.0.18/llms/extensions/app/README.md +20 -0
- llms_py-3.0.18/llms/extensions/app/__init__.py +588 -0
- llms_py-3.0.18/llms/extensions/app/db.py +540 -0
- {llms_py-2.0.21/llms → llms_py-3.0.18/llms/extensions/app}/ui/Recents.mjs +99 -73
- llms_py-2.0.21/llms/ui/Sidebar.mjs → llms_py-3.0.18/llms/extensions/app/ui/index.mjs +139 -68
- llms_py-3.0.18/llms/extensions/app/ui/threadStore.mjs +440 -0
- llms_py-3.0.18/llms/extensions/computer/README.md +96 -0
- llms_py-3.0.18/llms/extensions/computer/__init__.py +59 -0
- llms_py-3.0.18/llms/extensions/computer/base.py +80 -0
- llms_py-3.0.18/llms/extensions/computer/bash.py +185 -0
- llms_py-3.0.18/llms/extensions/computer/computer.py +523 -0
- llms_py-3.0.18/llms/extensions/computer/edit.py +299 -0
- llms_py-3.0.18/llms/extensions/computer/filesystem.py +542 -0
- llms_py-3.0.18/llms/extensions/computer/platform.py +461 -0
- llms_py-3.0.18/llms/extensions/computer/run.py +37 -0
- llms_py-3.0.18/llms/extensions/core_tools/CALCULATOR.md +32 -0
- llms_py-3.0.18/llms/extensions/core_tools/__init__.py +599 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +201 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +185 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +101 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +160 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +66 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +27 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +72 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +119 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +98 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/codemirror.css +344 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/codemirror.js +9884 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/doc/docs.css +225 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +942 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +118 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +962 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +62 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +402 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +40 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +135 -0
- llms_py-3.0.18/llms/extensions/core_tools/ui/index.mjs +650 -0
- llms_py-3.0.18/llms/extensions/gallery/README.md +61 -0
- llms_py-3.0.18/llms/extensions/gallery/__init__.py +63 -0
- llms_py-3.0.18/llms/extensions/gallery/db.py +243 -0
- llms_py-3.0.18/llms/extensions/gallery/ui/index.mjs +482 -0
- llms_py-3.0.18/llms/extensions/katex/README.md +39 -0
- llms_py-3.0.18/llms/extensions/katex/__init__.py +6 -0
- llms_py-3.0.18/llms/extensions/katex/ui/README.md +125 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/auto-render.js +338 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/auto-render.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/auto-render.mjs +244 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/copy-tex.js +127 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/copy-tex.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/copy-tex.mjs +105 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mathtex-script-type.js +109 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +24 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mhchem.js +3213 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mhchem.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/mhchem.mjs +3109 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/render-a11y-string.js +887 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +800 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- llms_py-3.0.18/llms/extensions/katex/ui/index.mjs +92 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex-swap.css +1230 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex-swap.min.css +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.css +1230 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.js +19080 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.min.css +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.min.js +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.min.mjs +1 -0
- llms_py-3.0.18/llms/extensions/katex/ui/katex.mjs +18547 -0
- llms_py-3.0.18/llms/extensions/providers/__init__.py +22 -0
- llms_py-3.0.18/llms/extensions/providers/anthropic.py +260 -0
- llms_py-3.0.18/llms/extensions/providers/cerebras.py +36 -0
- llms_py-3.0.18/llms/extensions/providers/chutes.py +153 -0
- llms_py-3.0.18/llms/extensions/providers/google.py +559 -0
- llms_py-3.0.18/llms/extensions/providers/nvidia.py +103 -0
- llms_py-3.0.18/llms/extensions/providers/openai.py +154 -0
- llms_py-3.0.18/llms/extensions/providers/openrouter.py +74 -0
- llms_py-3.0.18/llms/extensions/providers/zai.py +182 -0
- llms_py-3.0.18/llms/extensions/skills/LICENSE +202 -0
- llms_py-3.0.18/llms/extensions/skills/__init__.py +130 -0
- llms_py-3.0.18/llms/extensions/skills/errors.py +25 -0
- llms_py-3.0.18/llms/extensions/skills/models.py +39 -0
- llms_py-3.0.18/llms/extensions/skills/parser.py +178 -0
- llms_py-3.0.18/llms/extensions/skills/ui/index.mjs +376 -0
- llms_py-3.0.18/llms/extensions/skills/ui/skills/create-plan/SKILL.md +74 -0
- llms_py-3.0.18/llms/extensions/skills/validator.py +177 -0
- llms_py-3.0.18/llms/extensions/system_prompts/README.md +22 -0
- llms_py-3.0.18/llms/extensions/system_prompts/__init__.py +45 -0
- llms_py-3.0.18/llms/extensions/system_prompts/ui/index.mjs +276 -0
- llms_py-3.0.18/llms/extensions/system_prompts/ui/prompts.json +1067 -0
- llms_py-3.0.18/llms/extensions/tools/__init__.py +67 -0
- llms_py-3.0.18/llms/extensions/tools/ui/index.mjs +837 -0
- llms_py-3.0.18/llms/index.html +58 -0
- llms_py-3.0.18/llms/llms.json +400 -0
- llms_py-3.0.18/llms/main.py +4763 -0
- llms_py-3.0.18/llms/providers-extra.json +394 -0
- llms_py-3.0.18/llms/providers.json +1 -0
- llms_py-3.0.18/llms/ui/App.mjs +188 -0
- llms_py-3.0.18/llms/ui/ai.mjs +217 -0
- llms_py-3.0.18/llms/ui/app.css +7611 -0
- llms_py-3.0.18/llms/ui/ctx.mjs +459 -0
- llms_py-3.0.18/llms/ui/index.mjs +131 -0
- llms_py-3.0.18/llms/ui/lib/charts.mjs +16 -0
- llms_py-3.0.18/llms/ui/lib/servicestack-vue.mjs +37 -0
- llms_py-3.0.18/llms/ui/lib/vue.min.mjs +13 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/vue.mjs +1796 -1635
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/markdown.mjs +25 -14
- llms_py-3.0.18/llms/ui/modules/chat/ChatBody.mjs +1156 -0
- {llms_py-2.0.21/llms/ui → llms_py-3.0.18/llms/ui/modules/chat}/SettingsDialog.mjs +74 -74
- llms_py-3.0.18/llms/ui/modules/chat/index.mjs +995 -0
- llms_py-3.0.18/llms/ui/modules/icons.mjs +46 -0
- llms_py-3.0.18/llms/ui/modules/layout.mjs +271 -0
- llms_py-3.0.18/llms/ui/modules/model-selector.mjs +811 -0
- llms_py-3.0.18/llms/ui/tailwind.input.css +752 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/typography.css +54 -36
- llms_py-3.0.18/llms/ui/utils.mjs +285 -0
- llms_py-3.0.18/llms_py.egg-info/PKG-INFO +49 -0
- llms_py-3.0.18/llms_py.egg-info/SOURCES.txt +212 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/pyproject.toml +58 -3
- {llms_py-2.0.21 → llms_py-3.0.18}/requirements.txt +1 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/setup.py +7 -6
- llms_py-3.0.18/tests/test_async.py +116 -0
- llms_py-3.0.18/tests/test_config.py +89 -0
- llms_py-3.0.18/tests/test_core_tools_direct.py +38 -0
- llms_py-3.0.18/tests/test_extensions.py +48 -0
- llms_py-3.0.18/tests/test_gemini_tool_calling.py +101 -0
- llms_py-3.0.18/tests/test_gemini_upload.py +71 -0
- llms_py-3.0.18/tests/test_integration.py +116 -0
- llms_py-3.0.18/tests/test_interleaved_thinking.py +60 -0
- llms_py-3.0.18/tests/test_provider_checks.py +150 -0
- llms_py-3.0.18/tests/test_provider_config.py +235 -0
- llms_py-3.0.18/tests/test_utils.py +306 -0
- llms_py-2.0.21/PKG-INFO +0 -931
- llms_py-2.0.21/README.md +0 -891
- llms_py-2.0.21/llms/__init__.py +0 -2
- llms_py-2.0.21/llms/index.html +0 -84
- llms_py-2.0.21/llms/llms.json +0 -1099
- llms_py-2.0.21/llms/main.py +0 -1666
- llms_py-2.0.21/llms/ui/App.mjs +0 -20
- llms_py-2.0.21/llms/ui/Avatar.mjs +0 -28
- llms_py-2.0.21/llms/ui/Brand.mjs +0 -34
- llms_py-2.0.21/llms/ui/ChatPrompt.mjs +0 -443
- llms_py-2.0.21/llms/ui/Main.mjs +0 -740
- llms_py-2.0.21/llms/ui/ModelSelector.mjs +0 -60
- llms_py-2.0.21/llms/ui/ProviderIcon.mjs +0 -29
- llms_py-2.0.21/llms/ui/ProviderStatus.mjs +0 -105
- llms_py-2.0.21/llms/ui/SignIn.mjs +0 -64
- llms_py-2.0.21/llms/ui/SystemPromptEditor.mjs +0 -31
- llms_py-2.0.21/llms/ui/SystemPromptSelector.mjs +0 -36
- llms_py-2.0.21/llms/ui/Welcome.mjs +0 -8
- llms_py-2.0.21/llms/ui/ai.mjs +0 -81
- llms_py-2.0.21/llms/ui/app.css +0 -4164
- llms_py-2.0.21/llms/ui/lib/charts.mjs +0 -20
- llms_py-2.0.21/llms/ui/lib/servicestack-vue.mjs +0 -37
- llms_py-2.0.21/llms/ui/lib/vue.min.mjs +0 -12
- llms_py-2.0.21/llms/ui/tailwind.input.css +0 -270
- llms_py-2.0.21/llms/ui/threadStore.mjs +0 -524
- llms_py-2.0.21/llms/ui/utils.mjs +0 -156
- llms_py-2.0.21/llms/ui.json +0 -1069
- llms_py-2.0.21/llms_py.egg-info/PKG-INFO +0 -931
- llms_py-2.0.21/llms_py.egg-info/SOURCES.txt +0 -54
- {llms_py-2.0.21 → llms_py-3.0.18}/MANIFEST.in +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/__main__.py +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/fav.svg +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/chart.js +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/color.js +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/highlight.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/idb.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/marked.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/servicestack-client.mjs +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms/ui/lib/vue-router.min.mjs +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms_py.egg-info/dependency_links.txt +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms_py.egg-info/entry_points.txt +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms_py.egg-info/not-zip-safe +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms_py.egg-info/requires.txt +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/llms_py.egg-info/top_level.txt +0 -0
- {llms_py-2.0.21 → llms_py-3.0.18}/setup.cfg +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
Copyright (c) 2007-present, Demis Bellot, ServiceStack, Inc.
|
|
2
2
|
https://servicestack.net
|
|
3
|
-
All rights reserved.
|
|
4
3
|
|
|
5
4
|
Redistribution and use in source and binary forms, with or without
|
|
6
5
|
modification, are permitted provided that the following conditions are met:
|
|
@@ -9,7 +8,7 @@ modification, are permitted provided that the following conditions are met:
|
|
|
9
8
|
* Redistributions in binary form must reproduce the above copyright
|
|
10
9
|
notice, this list of conditions and the following disclaimer in the
|
|
11
10
|
documentation and/or other materials provided with the distribution.
|
|
12
|
-
* Neither the name of the
|
|
11
|
+
* Neither the name of the copyright holder nor the
|
|
13
12
|
names of its contributors may be used to endorse or promote products
|
|
14
13
|
derived from this software without specific prior written permission.
|
|
15
14
|
|
llms_py-3.0.18/PKG-INFO
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: llms-py
|
|
3
|
+
Version: 3.0.18
|
|
4
|
+
Summary: A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers
|
|
5
|
+
Home-page: https://github.com/ServiceStack/llms
|
|
6
|
+
Author: ServiceStack
|
|
7
|
+
Author-email: ServiceStack <team@servicestack.net>
|
|
8
|
+
Maintainer-email: ServiceStack <team@servicestack.net>
|
|
9
|
+
License-Expression: BSD-3-Clause
|
|
10
|
+
Project-URL: Homepage, https://github.com/ServiceStack/llms
|
|
11
|
+
Project-URL: Documentation, https://github.com/ServiceStack/llms#readme
|
|
12
|
+
Project-URL: Repository, https://github.com/ServiceStack/llms
|
|
13
|
+
Project-URL: Bug Reports, https://github.com/ServiceStack/llms/issues
|
|
14
|
+
Keywords: llm,ai,openai,anthropic,google,gemini,groq,mistral,ollama,cli,server,chat,completion
|
|
15
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
28
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
29
|
+
Classifier: Topic :: System :: Systems Administration
|
|
30
|
+
Classifier: Topic :: Utilities
|
|
31
|
+
Classifier: Environment :: Console
|
|
32
|
+
Requires-Python: >=3.7
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Requires-Dist: aiohttp
|
|
36
|
+
Dynamic: author
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
|
|
41
|
+
# llms.py
|
|
42
|
+
|
|
43
|
+
Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing multiple LLMs, entirely offline, with all data kept private in browser storage.
|
|
44
|
+
|
|
45
|
+
[llmspy.org](https://llmspy.org)
|
|
46
|
+
|
|
47
|
+
[](https://llmspy.org)
|
|
48
|
+
|
|
49
|
+
GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
|
llms_py-3.0.18/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# llms.py
|
|
2
|
+
|
|
3
|
+
Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing multiple LLMs, entirely offline, with all data kept private in browser storage.
|
|
4
|
+
|
|
5
|
+
[llmspy.org](https://llmspy.org)
|
|
6
|
+
|
|
7
|
+
[](https://llmspy.org)
|
|
8
|
+
|
|
9
|
+
GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import sqlite3
|
|
4
|
+
import threading
|
|
5
|
+
from queue import Empty, Queue
|
|
6
|
+
from threading import Event, Thread
|
|
7
|
+
|
|
8
|
+
POOL = os.getenv("LLMS_POOL", "0") == "1"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_reader_connection(db_path):
|
|
12
|
+
# isolation_level=None leaves the connection in autocommit mode
|
|
13
|
+
conn = sqlite3.connect(
|
|
14
|
+
db_path, timeout=1.0, check_same_thread=False, isolation_level=None
|
|
15
|
+
) # Lower - reads should be fast
|
|
16
|
+
conn.execute("PRAGMA query_only=1") # Read-only optimization
|
|
17
|
+
return conn
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_writer_connection(db_path):
|
|
21
|
+
conn = sqlite3.connect(db_path)
|
|
22
|
+
conn.execute("PRAGMA busy_timeout=5000") # Reasonable timeout for busy connections
|
|
23
|
+
conn.execute("PRAGMA journal_mode=WAL") # Enable WAL mode for better concurrency
|
|
24
|
+
conn.execute("PRAGMA cache_size=-128000") # Increase cache size for better performance
|
|
25
|
+
conn.execute("PRAGMA synchronous=NORMAL") # Reasonable durability/performance balance
|
|
26
|
+
return conn
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def writer_thread(ctx, db_path, task_queue, stop_event):
|
|
30
|
+
conn = create_writer_connection(db_path)
|
|
31
|
+
try:
|
|
32
|
+
while not stop_event.is_set():
|
|
33
|
+
try:
|
|
34
|
+
# Use timeout to check stop_event periodically
|
|
35
|
+
task = task_queue.get(timeout=0.1)
|
|
36
|
+
|
|
37
|
+
if task is None: # Poison pill for clean shutdown
|
|
38
|
+
break
|
|
39
|
+
|
|
40
|
+
sql, args, callback = task # Optional callback for results
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
ctx.dbg("SQL>" + ("\n" if "\n" in sql else " ") + sql + ("\n" if args else " ") + str(args))
|
|
44
|
+
cursor = conn.execute(sql, args)
|
|
45
|
+
conn.commit()
|
|
46
|
+
ctx.dbg(f"lastrowid {cursor.lastrowid}, rowcount {cursor.rowcount}")
|
|
47
|
+
if callback:
|
|
48
|
+
callback(cursor.lastrowid, cursor.rowcount)
|
|
49
|
+
except sqlite3.Error as e:
|
|
50
|
+
ctx.err("writer_thread", e)
|
|
51
|
+
if callback:
|
|
52
|
+
callback(None, None, error=e)
|
|
53
|
+
finally:
|
|
54
|
+
task_queue.task_done()
|
|
55
|
+
|
|
56
|
+
except Empty:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
finally:
|
|
60
|
+
conn.close()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def to_dto(ctx, row, json_columns):
|
|
64
|
+
# as=column -> [0,1,2]
|
|
65
|
+
if not isinstance(row, dict):
|
|
66
|
+
return row
|
|
67
|
+
|
|
68
|
+
to = {}
|
|
69
|
+
for k, v in row.items():
|
|
70
|
+
if k in json_columns and v is not None and isinstance(v, str):
|
|
71
|
+
try:
|
|
72
|
+
to[k] = json.loads(v)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(f"Failed to parse JSON for {k}: {v} ({type(v)})", e)
|
|
75
|
+
to[k] = v
|
|
76
|
+
else:
|
|
77
|
+
to[k] = v
|
|
78
|
+
return to
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def valid_columns(all_columns, fields):
|
|
82
|
+
if fields:
|
|
83
|
+
if not isinstance(fields, list):
|
|
84
|
+
fields = fields.split(",")
|
|
85
|
+
cols = []
|
|
86
|
+
for k in fields:
|
|
87
|
+
k = k.strip()
|
|
88
|
+
if k in all_columns:
|
|
89
|
+
cols.append(k)
|
|
90
|
+
return cols
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def table_columns(all_columns, fields):
|
|
95
|
+
cols = valid_columns(all_columns, fields)
|
|
96
|
+
return ", ".join(cols) if len(cols) > 0 else ", ".join(all_columns)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def select_columns(all_columns, fields, select=None):
|
|
100
|
+
columns = table_columns(all_columns, fields)
|
|
101
|
+
if select == "distinct":
|
|
102
|
+
return f"SELECT DISTINCT {columns}"
|
|
103
|
+
return f"SELECT {columns}"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def order_by(all_columns, sort):
|
|
107
|
+
cols = []
|
|
108
|
+
for k in sort.split(","):
|
|
109
|
+
k = k.strip()
|
|
110
|
+
by = ""
|
|
111
|
+
if k[0] == "-":
|
|
112
|
+
by = " DESC"
|
|
113
|
+
k = k[1:]
|
|
114
|
+
if k in all_columns:
|
|
115
|
+
cols.append(f"{k}{by}")
|
|
116
|
+
return f"ORDER BY {', '.join(cols)} " if len(cols) > 0 else ""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class DbManager:
|
|
120
|
+
def __init__(self, ctx, db_path, clone=None):
|
|
121
|
+
if db_path is None:
|
|
122
|
+
raise ValueError("db_path is required")
|
|
123
|
+
self.ctx = ctx
|
|
124
|
+
self.db_path = db_path
|
|
125
|
+
self.read_only_pool = Queue()
|
|
126
|
+
if not clone:
|
|
127
|
+
self.task_queue = Queue()
|
|
128
|
+
self.stop_event = Event()
|
|
129
|
+
self.writer_thread = Thread(target=writer_thread, args=(ctx, db_path, self.task_queue, self.stop_event))
|
|
130
|
+
self.writer_thread.start()
|
|
131
|
+
else:
|
|
132
|
+
# share singleton writer thread in clones
|
|
133
|
+
self.task_queue = clone.task_queue
|
|
134
|
+
self.stop_event = clone.stop_event
|
|
135
|
+
self.writer_thread = clone.writer_thread
|
|
136
|
+
|
|
137
|
+
def create_reader_connection(self):
|
|
138
|
+
return create_reader_connection(self.db_path)
|
|
139
|
+
|
|
140
|
+
def create_writer_connection(self):
|
|
141
|
+
return create_writer_connection(self.db_path)
|
|
142
|
+
|
|
143
|
+
def resolve_connection(self):
|
|
144
|
+
if POOL:
|
|
145
|
+
try:
|
|
146
|
+
return self.read_only_pool.get_nowait()
|
|
147
|
+
except Empty:
|
|
148
|
+
return self.create_reader_connection()
|
|
149
|
+
else:
|
|
150
|
+
return self.create_reader_connection()
|
|
151
|
+
|
|
152
|
+
def release_connection(self, conn):
|
|
153
|
+
if POOL:
|
|
154
|
+
conn.rollback()
|
|
155
|
+
self.read_only_pool.put(conn)
|
|
156
|
+
else:
|
|
157
|
+
conn.close()
|
|
158
|
+
|
|
159
|
+
def write(self, query, args=None, callback=None):
|
|
160
|
+
"""
|
|
161
|
+
Execute a write operation asynchronously.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
query (str): The SQL query to execute.
|
|
165
|
+
args (tuple, optional): Arguments for the query.
|
|
166
|
+
callback (callable, optional): A function called after execution with signature:
|
|
167
|
+
callback(lastrowid, rowcount, error=None)
|
|
168
|
+
- lastrowid (int): output of cursor.lastrowid
|
|
169
|
+
- rowcount (int): output of cursor.rowcount
|
|
170
|
+
- error (Exception): exception if operation failed, else None
|
|
171
|
+
"""
|
|
172
|
+
self.task_queue.put((query, args, callback))
|
|
173
|
+
|
|
174
|
+
def log_sql(self, sql, parameters=None):
|
|
175
|
+
if self.ctx.debug:
|
|
176
|
+
self.ctx.dbg(
|
|
177
|
+
"SQL>" + ("\n" if "\n" in sql else " ") + sql + ("\n" if parameters else " ") + str(parameters)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def exec(self, connection, sql, parameters=None):
|
|
181
|
+
self.log_sql(sql, parameters)
|
|
182
|
+
return connection.execute(sql, parameters or ())
|
|
183
|
+
|
|
184
|
+
def all(self, sql, parameters=None, connection=None):
|
|
185
|
+
"""
|
|
186
|
+
Execute a query and return all rows as a list of dictionaries.
|
|
187
|
+
"""
|
|
188
|
+
conn = self.resolve_connection() if connection is None else connection
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
self.log_sql(sql, parameters)
|
|
192
|
+
conn.row_factory = sqlite3.Row
|
|
193
|
+
cursor = conn.execute(sql, parameters or ())
|
|
194
|
+
rows = [dict(row) for row in cursor.fetchall()]
|
|
195
|
+
return rows
|
|
196
|
+
finally:
|
|
197
|
+
if connection is None:
|
|
198
|
+
conn.row_factory = None
|
|
199
|
+
self.release_connection(conn)
|
|
200
|
+
|
|
201
|
+
def one(self, sql, parameters=None, connection=None):
|
|
202
|
+
"""
|
|
203
|
+
Execute a query and return the first row as a dictionary.
|
|
204
|
+
"""
|
|
205
|
+
conn = self.resolve_connection() if connection is None else connection
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
self.log_sql(sql, parameters)
|
|
209
|
+
conn.row_factory = sqlite3.Row
|
|
210
|
+
cursor = conn.execute(sql, parameters or ())
|
|
211
|
+
row = cursor.fetchone()
|
|
212
|
+
return dict(row) if row else None
|
|
213
|
+
finally:
|
|
214
|
+
if connection is None:
|
|
215
|
+
conn.row_factory = None
|
|
216
|
+
self.release_connection(conn)
|
|
217
|
+
|
|
218
|
+
def scalar(self, sql, parameters=None, connection=None):
|
|
219
|
+
"""
|
|
220
|
+
Execute a scalar query and return the first column of the first row.
|
|
221
|
+
"""
|
|
222
|
+
conn = self.resolve_connection() if connection is None else connection
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
self.log_sql(sql, parameters)
|
|
226
|
+
conn.row_factory = sqlite3.Row
|
|
227
|
+
cursor = conn.execute(sql, parameters or ())
|
|
228
|
+
row = cursor.fetchone()
|
|
229
|
+
return row[0] if row else None
|
|
230
|
+
finally:
|
|
231
|
+
if connection is None:
|
|
232
|
+
conn.row_factory = None
|
|
233
|
+
self.release_connection(conn)
|
|
234
|
+
|
|
235
|
+
def column(self, sql, parameters=None, connection=None):
|
|
236
|
+
"""
|
|
237
|
+
Execute a 1 column query and return the values as a list.
|
|
238
|
+
"""
|
|
239
|
+
conn = self.resolve_connection() if connection is None else connection
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
self.log_sql(sql, parameters)
|
|
243
|
+
cursor = conn.execute(sql, parameters or ())
|
|
244
|
+
return [row[0] for row in cursor.fetchall()]
|
|
245
|
+
finally:
|
|
246
|
+
if connection is None:
|
|
247
|
+
self.release_connection(conn)
|
|
248
|
+
|
|
249
|
+
def dict(self, sql, parameters=None, connection=None):
|
|
250
|
+
"""
|
|
251
|
+
Execute a 2 column query and return the keys as the first column and the values as the second column.
|
|
252
|
+
"""
|
|
253
|
+
conn = self.resolve_connection() if connection is None else connection
|
|
254
|
+
|
|
255
|
+
try:
|
|
256
|
+
self.log_sql(sql, parameters)
|
|
257
|
+
conn.row_factory = sqlite3.Row
|
|
258
|
+
cursor = conn.execute(sql, parameters or ())
|
|
259
|
+
rows = cursor.fetchall()
|
|
260
|
+
return {row[0]: row[1] for row in rows}
|
|
261
|
+
finally:
|
|
262
|
+
if connection is None:
|
|
263
|
+
conn.row_factory = None
|
|
264
|
+
self.release_connection(conn)
|
|
265
|
+
|
|
266
|
+
# Helper to safely dump JSON if value exists
|
|
267
|
+
def value(self, val):
|
|
268
|
+
if val is None or val == "":
|
|
269
|
+
return None
|
|
270
|
+
if isinstance(val, (dict, list)):
|
|
271
|
+
return json.dumps(val)
|
|
272
|
+
return val
|
|
273
|
+
|
|
274
|
+
def insert(self, table, columns, info, callback=None):
|
|
275
|
+
if not info:
|
|
276
|
+
raise Exception("info is required")
|
|
277
|
+
|
|
278
|
+
args = {}
|
|
279
|
+
known_columns = columns.keys()
|
|
280
|
+
for k, val in info.items():
|
|
281
|
+
if k in known_columns and k != "id":
|
|
282
|
+
args[k] = self.value(val)
|
|
283
|
+
|
|
284
|
+
insert_keys = list(args.keys())
|
|
285
|
+
insert_body = ", ".join(insert_keys)
|
|
286
|
+
insert_values = ", ".join(["?" for _ in insert_keys])
|
|
287
|
+
|
|
288
|
+
sql = f"INSERT INTO {table} ({insert_body}) VALUES ({insert_values})"
|
|
289
|
+
|
|
290
|
+
self.write(sql, tuple(args[k] for k in insert_keys), callback)
|
|
291
|
+
|
|
292
|
+
async def insert_async(self, table, columns, info):
|
|
293
|
+
event = threading.Event()
|
|
294
|
+
|
|
295
|
+
ret = [None, None]
|
|
296
|
+
|
|
297
|
+
def cb(lastrowid, rowcount, error=None):
|
|
298
|
+
nonlocal ret
|
|
299
|
+
if error:
|
|
300
|
+
ret[1] = error
|
|
301
|
+
else:
|
|
302
|
+
ret[0] = lastrowid
|
|
303
|
+
event.set()
|
|
304
|
+
|
|
305
|
+
self.insert(table, columns, info, cb)
|
|
306
|
+
event.wait()
|
|
307
|
+
if ret[1]:
|
|
308
|
+
raise ret[1]
|
|
309
|
+
return ret[0]
|
|
310
|
+
|
|
311
|
+
def update(self, table, columns, info, callback=None):
|
|
312
|
+
if not info:
|
|
313
|
+
raise Exception("info is required")
|
|
314
|
+
|
|
315
|
+
args = {}
|
|
316
|
+
known_columns = columns.keys()
|
|
317
|
+
for k, val in info.items():
|
|
318
|
+
if k in known_columns and k != "id":
|
|
319
|
+
args[k] = self.value(val)
|
|
320
|
+
|
|
321
|
+
update_keys = list(args.keys())
|
|
322
|
+
update_body = ", ".join([f"{k} = :{k}" for k in update_keys])
|
|
323
|
+
|
|
324
|
+
args["id"] = info["id"]
|
|
325
|
+
sql = f"UPDATE {table} SET {update_body} WHERE id = :id"
|
|
326
|
+
|
|
327
|
+
self.write(sql, args, callback)
|
|
328
|
+
|
|
329
|
+
async def update_async(self, table, columns, info):
|
|
330
|
+
event = threading.Event()
|
|
331
|
+
|
|
332
|
+
ret = [None, None]
|
|
333
|
+
|
|
334
|
+
def cb(lastrowid, rowcount, error=None):
|
|
335
|
+
nonlocal ret
|
|
336
|
+
if error:
|
|
337
|
+
ret[1] = error
|
|
338
|
+
else:
|
|
339
|
+
ret[0] = rowcount
|
|
340
|
+
event.set()
|
|
341
|
+
|
|
342
|
+
self.update(table, columns, info, cb)
|
|
343
|
+
event.wait()
|
|
344
|
+
if ret[1]:
|
|
345
|
+
raise ret[1]
|
|
346
|
+
return ret[0]
|
|
347
|
+
|
|
348
|
+
def close(self):
|
|
349
|
+
self.ctx.dbg("Closing database")
|
|
350
|
+
self.stop_event.set()
|
|
351
|
+
self.task_queue.put(None) # Poison pill to signal shutdown
|
|
352
|
+
self.writer_thread.join()
|
|
353
|
+
|
|
354
|
+
while not self.read_only_pool.empty():
|
|
355
|
+
try:
|
|
356
|
+
conn = self.read_only_pool.get_nowait()
|
|
357
|
+
conn.close()
|
|
358
|
+
except Empty:
|
|
359
|
+
break
|