llms-py 3.0.6__tar.gz → 3.0.8__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 (197) hide show
  1. {llms_py-3.0.6/llms_py.egg-info → llms_py-3.0.8}/PKG-INFO +1 -1
  2. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/analytics/ui/index.mjs +1 -1
  3. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/__init__.py +3 -1
  4. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/ui/Recents.mjs +1 -1
  5. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/ui/threadStore.mjs +4 -3
  6. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/__init__.py +14 -12
  7. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/__init__.py +2 -0
  8. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/anthropic.py +1 -1
  9. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/chutes.py +7 -9
  10. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/google.py +3 -3
  11. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/nvidia.py +9 -11
  12. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/openai.py +1 -3
  13. llms_py-3.0.8/llms/extensions/providers/zai.py +182 -0
  14. llms_py-3.0.8/llms/extensions/tools/__init__.py +144 -0
  15. llms_py-3.0.8/llms/extensions/tools/ui/index.mjs +706 -0
  16. {llms_py-3.0.6 → llms_py-3.0.8}/llms/llms.json +14 -2
  17. {llms_py-3.0.6 → llms_py-3.0.8}/llms/main.py +461 -99
  18. {llms_py-3.0.6 → llms_py-3.0.8}/llms/providers-extra.json +38 -0
  19. llms_py-3.0.8/llms/providers.json +1 -0
  20. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/App.mjs +1 -1
  21. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/ai.mjs +1 -1
  22. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/app.css +287 -18
  23. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/ctx.mjs +16 -3
  24. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/index.mjs +1 -1
  25. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/chat/ChatBody.mjs +384 -107
  26. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/chat/index.mjs +18 -4
  27. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/tailwind.input.css +54 -0
  28. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/utils.mjs +33 -4
  29. {llms_py-3.0.6 → llms_py-3.0.8/llms_py.egg-info}/PKG-INFO +1 -1
  30. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/SOURCES.txt +1 -0
  31. {llms_py-3.0.6 → llms_py-3.0.8}/pyproject.toml +1 -1
  32. {llms_py-3.0.6 → llms_py-3.0.8}/setup.py +1 -1
  33. llms_py-3.0.6/llms/extensions/tools/__init__.py +0 -5
  34. llms_py-3.0.6/llms/extensions/tools/ui/index.mjs +0 -204
  35. llms_py-3.0.6/llms/providers.json +0 -1
  36. {llms_py-3.0.6 → llms_py-3.0.8}/LICENSE +0 -0
  37. {llms_py-3.0.6 → llms_py-3.0.8}/MANIFEST.in +0 -0
  38. {llms_py-3.0.6 → llms_py-3.0.8}/README.md +0 -0
  39. {llms_py-3.0.6 → llms_py-3.0.8}/llms/__init__.py +0 -0
  40. {llms_py-3.0.6 → llms_py-3.0.8}/llms/__main__.py +0 -0
  41. {llms_py-3.0.6 → llms_py-3.0.8}/llms/db.py +0 -0
  42. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/README.md +0 -0
  43. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/db.py +0 -0
  44. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/app/ui/index.mjs +0 -0
  45. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/CALCULATOR.md +0 -0
  46. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -0
  47. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -0
  48. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -0
  49. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -0
  50. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -0
  51. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -0
  52. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -0
  53. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -0
  54. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -0
  55. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/codemirror.css +0 -0
  56. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/codemirror.js +0 -0
  57. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -0
  58. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  59. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -0
  60. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -0
  61. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -0
  62. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -0
  63. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -0
  64. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -0
  65. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -0
  66. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/core_tools/ui/index.mjs +0 -0
  67. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/gallery/README.md +0 -0
  68. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/gallery/__init__.py +0 -0
  69. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/gallery/db.py +0 -0
  70. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/gallery/ui/index.mjs +0 -0
  71. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/README.md +0 -0
  72. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/__init__.py +0 -0
  73. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/README.md +0 -0
  74. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/auto-render.js +0 -0
  75. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/auto-render.min.js +0 -0
  76. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/auto-render.mjs +0 -0
  77. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/copy-tex.js +0 -0
  78. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -0
  79. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -0
  80. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -0
  81. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -0
  82. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -0
  83. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mhchem.js +0 -0
  84. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mhchem.min.js +0 -0
  85. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/mhchem.mjs +0 -0
  86. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -0
  87. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -0
  88. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -0
  89. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  90. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  91. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  92. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  93. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  94. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  95. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  96. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  97. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  98. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  99. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  100. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  101. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  102. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  103. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  104. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  105. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  106. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  107. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  108. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  109. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  110. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  111. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  112. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  113. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  114. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  115. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  116. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  117. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  118. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  119. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  120. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  121. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  122. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  123. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  124. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  125. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  126. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  127. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  128. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  129. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  130. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  131. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  132. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  133. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  134. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  135. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  136. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  137. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  138. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  139. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  140. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  141. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  142. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  143. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  144. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  145. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  146. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  147. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  148. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  149. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/index.mjs +0 -0
  150. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex-swap.css +0 -0
  151. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex-swap.min.css +0 -0
  152. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.css +0 -0
  153. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.js +0 -0
  154. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.min.css +0 -0
  155. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.min.js +0 -0
  156. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.min.mjs +0 -0
  157. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/katex/ui/katex.mjs +0 -0
  158. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/cerebras.py +0 -0
  159. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/providers/openrouter.py +0 -0
  160. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/system_prompts/README.md +0 -0
  161. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/system_prompts/__init__.py +0 -0
  162. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/system_prompts/ui/index.mjs +0 -0
  163. {llms_py-3.0.6 → llms_py-3.0.8}/llms/extensions/system_prompts/ui/prompts.json +0 -0
  164. {llms_py-3.0.6 → llms_py-3.0.8}/llms/index.html +0 -0
  165. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/fav.svg +0 -0
  166. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/chart.js +0 -0
  167. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/charts.mjs +0 -0
  168. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/color.js +0 -0
  169. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/highlight.min.mjs +0 -0
  170. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/idb.min.mjs +0 -0
  171. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/marked.min.mjs +0 -0
  172. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/servicestack-client.mjs +0 -0
  173. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/servicestack-vue.mjs +0 -0
  174. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/vue-router.min.mjs +0 -0
  175. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/vue.min.mjs +0 -0
  176. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/lib/vue.mjs +0 -0
  177. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/markdown.mjs +0 -0
  178. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
  179. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/icons.mjs +0 -0
  180. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/layout.mjs +0 -0
  181. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/modules/model-selector.mjs +0 -0
  182. {llms_py-3.0.6 → llms_py-3.0.8}/llms/ui/typography.css +0 -0
  183. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/dependency_links.txt +0 -0
  184. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/entry_points.txt +0 -0
  185. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/not-zip-safe +0 -0
  186. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/requires.txt +0 -0
  187. {llms_py-3.0.6 → llms_py-3.0.8}/llms_py.egg-info/top_level.txt +0 -0
  188. {llms_py-3.0.6 → llms_py-3.0.8}/requirements.txt +0 -0
  189. {llms_py-3.0.6 → llms_py-3.0.8}/setup.cfg +0 -0
  190. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_async.py +0 -0
  191. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_config.py +0 -0
  192. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_extensions.py +0 -0
  193. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_gemini_upload.py +0 -0
  194. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_integration.py +0 -0
  195. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_provider_checks.py +0 -0
  196. {llms_py-3.0.6 → llms_py-3.0.8}/tests/test_provider_config.py +0 -0
  197. {llms_py-3.0.6 → llms_py-3.0.8}/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.6
