llms-py 3.0.0b9__tar.gz → 3.0.0b10__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.
Files changed (219) hide show
  1. {llms_py-3.0.0b9/llms_py.egg-info → llms_py-3.0.0b10}/PKG-INFO +1 -1
  2. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/main.cpython-314.pyc +0 -0
  3. llms_py-3.0.0b10/llms/extensions/app/README.md +20 -0
  4. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/__init__.py +16 -5
  5. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  6. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  7. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/db.py +7 -6
  8. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/ui/index.mjs +1 -1
  9. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/ui/threadStore.mjs +21 -17
  10. llms_py-3.0.0b10/llms/extensions/core_tools/CALCULATOR.md +32 -0
  11. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/__init__.py +1 -1
  12. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  13. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/index.mjs +4 -4
  14. llms_py-3.0.0b10/llms/extensions/gallery/README.md +61 -0
  15. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/gallery/ui/index.mjs +1 -0
  16. llms_py-3.0.0b10/llms/extensions/katex/README.md +39 -0
  17. llms_py-3.0.0b10/llms/extensions/system_prompts/README.md +22 -0
  18. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/llms.json +9 -12
  19. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/main.py +25 -5
  20. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/ai.mjs +20 -5
  21. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/ctx.mjs +22 -0
  22. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/modules/chat/ChatBody.mjs +43 -27
  23. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/modules/chat/index.mjs +17 -29
  24. {llms_py-3.0.0b9 → llms_py-3.0.0b10/llms_py.egg-info}/PKG-INFO +1 -1
  25. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/SOURCES.txt +5 -1
  26. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/pyproject.toml +1 -1
  27. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/setup.py +1 -1
  28. llms_py-3.0.0b9/llms/ui/modules/chat/HomeTools.mjs +0 -12
  29. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/LICENSE +0 -0
  30. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/MANIFEST.in +0 -0
  31. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/README.md +0 -0
  32. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__init__.py +0 -0
  33. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__main__.py +0 -0
  34. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/__init__.cpython-312.pyc +0 -0
  35. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/__init__.cpython-313.pyc +0 -0
  36. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/__init__.cpython-314.pyc +0 -0
  37. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/__main__.cpython-312.pyc +0 -0
  38. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/__main__.cpython-314.pyc +0 -0
  39. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/llms.cpython-312.pyc +0 -0
  40. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/main.cpython-312.pyc +0 -0
  41. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/main.cpython-313.pyc +0 -0
  42. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/__pycache__/plugins.cpython-314.pyc +0 -0
  43. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/analytics/ui/index.mjs +0 -0
  44. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  45. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/db_manager.py +0 -0
  46. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/requests.json +0 -0
  47. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/threads.json +0 -0
  48. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/app/ui/Recents.mjs +0 -0
  49. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -0
  50. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -0
  51. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -0
  52. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -0
  53. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -0
  54. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -0
  55. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -0
  56. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -0
  57. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -0
  58. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -0
  59. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  60. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -0
  61. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -0
  62. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -0
  63. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -0
  64. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -0
  65. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -0
  66. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -0
  67. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -0
  68. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -0
  69. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/gallery/__init__.py +0 -0
  70. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  71. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  72. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/gallery/db.py +0 -0
  73. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/__init__.py +0 -0
  74. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  75. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/README.md +0 -0
  76. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/auto-render.js +0 -0
  77. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/auto-render.min.js +0 -0
  78. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/auto-render.mjs +0 -0
  79. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/copy-tex.js +0 -0
  80. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -0
  81. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -0
  82. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -0
  83. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -0
  84. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -0
  85. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mhchem.js +0 -0
  86. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mhchem.min.js +0 -0
  87. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/mhchem.mjs +0 -0
  88. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -0
  89. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -0
  90. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -0
  91. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  92. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  93. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  94. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  95. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  96. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  97. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  98. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  99. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  100. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  101. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  102. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  103. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  104. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  105. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  106. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  107. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  108. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  109. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  110. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  111. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  112. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  113. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  114. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  115. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  116. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  117. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  118. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  119. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  120. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  121. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  122. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  123. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  124. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  125. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  126. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  127. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  128. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  129. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  130. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  131. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  132. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  133. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  134. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  135. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  136. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  137. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  138. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  139. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  140. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  141. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  142. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  143. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  144. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  145. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  146. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  147. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  148. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  149. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  150. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  151. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/index.mjs +0 -0
  152. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex-swap.css +0 -0
  153. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex-swap.min.css +0 -0
  154. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.css +0 -0
  155. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.js +0 -0
  156. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.min.css +0 -0
  157. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.min.js +0 -0
  158. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.min.mjs +0 -0
  159. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/katex/ui/katex.mjs +0 -0
  160. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__init__.py +0 -0
  161. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  162. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  163. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  164. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  165. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  166. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
  167. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  168. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/anthropic.py +0 -0
  169. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/chutes.py +0 -0
  170. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/google.py +0 -0
  171. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/nvidia.py +0 -0
  172. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/openai.py +0 -0
  173. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/providers/openrouter.py +0 -0
  174. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/system_prompts/__init__.py +0 -0
  175. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  176. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/system_prompts/ui/index.mjs +0 -0
  177. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/system_prompts/ui/prompts.json +0 -0
  178. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/tools/__init__.py +0 -0
  179. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  180. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/extensions/tools/ui/index.mjs +0 -0
  181. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/index.html +0 -0
  182. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/providers-extra.json +0 -0
  183. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/providers.json +0 -0
  184. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/App.mjs +0 -0
  185. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/app.css +0 -0
  186. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/fav.svg +0 -0
  187. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/index.mjs +0 -0
  188. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/chart.js +0 -0
  189. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/charts.mjs +0 -0
  190. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/color.js +0 -0
  191. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/highlight.min.mjs +0 -0
  192. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/idb.min.mjs +0 -0
  193. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/marked.min.mjs +0 -0
  194. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/servicestack-client.mjs +0 -0
  195. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/servicestack-vue.mjs +0 -0
  196. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/vue-router.min.mjs +0 -0
  197. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/vue.min.mjs +0 -0
  198. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/lib/vue.mjs +0 -0
  199. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/markdown.mjs +0 -0
  200. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
  201. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/modules/layout.mjs +0 -0
  202. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/modules/model-selector.mjs +0 -0
  203. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/tailwind.input.css +0 -0
  204. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/typography.css +0 -0
  205. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms/ui/utils.mjs +0 -0
  206. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/dependency_links.txt +0 -0
  207. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/entry_points.txt +0 -0
  208. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/not-zip-safe +0 -0
  209. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/requires.txt +0 -0
  210. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/llms_py.egg-info/top_level.txt +0 -0
  211. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/requirements.txt +0 -0
  212. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/setup.cfg +0 -0
  213. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_async.py +0 -0
  214. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_config.py +0 -0
  215. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_extensions.py +0 -0
  216. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_integration.py +0 -0
  217. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_provider_checks.py +0 -0
  218. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/tests/test_provider_config.py +0 -0
  219. {llms_py-3.0.0b9 → llms_py-3.0.0b10}/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.0b9