3
+ Version: 3.0.8
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
@@ -370,7 +370,7 @@ export const Analytics = {
370
370
  </div>
371
371
  <div>
372
372
  <div class="text-xs text-gray-500 dark:text-gray-400 font-medium">Duration</div>
373
- <div v-if="request.duration" class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $fmt.humanifyMs(request.duration) }}</div>
373
+ <div v-if="request.duration" class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $fmt.humanifyMs(request.duration * 1000) }}</div>
374
374
  </div>
375
375
  <div>
376
376
  <div class="text-xs text-gray-500 dark:text-gray-400 font-medium">Speed</div>
@@ -444,7 +444,9 @@ def install(ctx):
444
444
  input_tokens = usage.get("prompt_tokens", 0)
445
445
  output_tokens = usage.get("completion_tokens", 0)
446
446
  total_tokens = usage.get("total_tokens", input_tokens + output_tokens)
447
- cost = o.get("cost", ((input_price * input_tokens) + (output_price * output_tokens)) / 1000000)
447
+ cost = usage.get("cost") or o.get(
448
+ "cost", ((input_price * input_tokens) + (output_price * output_tokens)) / 1000000
449
+ )
448
450
 
449
451
  request = {
450
452
  "user": user,
@@ -67,7 +67,7 @@ const RecentResults = {
67
67
 
68
68
  const normalized = (s) => (s || '').toString().toLowerCase()
69
69
  const replaceChars = new Set('<>`*|#'.split(''))
70
- const clean = s => [...s].map(c => replaceChars.has(c) ? ' ' : c).join('')
70
+ const clean = s => [...(s || '')].map(c => replaceChars.has(c) ? ' ' : c).join('')
71
71
 
72
72
  const loadMore = async (reset = false) => {
73
73
  if (reset) {
@@ -139,10 +139,11 @@ async function updateThread(threadId, updates) {
139
139
  }
140
140
  }
141
141
 
142
- async function deleteMessageFromThread(threadId, messageId) {
142
+ async function deleteMessageFromThread(threadId, timestamp) {
143
143
  const thread = await getThread(threadId)
144
144
  if (!thread) throw new Error('Thread not found')
145
- const updatedMessages = thread.messages.filter(m => m.id !== messageId)
145
+ const updatedMessages = thread.messages.filter(m => m.timestamp !== timestamp)
146
+ console.log('deleteMessageFromThread', threadId, timestamp, updatedMessages)
146
147
  await updateThread(threadId, { messages: updatedMessages })
147
148
  }
148
149
 
@@ -150,7 +151,7 @@ async function updateMessageInThread(threadId, messageId, updates) {
150
151
  const thread = await getThread(threadId)
151
152
  if (!thread) throw new Error('Thread not found')
152
153
 
153
- const messageIndex = thread.messages.findIndex(m => m.id === messageId)
154
+ const messageIndex = thread.messages.findIndex(m => m.timestamp === messageId)
154
155
  if (messageIndex === -1) throw new Error('Message not found')
155
156
 
156
157
  const updatedMessages = [...thread.messages]
@@ -538,12 +538,13 @@ def get_current_time(tz_name: Optional[str] = None) -> str:
538
538
  def install(ctx):
539
539
  global g_ctx
540
540
  g_ctx = ctx
541
+ group = "core_tools"
541
542
  # Examples of registering tools using automatic definition generation
542
- ctx.register_tool(memory_read)
543
- ctx.register_tool(memory_write)
543
+ ctx.register_tool(memory_read, group=group)
544
+ ctx.register_tool(memory_write, group=group)
544
545
  # ctx.register_tool(semantic_search) # TODO: implement
545
- ctx.register_tool(read_file)
546
- ctx.register_tool(write_file)
546
+ ctx.register_tool(read_file, group=group)
547
+ ctx.register_tool(write_file, group=group)
547
548
  ctx.register_tool(
548
549
  edit_file,
549
550
  {
@@ -562,15 +563,16 @@ def install(ctx):
562
563
  },
563
564
  },
564
565
  },
566
+ group=group,
565
567
  )
566
- ctx.register_tool(list_directory)
567
- ctx.register_tool(glob_paths)
568
- ctx.register_tool(calc)
569
- ctx.register_tool(run_python)
570
- ctx.register_tool(run_typescript)
571
- ctx.register_tool(run_javascript)
572
- ctx.register_tool(run_csharp)
573
- ctx.register_tool(get_current_time)
568
+ ctx.register_tool(list_directory, group=group)
569
+ ctx.register_tool(glob_paths, group=group)
570
+ ctx.register_tool(calc, group=group)
571
+ ctx.register_tool(run_python, group=group)
572
+ ctx.register_tool(run_typescript, group=group)
573
+ ctx.register_tool(run_javascript, group=group)
574
+ ctx.register_tool(run_csharp, group=group)
575
+ ctx.register_tool(get_current_time, group=group)
574
576
 
575
577
  def exec_language(language: str, code: str) -> Dict[str, Any]:
576
578
  if language == "python":
@@ -5,6 +5,7 @@ from .google import install_google
5
5
  from .nvidia import install_nvidia
6
6
  from .openai import install_openai
7
7
  from .openrouter import install_openrouter
8
+ from .zai import install_zai
8
9
 
9
10
 
10
11
  def install(ctx):
@@ -15,6 +16,7 @@ def install(ctx):
15
16
  install_nvidia(ctx)
16
17
  install_openai(ctx)
17
18
  install_openrouter(ctx)
19
+ install_zai(ctx)
18
20
 
19
21
 
20
22
  __install__ = install
@@ -221,7 +221,7 @@ def install_anthropic(ctx):
221
221
  # Add metadata
222
222
  if "metadata" not in ret:
223
223
  ret["metadata"] = {}
224
- ret["metadata"]["duration"] = int((time.time() - started_at) * 1000)
224
+ ret["metadata"]["duration"] = int(time.time() - started_at)
225
225
 
226
226
  if chat is not None and "model" in chat:
227
227
  cost = self.model_cost(chat["model"])
@@ -66,15 +66,13 @@ def install_chutes(ctx):
66
66
  if chat["model"] in self.model_negative_prompt:
67
67
  payload["negative_prompt"] = self.negative_prompt
68
68
 
69
- image_config = chat.get("image_config", {})
70
- aspect_ratio = image_config.get("aspect_ratio")
71
- if aspect_ratio:
72
- dimension = ctx.app.aspect_ratios.get(aspect_ratio)
73
- if dimension:
74
- w, h = dimension.split("×")
75
- width, height = int(w), int(h)
76
- payload["width"] = width
77
- payload["height"] = height
69
+ aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
70
+ dimension = ctx.app.aspect_ratios.get(aspect_ratio)
71
+ if dimension:
72
+ w, h = dimension.split("×")
73
+ width, height = int(w), int(h)
74
+ payload["width"] = width
75
+ payload["height"] = height
78
76
 
79
77
  if chat["model"] in self.model_resolutions:
80
78
  # if models use resolution, remove width and height
@@ -471,9 +471,9 @@ def install_google(ctx):
471
471
  if "usageMetadata" in obj:
472
472
  usage = obj["usageMetadata"]
473
473
  response["usage"] = {
474
- "completion_tokens": usage["candidatesTokenCount"],
475
- "total_tokens": usage["totalTokenCount"],
476
- "prompt_tokens": usage["promptTokenCount"],
474
+ "completion_tokens": usage.get("candidatesTokenCount", 0),
475
+ "total_tokens": usage.get("totalTokenCount", 0),
476
+ "prompt_tokens": usage.get("promptTokenCount", 0),
477
477
  }
478
478
 
479
479
  return ctx.log_json(self.to_response(response, chat, started_at))
@@ -66,17 +66,15 @@ def install_nvidia(ctx):
66
66
  }
67
67
  modalities = chat.get("modalities", ["text"])
68
68
  if "image" in modalities:
69
- image_config = chat.get("image_config", {})
70
- aspect_ratio = image_config.get("aspect_ratio")
71
- if aspect_ratio:
72
- dimension = ctx.app.aspect_ratios.get(aspect_ratio)
73
- if dimension:
74
- width, height = dimension.split("×")
75
- gen_request["width"] = int(width)
76
- gen_request["height"] = int(height)
77
- else:
78
- gen_request["width"] = self.width
79
- gen_request["height"] = self.height
69
+ aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
70
+ dimension = ctx.app.aspect_ratios.get(aspect_ratio)
71
+ if dimension:
72
+ width, height = dimension.split("×")
73
+ gen_request["width"] = int(width)
74
+ gen_request["height"] = int(height)
75
+ else:
76
+ gen_request["width"] = self.width
77
+ gen_request["height"] = self.height
80
78
 
81
79
  gen_request["mode"] = self.mode
82
80
  gen_request["cfg_scale"] = self.cfg_scale
@@ -119,9 +119,7 @@ def install_openai(ctx):
119
119
  if chat["model"] in self.map_image_models:
120
120
  chat["model"] = self.map_image_models[chat["model"]]
121
121
 
122
- aspect_ratio = "1:1"
123
- if "image_config" in chat and "aspect_ratio" in chat["image_config"]:
124
- aspect_ratio = chat["image_config"].get("aspect_ratio", "1:1")
122
+ aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
125
123
  payload = {
126
124
  "model": chat["model"],
127
125
  "prompt": ctx.last_user_prompt(chat),
@@ -0,0 +1,182 @@
1
+ import json
2
+ import time
3
+ from typing import Optional
4
+
5
+ import aiohttp
6
+
7
+
8
+ def install_zai(ctx):
9
+ from llms.main import GeneratorBase
10
+
11
+ # https://docs.z.ai/guides/image/glm-image
12
+ class ZaiGenerator(GeneratorBase):
13
+ sdk = "zai/image"
14
+
15
+ def __init__(self, **kwargs):
16
+ super().__init__(**kwargs)
17
+ self.aspect_ratios = {
18
+ "1:1": "1280×1280",
19
+ "2:3": "1056×1568",
20
+ "3:2": "1568×1056",
21
+ "3:4": "1088×1472",
22
+ "4:3": "1472×1088",
23
+ "4:5": "1088×1472",
24
+ "5:4": "1472×1088",
25
+ "9:16": "960×1728",
26
+ "16:9": "1728×960",
27
+ "21:9": "1728×960",
28
+ }
29
+ self.model: str = kwargs.get("model", "glm-image")
30
+ self.n: Optional[int] = kwargs.get("n")
31
+ self.quality: Optional[str] = kwargs.get("quality")
32
+ self.response_format: Optional[str] = kwargs.get("response_format")
33
+ self.size: Optional[str] = kwargs.get("size")
34
+ self.style: Optional[str] = kwargs.get("style")
35
+ self.sensitive_word_check: Optional[str] = kwargs.get("sensitive_word_check")
36
+ self.user: Optional[str] = kwargs.get("user")
37
+ self.request_id: Optional[str] = kwargs.get("request_id")
38
+ self.user_id: Optional[str] = kwargs.get("user_id")
39
+ self.extra_headers: Optional[dict] = kwargs.get("extra_headers")
40
+ self.extra_body: Optional[dict] = kwargs.get("extra_body")
41
+ self.disable_strict_validation: Optional[bool] = kwargs.get("disable_strict_validation")
42
+ self.timeout: Optional[float] = float(kwargs.get("timeout") or 300)
43
+ self.watermark_enabled: Optional[bool] = kwargs.get("watermark_enabled")
44
+
45
+ async def chat(self, chat, provider=None, context=None):
46
+ headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
47
+ if self.extra_headers:
48
+ headers.update(self.extra_headers)
49
+
50
+ chat_url = "https://api.z.ai/api/paas/v4/images/generations"
51
+ if provider is not None:
52
+ headers["Authorization"] = f"Bearer {provider.api_key}"
53
+ chat["model"] = provider.provider_model(chat["model"]) or chat["model"]
54
+ chat_url = provider.api + "/images/generations"
55
+
56
+ body = {}
57
+ attrs = [
58
+ "model",
59
+ "n",
60
+ "quality",
61
+ "response_format",
62
+ "size",
63
+ "style",
64
+ "sensitive_word_check",
65
+ "user",
66
+ "request_id",
67
+ "user_id",
68
+ "disable_strict_validation",
69
+ "watermark_enabled",
70
+ ]
71
+ for attr in attrs:
72
+ if hasattr(self, attr) and getattr(self, attr) is not None:
73
+ body[attr] = getattr(self, attr)
74
+
75
+ if self.extra_body:
76
+ body.update(self.extra_body)
77
+
78
+ if "model" in chat:
79
+ body["model"] = chat["model"]
80
+
81
+ body["prompt"] = ctx.last_user_prompt(chat)
82
+
83
+ aspect_ratio = ctx.chat_to_aspect_ratio(chat) or "1:1"
84
+ size = self.aspect_ratios.get(aspect_ratio, "1280x1280").replace("×", "x")
85
+ body["size"] = size
86
+
87
+ username = ctx.context_to_username(context)
88
+ if username:
89
+ body["user"] = username
90
+
91
+ ctx.dbg(f"ZaiProvider.chat: {chat_url}")
92
+ ctx.dbg(json.dumps(body, indent=2))
93
+ started_at = time.time()
94
+ async with aiohttp.ClientSession() as session, session.post(
95
+ chat_url,
96
+ headers=headers,
97
+ data=json.dumps(body),
98
+ timeout=aiohttp.ClientTimeout(total=self.timeout),
99
+ ) as response:
100
+ # Example Response
101
+ # {
102
+ # "created": 1768451303,
103
+ # "data": [
104
+ # {
105
+ # "url": "https://mfile.z.ai/1768451374203-b334959408a643a8a6c74eb104746dcb.png?ufileattname=202601151228236805d575507d4570_watermark.png"
106
+ # }
107
+ # ],
108
+ # "id": "202601151228236805d575507d4570",
109
+ # "request_id": "202601151228236805d575507d4570",
110
+ # "usage": {
111
+ # "tokens": 0,
112
+ # "price": 0,
113
+ # "cost": 0.0,
114
+ # "duration": 71
115
+ # },
116
+ # "timestamp": 1768451374519,
117
+ # "model": "GLM-Image"
118
+ # }
119
+
120
+ response_json = await self.response_json(response)
121
+ duration = int(time.time() - started_at)
122
+ usage = response_json.get("usage", {})
123
+ if context is not None:
124
+ context["providerResponse"] = response_json
125
+ if "cost" in usage:
126
+ context["cost"] = usage.get("cost")
127
+
128
+ images = []
129
+ for image in response_json.get("data", []):
130
+ url = image.get("url")
131
+ if not url:
132
+ continue
133
+ # download url with aiohttp
134
+ async with session.get(url) as image_response:
135
+ headers = image_response.headers
136
+ # get filename from Content-Disposition
137
+ # attachment; filename="202601151228236805d575507d4570_watermark.png"
138
+ mime_type = headers.get("Content-Type") or "image/png"
139
+ disposition = headers.get("Content-Disposition")
140
+ if disposition:
141
+ start = disposition.index('filename="') + len('filename="')
142
+ end = disposition.index('"', start)
143
+ filename = disposition[start:end]
144
+ else:
145
+ ext = mime_type.split("/")[1]
146
+ filename = f"{body['model'].lower()}-{response_json.get('id', int(started_at))}.{ext}"
147
+ image_bytes = await image_response.read()
148
+
149
+ info = {
150
+ "prompt": body["prompt"],
151
+ "type": mime_type,
152
+ "width": int(size.split("x")[0]),
153
+ "height": int(size.split("x")[1]),
154
+ "duration": duration,
155
+ }
156
+ info.update(usage)
157
+ cache_url, info = ctx.save_image_to_cache(
158
+ image_bytes, filename, image_info=info, ignore_info=True
159
+ )
160
+
161
+ images.append(
162
+ {
163
+ "type": "image_url",
164
+ "image_url": {
165
+ "url": cache_url,
166
+ },
167
+ }
168
+ )
169
+
170
+ chat_response = {
171
+ "choices": [{"message": {"role": "assistant", "content": self.default_content, "images": images}}],
172
+ "created": int(time.time()),
173
+ "usage": {
174
+ "prompt_tokens": 0,
175
+ "completion_tokens": 1_000_000, # Price per image is 0.015, so 1M token is 0.015
176
+ },
177
+ }
178
+ if "cost" in usage:
179
+ chat_response["cost"] = usage["cost"]
180
+ return ctx.log_json(chat_response)
181
+
182
+ ctx.add_provider(ZaiGenerator)
@@ -0,0 +1,144 @@
1
+ import json
2
+
3
+ from aiohttp import web
4
+
5
+
6
+ def install(ctx):
7
+ async def tools_handler(request):
8
+ return web.json_response(
9
+ {
10
+ "groups": ctx.app.tool_groups,
11
+ "definitions": ctx.app.tool_definitions,
12
+ }
13
+ )
14
+
15
+ ctx.add_get("", tools_handler)
16
+
17
+ def prop_def_types(prop_def):
18
+ prop_type = prop_def.get("type")
19
+ if not prop_type:
20
+ any_of = prop_def.get("anyOf")
21
+ if any_of:
22
+ return [item.get("type") for item in any_of]
23
+ else:
24
+ return []
25
+ return [prop_type]
26
+
27
+ def tool_prop_value(value, prop_def):
28
+ """
29
+ Convert a value to the specified type.
30
+ types: string, number, integer, boolean, object, array, null
31
+ example prop_def = [
32
+ {
33
+ "type": "string"
34
+ },
35
+ {
36
+ "default": "name",
37
+ "type": "string",
38
+ "enum": ["name", "size"]
39
+ },
40
+ {
41
+ "default": [],
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string"
45
+ }
46
+ },
47
+ {
48
+ "anyOf": [
49
+ {
50
+ "type": "string"
51
+ },
52
+ {
53
+ "type": "null"
54
+ }
55
+ ],
56
+ "default": null,
57
+ },
58
+ ]
59
+ """
60
+ if value is None:
61
+ default = prop_def.get("default")
62
+ if default is not None:
63
+ default = tool_prop_value(default, prop_def)
64
+ return default
65
+
66
+ prop_types = prop_def_types(prop_def)
67
+ if "integer" in prop_types:
68
+ return int(value)
69
+ elif "number" in prop_types:
70
+ return float(value)
71
+ elif "boolean" in prop_types:
72
+ return bool(value)
73
+ elif "object" in prop_types:
74
+ return value if isinstance(value, dict) else json.loads(value)
75
+ elif "array" in prop_types:
76
+ return value if isinstance(value, list) else value.split(",")
77
+ else:
78
+ enum = prop_def.get("enum")
79
+ if enum and value not in enum:
80
+ raise Exception(f"'{value}' is not in {enum}")
81
+ return value
82
+
83
+ async def exec_handler(request):
84
+ name = request.match_info.get("name")
85
+ args = await request.json()
86
+
87
+ tool_def = ctx.get_tool_definition(name)
88
+ if not tool_def:
89
+ raise Exception(f"Tool '{name}' not found")
90
+
91
+ type = tool_def.get("type")
92
+ if type != "function":
93
+ raise Exception(f"Tool '{name}' of type '{type}' is not supported")
94
+
95
+ ctx.dbg(f"Executing tool '{name}' with args:\n{json.dumps(args, indent=2)}")
96
+ function_args = {}
97
+ parameters = tool_def.get("function", {}).get("parameters")
98
+ if parameters:
99
+ properties = parameters.get("properties")
100
+ required_props = parameters.get("required", [])
101
+ if properties:
102
+ for prop_name, prop_def in properties.items():
103
+ prop_title = prop_def.get("title", prop_name)
104
+ prop_types = prop_def_types(prop_def)
105
+ value = None
106
+ if prop_name in args:
107
+ value = tool_prop_value(args[prop_name], prop_def)
108
+ elif prop_name in required_props:
109
+ if "null" in prop_types:
110
+ value = None
111
+ elif "default" in prop_def:
112
+ value = tool_prop_value(prop_def["default"], prop_def)
113
+ else:
114
+ raise Exception(f"Missing required parameter '{prop_title}' for tool '{name}'")
115
+ if value is not None or "null" in prop_types:
116
+ function_args[prop_name] = value
117
+ else:
118
+ ctx.dbg(f"tool '{name}' has no properties:\n{json.dumps(tool_def, indent=2)}")
119
+ else:
120
+ ctx.dbg(f"tool '{name}' has no parameters:\n{json.dumps(tool_def, indent=2)}")
121
+
122
+ try:
123
+ text, resources = await ctx.exec_tool(name, function_args)
124
+
125
+ results = []
126
+ if text:
127
+ results.append(
128
+ {
129
+ "type": "text",
130
+ "text": text,
131
+ }
132
+ )
133
+ if resources:
134
+ results.extend(resources)
135
+
136
+ return web.json_response(results)
137
+ except Exception as e:
138
+ ctx.err(f"Failed to execute tool '{name}' with args:\n{json.dumps(function_args, indent=2)}", e)
139
+ raise e
140
+
141
+ ctx.add_post("exec/{name}", exec_handler)
142
+
143
+
144
+ __install__ = install