3
+ Version: 3.0.0b10
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
@@ -0,0 +1,20 @@
1
+ # App Extension
2
+
3
+ This extension provides the core application logic and data persistence for the LLMs platform.
4
+
5
+ ## Data Storage & Architecture
6
+
7
+ ### Server-Side SQLite Migration
8
+ The application has migrated from client-side IndexedDB storage to a robust server-side SQLite solution. This architectural shift ensures better data consistency, improved performance, and enables multi-device access to your chat history.
9
+
10
+ ### Asset Management
11
+ To keep the database efficient and portable, binary assets (images, audio, etc.) are not stored directly in the SQLite database. Instead:
12
+ - All generated assets are stored in the local file system cache at `~/.llms/cache`.
13
+ - The database stores only **relative URLs** pointing to these assets.
14
+ - This approach allows for efficient caching and serving of static media.
15
+
16
+ ### Concurrency Model
17
+ To ensure data integrity and high performance without complex locking mechanisms, the system utilizes a **single background thread** for managing all write operations to the database. This design improves concurrency handling and eliminates database locking issues during high-load scenarios.
18
+
19
+ ### Multi-Tenancy & Security
20
+ When authentication is enabled, data isolation is automatically enforced. All core tables, including `threads` and `requests`, are scoped to the authenticated user, ensuring that users can only access their own data.
@@ -60,7 +60,7 @@ def install(ctx):
60
60
  return to
61
61
 
62
62
  def thread_dto(row):
63
- return row and to_dto(row, ["messages", "modalities", "args", "modelInfo", "stats"])
63
+ return row and to_dto(row, ["messages", "modalities", "args", "modelInfo", "stats", "metadata"])
64
64
 
65
65
  def request_dto(row):
66
66
  return row and to_dto(row, ["usage"])
@@ -174,12 +174,13 @@ def install(ctx):
174
174
  if not thread:
175
175
  raise Exception("Thread not found")
176
176
 
177
+ metadata = thread.get("metadata", {})
177
178
  chat = {
178
179
  "model": thread.get("model"),
179
180
  "messages": thread.get("messages"),
180
181
  "modalities": thread.get("modalities"),
181
182
  "systemPrompt": thread.get("systemPrompt"),
182
- "metadata": thread.get("metadata", {}),
183
+ "metadata": metadata,
183
184
  }
184
185
  for k, v in thread.get("args", {}).items():
185
186
  if k in ctx.request_args:
@@ -189,7 +190,8 @@ def install(ctx):
189
190
  "chat": chat,
190
191
  "user": user,
191
192
  "threadId": id,
192
- "tools": chat.get("metadata").get("tools", "all"),
193
+ "metadata": metadata,
194
+ "tools": metadata.get("tools", "all"),
193
195
  }
194
196
 
195
197
  # execute chat in background thread
@@ -341,13 +343,22 @@ def install(ctx):
341
343
 
342
344
  ctx.register_chat_request_filter(chat_request)
343
345
 
344
- async def tool_request(tool_chat, context):
345
- ctx.dbg("tool_request")
346
+ async def tool_request(chat_request, context):
347
+ messages = chat_request.get("messages", [])
348
+ ctx.dbg(f"tool_request: messages {len(messages)}")
346
349
  thread_id = context.get("threadId", None)
347
350
  if not thread_id:
348
351
  ctx.dbg("Missing threadId")
349
352
  return
350
353
  user = context.get("user", None)
354
+ await g_db.update_thread_async(
355
+ thread_id,
356
+ {
357
+ "messages": messages,
358
+ },
359
+ user=user,
360
+ )
361
+
351
362
  completed_at = g_db.get_thread_column(thread_id, "completedAt", user=user)
352
363
  if completed_at:
353
364
  context["completed"] = True
@@ -91,6 +91,7 @@ class AppDB:
91
91
  "publishedAt": "TIMESTAMP",
92
92
  "startedAt": "TIMESTAMP",
93
93
  "completedAt": "TIMESTAMP",
94
+ "metadata": "JSON",
94
95
  "error": "TEXT",
95
96
  "ref": "TEXT",
96
97
  },
@@ -444,7 +445,7 @@ class AppDB:
444
445
  event.wait()
445
446
  return ret[0]
446
447
 
447
- def prepare_thread(self, thread, id=None):
448
+ def prepare_thread(self, thread, id=None, user=None):
448
449
  now = datetime.now()
449
450
  if id:
450
451
  thread["id"] = id
@@ -454,19 +455,19 @@ class AppDB:
454
455
  if "messages" in thread:
455
456
  for m in thread["messages"]:
456
457
  self.ctx.cache_message_inline_data(m)
457
- return thread
458
+ return with_user(thread, user=user)
458
459
 
459
460
  def create_thread(self, thread: Dict[str, Any], user=None):
460
- return self.insert("thread", with_user(self.prepare_thread(thread), user=user))
461
+ return self.insert("thread", self.prepare_thread(thread, user=user))
461
462
 
462
463
  async def create_thread_async(self, thread: Dict[str, Any], user=None):
463
- return await self.insert_async("thread", with_user(self.prepare_thread(thread), user=user))
464
+ return await self.insert_async("thread", self.prepare_thread(thread, user=user))
464
465
 
465
466
  def update_thread(self, id, thread: Dict[str, Any], user=None):
466
- return self.update("thread", with_user(self.prepare_thread(thread, id), user=user))
467
+ return self.update("thread", self.prepare_thread(thread, id, user=user))
467
468
 
468
469
  async def update_thread_async(self, id, thread: Dict[str, Any], user=None):
469
- return await self.update_async("thread", with_user(self.prepare_thread(thread, id), user=user))
470
+ return await self.update_async("thread", self.prepare_thread(thread, id, user=user))
470
471
 
471
472
  def delete_thread(self, id, user=None, callback=None):
472
473
  sql_where, params = self.get_user_filter(user, {"id": id})
@@ -219,7 +219,7 @@ const ThreadsSidebar = {
219
219
  }
220
220
 
221
221
  const createNewThread = async () => {
222
- ctx.threads.startNewThread({ title: 'New Chat', model: ctx.chat.getSelectedModel() })
222
+ ctx.threads.startNewThread({ title: 'New Chat', model: ctx.chat.getSelectedModel(), redirect: true })
223
223
  }
224
224
 
225
225
  const goToInitialState = () => {
@@ -1,5 +1,5 @@
1
1
  import { ref, computed } from 'vue'
2
- import { appendQueryString, createErrorStatus } from '@servicestack/client'
2
+ import { appendQueryString } from '@servicestack/client'
3
3
 
4
4
  /**
5
5
  * Returns an ever-increasing unique integer id.
@@ -21,11 +21,6 @@ const isLoading = ref(false)
21
21
  let ctx = null
22
22
  let ext = null
23
23
 
24
- // Generate unique thread ID
25
- function generateThreadId() {
26
- return Date.now().toString()
27
- }
28
-
29
24
  function setError(error, msg = null) {
30
25
  ctx?.setError(error, msg)
31
26
  }
@@ -170,7 +165,7 @@ async function redoMessageFromThread(threadId, timestamp) {
170
165
  // Find the index of the message to redo
171
166
  const messageIndex = thread.messages.findIndex(m => m.timestamp === timestamp)
172
167
  if (messageIndex === -1) {
173
- setError(createErrorStatus(`Message not found for timestamp ${timestamp}`))
168
+ setError({ message: `Message not found for timestamp ${timestamp}` })
174
169
  return
175
170
  }
176
171
 
@@ -188,7 +183,8 @@ async function redoMessageFromThread(threadId, timestamp) {
188
183
  const updatedMessages = thread.messages.slice(0, messageIndex + 1)
189
184
 
190
185
  // Update the thread with the new messages
191
- const api = await queueChat(threadId, { messages: updatedMessages })
186
+ const request = { messages: updatedMessages }
187
+ const api = await queueChat({ request, thread })
192
188
  if (api.response) {
193
189
  replaceThread(api.response)
194
190
  } else {
@@ -311,7 +307,7 @@ function getLatestCachedThread() {
311
307
  return threads.value[0]
312
308
  }
313
309
 
314
- async function startNewThread({ title, model }) {
310
+ async function startNewThread({ title, model, redirect }) {
315
311
  if (!model) {
316
312
  console.error('No model selected')
317
313
  return
@@ -337,8 +333,10 @@ async function startNewThread({ title, model }) {
337
333
  })
338
334
 
339
335
  console.log('newThread', newThread, model)
340
- // Navigate to the new thread URL
341
- ctx.to(`/c/${newThread.id}`)
336
+ if (redirect) {
337
+ // Navigate to the new thread URL
338
+ ctx.to(`/c/${newThread.id}`)
339
+ }
342
340
 
343
341
  // Get the thread to check for duplicates
344
342
  let thread = await getThread(newThread.id)
@@ -346,12 +344,19 @@ async function startNewThread({ title, model }) {
346
344
  return thread
347
345
  }
348
346
 
349
- async function queueChat(threadId, body, options = {}) {
350
- const api = await ctx.postJson(`/ext/app/threads/${threadId}/chat`, {
347
+ async function queueChat(ctxRequest, options = {}) {
348
+ if (!ctxRequest.request) return ctx.createErrorResult({ message: 'No request provided' })
349
+ if (!ctxRequest.thread) return ctx.createErrorResult({ message: 'No thread provided' })
350
+ if (!ctxRequest.request.metadata) {
351
+ ctxRequest.request.metadata = {}
352
+ }
353
+ ctx.chatRequestFilters.forEach(f => f(ctxRequest))
354
+ const { thread, request } = ctxRequest
355
+ const api = await ctx.postJson(`/ext/app/threads/${thread.id}/chat`, {
351
356
  ...options,
352
- body: typeof body == 'string'
353
- ? body
354
- : JSON.stringify(body),
357
+ body: typeof request == 'string'
358
+ ? request
359
+ : JSON.stringify(request),
355
360
  })
356
361
  return api
357
362
  }
@@ -380,7 +385,6 @@ export function useThreadStore() {
380
385
  clearCurrentThread,
381
386
  getGroupedThreads,
382
387
  getLatestCachedThread,
383
- generateThreadId,
384
388
  startNewThread,
385
389
  replaceThread,
386
390
  queueChat,
@@ -0,0 +1,32 @@
1
+ # Calculator
2
+
3
+ A powerful and safe mathematical expression evaluator with a rich web interface.
4
+
5
+ ## Features
6
+
7
+ ### 🖥️ UX Friendly Interface
8
+ Experience a clean, modern interface designed for efficiency. The UI is fully responsive and supports dark mode, seamlessly integrating with the rest of the application.
9
+
10
+ ### 💾 Persistent History
11
+ Never lose track of your calculations. The Calculator automatically saves your history to `localStorage`, ensuring your previous expressions and results are preserved between sessions.
12
+
13
+ ### ⚡ 1-Click Interaction
14
+ Streamline your workflow with interactive history items:
15
+ - **Load & Copy**: Click on any past expression or answer to instantly load it into the input field and copy it to your clipboard.
16
+ - **Visual Feedback**: Temporary checkmarks confirm successful copy actions.
17
+
18
+ ### ⌨️ Keyboard-Free Access
19
+ While full keyboard support is available, you can perform complex calculations entirely via the UI:
20
+ - **Numbers & Constants**: Quick access to digits and mathematical constants like `pi`, `e`, `inf`.
21
+ - **Operators**: Comprehensive set of buttons for arithmetic (`+`, `-`, `*`, `/`, `%`, `^`) and boolean logic (`and`, `or`, `not`).
22
+ - **Functions**: One-click insertion or wrapping of selection for all supported math functions.
23
+
24
+ ### 🐍 Python Math Support
25
+ Unlock the power of Python's math library directly in the browser.
26
+ - **Math Functions**: Support for `sin`, `cos`, `tan`, `sqrt`, `log`, `factorial`, and many more.
27
+ - **Statistics**: Built-in functions for `mean`, `median`, `stdev`, and `variance`.
28
+
29
+ ### 🛡️ Safe Evaluation
30
+ Security is a priority. Instead of using Python's unsafe `eval()`, the Calculator uses a robust **AST (Abstract Syntax Tree) evaluator**.
31
+ - **Restricted Environment**: Only allowed mathematical operations and functions are executed.
32
+ - **No Side Effects**: Prevents arbitrary code execution, making it safe to evaluate expressions from untrusted sources.
@@ -523,7 +523,7 @@ def install(ctx):
523
523
  # Examples of registering tools using automatic definition generation
524
524
  ctx.register_tool(memory_read)
525
525
  ctx.register_tool(memory_write)
526
- ctx.register_tool(semantic_search)
526
+ # ctx.register_tool(semantic_search) # TODO: implement
527
527
  ctx.register_tool(read_file)
528
528
  ctx.register_tool(write_file)
529
529
  ctx.register_tool(list_directory)
@@ -381,7 +381,7 @@ const CalcPage = {
381
381
  type="button"
382
382
  @click="insert(num)"
383
383
  class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
384
- title="Click to insert"
384
+ :title="'insert number ' + num"
385
385
  >
386
386
  {{ num }}
387
387
  </button>
@@ -392,7 +392,7 @@ const CalcPage = {
392
392
  type="button"
393
393
  @click="insert(c)"
394
394
  class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
395
- title="Click to insert"
395
+ :title="'insert constant ' + c"
396
396
  >
397
397
  {{ c }}
398
398
  </button>
@@ -408,7 +408,7 @@ const CalcPage = {
408
408
  type="button"
409
409
  @click="insert(op)"
410
410
  class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300 hover:text-blue-700 dark:hover:text-blue-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
411
- title="Click to insert"
411
+ :title="'insert operator ' + op"
412
412
  >
413
413
  {{ op }}
414
414
  </button>
@@ -425,7 +425,7 @@ const CalcPage = {
425
425
  type="button"
426
426
  @click="wrapWithFunction(func)"
427
427
  class="px-3 py-1 bg-gray-100 dark:bg-gray-800 hover:bg-purple-100 dark:hover:bg-purple-900/30 text-gray-700 dark:text-gray-300 hover:text-purple-700 dark:hover:text-purple-300 border border-gray-200 dark:border-gray-700 rounded text-sm font-mono transition-colors"
428
- title="Click to insert"
428
+ :title="'use function ' + func"
429
429
  >
430
430
  {{ func }}
431
431
  </button>
@@ -0,0 +1,61 @@
1
+ # Gallery Extension
2
+
3
+ The Gallery extension intercepts all generated image, audio & file assets and uploaded files in `~/.llms/cache` file storage whose metadata is maintained in a SQLite database.
4
+
5
+ Dedicated UIs are available for quickly browsing and navigating or generated images / audio files with optimized UIs for viewing portrait, square and landscape images.
6
+
7
+ ## Generated Asset Interception
8
+
9
+ The Gallery extension automatically monitors the creation of new cache entries. Whenever a file is saved to the LLMs cache (located at `~/.llms/cache`), the extension captures its metadata and stores it in the gallery database.
10
+
11
+ This includes:
12
+ - **Generated Images**: Images created by AI models (e.g., DALL-E, Stable Diffusion).
13
+ - **Generated Audio**: Audio files generated by text-to-speech or audio models.
14
+ - **Uploaded Files**: Any files uploaded through the UI.
15
+
16
+ All metadata is stored in a dedicated SQLite database located at `~/.llms/user/default/gallery/gallery.sqlite`, in the `media` table.
17
+
18
+ ## User Interface
19
+
20
+ The Gallery UI provides a rich, interactive way to explore your generated assets. You can access it via the **Gallery** tab in the sidebar or by navigating to `/gallery`.
21
+
22
+ ### Image Gallery
23
+
24
+ The image view offers a responsive grid layout optimized for different aspect ratios.
25
+
26
+ - **Filtering**:
27
+ - **By Format**: Easily switch between **Portrait**, **Square**, and **Landscape** views to see images in their best light.
28
+ - **Search**: Real-time search by prompt, model name, or other metadata.
29
+ - **Interactions**:
30
+ - **Lightbox**: Click any image to view it in full screen.
31
+ - **Details**: View comprehensive metadata including the prompt used, generation model, dimensions, file size, creation date, and generation cost.
32
+ - **Download**: extensive download options.
33
+ - **Remix**: Quickly re-use the prompt and settings of an existing image to generate a new one.
34
+ - **Delete**: Remove unwanted images from the gallery.
35
+
36
+ ### Audio Gallery
37
+
38
+ The audio view presents a list layout designed for easy listening and management.
39
+
40
+ - **Playback**: Integrated audio player to preview generated sounds directly in the list.
41
+ - **Metadata**: Displays the caption/prompt, model, and creation time.
42
+ - **Actions**:
43
+ - **Remix**: Regenerate audio using the same prompt.
44
+ - **Delete**: Remove audio files.
45
+
46
+ ## Storage Data model
47
+
48
+ The `media` table tracks extensive information about each asset to support the search and filtering capabilities:
49
+
50
+ | Column | Description |
51
+ |---|---|
52
+ | `url` | Relative path to the file in `~/.llms/cache` |
53
+ | `type` | Asset type (image, audio, video) |
54
+ | `prompt` | The prompt used to generate the asset |
55
+ | `model` | The AI model used |
56
+ | `aspect_ratio` | Aspect ratio (e.g., "1:1", "16:9") |
57
+ | `cost` | Generation cost |
58
+ | `metadata` | Additional JSON metadata |
59
+ | `created` | Timestamp of creation |
60
+
61
+ This local database ensures your gallery remains fast and responsive, even with a large collection of generated assets.
@@ -390,6 +390,7 @@ const GalleryPage = {
390
390
  ctx.threads.startNewThread({
391
391
  title: selected.prompt,
392
392
  model: ctx.chat.getSelectedModel(),
393
+ redirect: true,
393
394
  })
394
395
  }
395
396
 
@@ -0,0 +1,39 @@
1
+ # KaTeX Extension
2
+
3
+ This extension enables beautiful rendering of LaTeX math expressions in AI responses using [KaTeX](https://katex.org/). It integrates automatically with the markdown parser to render math equations in both inline and block formats.
4
+
5
+ ## Features
6
+
7
+ - **Fast Rendering**: Uses KaTeX for high-performance rendering of math expressions.
8
+ - **Inline Math**: Renders math within text using `$` or `$$` delimiters.
9
+ - **Block Math**: Renders complex equations in their own block using `$` or `$$` delimiters across multiple lines.
10
+ - **Auto-Integration**: Automatically extends the `marked` parser used in the application.
11
+
12
+ ## Usage
13
+
14
+ The extension supports standard LaTeX math syntax.
15
+
16
+ ### Inline Math
17
+
18
+ Surround your LaTeX expression with single `$` (for inline style) or double `$$` (for display style) delimiters.
19
+
20
+ **Example:**
21
+ `The quadratic formula is $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$.`
22
+
23
+ ### Block Math
24
+
25
+ For larger equations or when you want the math to be displayed on its own line, use block syntax by placing the delimiters on separate lines. Standard usage is to use double `$$` delimiters.
26
+
27
+ **Example:**
28
+ ```latex
29
+ $$
30
+ \int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}
31
+ $$
32
+ ```
33
+
34
+ ## Configuration
35
+
36
+ The extension automatically registers:
37
+ - **Import Maps**: Loads `katex.min.mjs` for the frontend.
38
+ - **CSS**: Injects `katex.min.css` for styling.
39
+ - **Markdown Extension**: Adds a custom tokenizer and renderer to `marked` to detect and render LaTeX patterns.
@@ -0,0 +1,22 @@
1
+ # System Prompts Extension
2
+
3
+ This extension configures AI requests with a library of **over 200+** awesome curated system prompts that can be selected from the UI.
4
+
5
+ ## Custom System Prompts
6
+
7
+ You can also maintain your own library of system prompts which can be maintained for all anonymous users at:
8
+ `~/.llms/user/default/system-prompts.json`
9
+
10
+ Or for signed in users at:
11
+ `~/.llms/user/<github-user>/system-prompts.json`
12
+
13
+ The JSON file should contain an array of Prompt objects, e.g:
14
+
15
+ ```json
16
+ [
17
+ {
18
+ "name": "Helpful Assistant",
19
+ "prompt": "You are a helpful assistant."
20
+ }
21
+ ]
22
+ ```
@@ -9,14 +9,11 @@
9
9
  "restrict_to": "GITHUB_USERS"
10
10
  }
11
11
  },
12
- "disable_extensions": [
13
- "xmas",
14
- "duckduckgo"
15
- ],
12
+ "disable_extensions": [],
16
13
  "defaults": {
17
14
  "headers": {
18
15
  "Content-Type": "application/json",
19
- "User-Agent": "llmspy.org/1.0"
16
+ "User-Agent": "llmspy.org/3.0"
20
17
  },
21
18
  "text": {
22
19
  "model": "kimi-k2",
@@ -95,7 +92,7 @@
95
92
  ]
96
93
  },
97
94
  "out:image": {
98
- "model": "black-forest-labs/flux.1-dev",
95
+ "model": "gemini-2.5-flash-image",
99
96
  "messages": [
100
97
  {
101
98
  "role": "user",
@@ -202,10 +199,10 @@
202
199
  }
203
200
  },
204
201
  "github-copilot": {
205
- "enabled": false
202
+ "enabled": true
206
203
  },
207
204
  "github-models": {
208
- "enabled": false,
205
+ "enabled": true,
209
206
  "check": {
210
207
  "messages": [
211
208
  {
@@ -230,13 +227,13 @@
230
227
  "temperature": 1.0
231
228
  },
232
229
  "ollama": {
233
- "enabled": true,
230
+ "enabled": false,
234
231
  "id": "ollama",
235
232
  "npm": "ollama",
236
233
  "api": "http://localhost:11434"
237
234
  },
238
235
  "lmstudio": {
239
- "enabled": true,
236
+ "enabled": false,
240
237
  "npm": "lmstudio",
241
238
  "api": "http://127.0.0.1:1234/v1",
242
239
  "models": {}
@@ -351,7 +348,7 @@
351
348
  "enabled": true
352
349
  },
353
350
  "moonshotai": {
354
- "enabled": false
351
+ "enabled": true
355
352
  },
356
353
  "nvidia": {
357
354
  "enabled": true,
@@ -372,7 +369,7 @@
372
369
  "enabled": true
373
370
  },
374
371
  "fireworks-ai": {
375
- "enabled": false
372
+ "enabled": true
376
373
  },
377
374
  "openrouter": {
378
375
  "enabled": true,
@@ -29,7 +29,7 @@ from importlib import resources # Py≥3.9 (pip install importlib_resources fo
29
29
  from io import BytesIO
30
30
  from pathlib import Path
31
31
  from typing import get_type_hints
32
- from urllib.parse import parse_qs, urlencode
32
+ from urllib.parse import parse_qs, urlencode, urljoin
33
33
 
34
34
  import aiohttp
35
35
  from aiohttp import web
@@ -41,7 +41,7 @@ try:
41
41
  except ImportError:
42
42
  HAS_PIL = False
43
43
 
44
- VERSION = "3.0.0b9"
44
+ VERSION = "3.0.0b10"
45
45
  _ROOT = None
46
46
  DEBUG = os.getenv("DEBUG") == "1"
47
47
  MOCK = os.getenv("MOCK") == "1"
@@ -204,6 +204,12 @@ def id_to_name(id):
204
204
  return id.replace("-", " ").title()
205
205
 
206
206
 
207
+ def pluralize(word, count):
208
+ if count == 1:
209
+ return word
210
+ return word + "s"
211
+
212
+
207
213
  def get_file_mime_type(filename):
208
214
  mime_type, _ = mimetypes.guess_type(filename)
209
215
  return mime_type or "application/octet-stream"
@@ -368,6 +374,9 @@ async def process_chat(chat, provider_id=None):
368
374
  raise Exception("No chat provided")
369
375
  if "stream" not in chat:
370
376
  chat["stream"] = False
377
+ # Some providers don't support empty tools
378
+ if "tools" in chat and len(chat["tools"]) == 0:
379
+ del chat["tools"]
371
380
  if "messages" not in chat:
372
381
  return chat
373
382
 
@@ -700,6 +709,7 @@ def save_image_to_cache(base64_data, filename, image_info, ignore_info=False):
700
709
  async def response_json(response):
701
710
  text = await response.text()
702
711
  if response.status >= 400:
712
+ _dbg(f"HTTP {response.status} {response.reason}: {text}")
703
713
  raise HTTPError(response.status, reason=response.reason, body=text, headers=dict(response.headers))
704
714
  response.raise_for_status()
705
715
  body = json.loads(text)
@@ -1427,6 +1437,8 @@ async def g_chat_completion(chat, context=None):
1427
1437
  current_chat["messages"].append(message)
1428
1438
  tool_history.append(message)
1429
1439
 
1440
+ await g_app.on_chat_tool(current_chat, context)
1441
+
1430
1442
  for tool_call in tool_calls:
1431
1443
  function_name = tool_call["function"]["name"]
1432
1444
  try:
@@ -1450,8 +1462,7 @@ async def g_chat_completion(chat, context=None):
1450
1462
  current_chat["messages"].append(tool_msg)
1451
1463
  tool_history.append(tool_msg)
1452
1464
 
1453
- for filter_func in g_app.chat_tool_filters:
1454
- await filter_func(current_chat, context)
1465
+ await g_app.on_chat_tool(current_chat, context)
1455
1466
 
1456
1467
  if should_cancel_thread(context):
1457
1468
  return
@@ -1606,7 +1617,7 @@ async def cli_chat(chat, tools=None, image=None, audio=None, file=None, args=Non
1606
1617
  for file in generated_files:
1607
1618
  if file.startswith("/~cache"):
1608
1619
  print(get_cache_path(file[8:]))
1609
- print(f"http://localhost:8000/{file}")
1620
+ print(urljoin("http://localhost:8000", file))
1610
1621
  else:
1611
1622
  print(file)
1612
1623
 
@@ -2443,6 +2454,15 @@ class AppExtensions:
2443
2454
  except Exception as e:
2444
2455
  _err("chat error filter failed", e)
2445
2456
 
2457
+ async def on_chat_tool(self, chat, context):
2458
+ m_len = len(chat.get("messages", []))
2459
+ t_len = len(self.chat_tool_filters)
2460
+ _dbg(
2461
+ f"on_tool_call for thread {context.get('threadId', None)} with {m_len} {pluralize('message', m_len)}, invoking {t_len} {pluralize('filter', t_len)}:"
2462
+ )
2463
+ for filter_func in self.chat_tool_filters:
2464
+ await filter_func(chat, context)
2465
+
2446
2466
  def exit(self, exit_code=0):
2447
2467
  if len(self.shutdown_handlers) > 0:
2448
2468
  _dbg(f"running {len(self.shutdown_handlers)} shutdown handlers...")
@@ -6,7 +6,7 @@ const headers = { 'Accept': 'application/json' }
6
6
  const prefsKey = 'llms.prefs'
7
7
 
8
8
  export const o = {
9
- version: '3.0.0b9',
9
+ version: '3.0.0b10',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',
@@ -74,12 +74,27 @@ export const o = {
74
74
  return { responseStatus }
75
75
  }
76
76
  },
77
+ createErrorStatus({ message, errorCode, stackTrace, errors, meta }) {
78
+ const ret = {
79
+ errorCode: errorCode || 'Error',
80
+ message: message,
81
+ }
82
+ if (stackTrace) {
83
+ ret.stackTrace = stackTrace
84
+ }
85
+ if (errors && Array.isArray(errors)) {
86
+ ret.errors = errors
87
+ }
88
+ if (meta) {
89
+ ret.meta = meta
90
+ }
91
+ return ret
92
+ },
77
93
  createErrorResult(e) {
78
94
  return new ApiResult({
79
- responseStatus: {
80
- errorCode: 'Error',
81
- message: `${e.message ?? e}`
82
- }
95
+ error: e.errorCode
96
+ ? this.createErrorStatus(e)
97
+ : this.createErrorStatus({ message: `${e.message ?? e}` })
83
98
  })
84
99
  },
85
100
  async getConfig() {