llms-py 3.0.11__tar.gz → 3.0.12__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.11 → llms_py-3.0.12}/PKG-INFO +2 -2
  2. {llms_py-3.0.11 → llms_py-3.0.12}/README.md +1 -1
  3. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/google.py +55 -4
  4. {llms_py-3.0.11 → llms_py-3.0.12}/llms/main.py +7 -2
  5. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/ai.mjs +1 -1
  6. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/PKG-INFO +2 -2
  7. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/SOURCES.txt +2 -0
  8. {llms_py-3.0.11 → llms_py-3.0.12}/pyproject.toml +1 -1
  9. {llms_py-3.0.11 → llms_py-3.0.12}/setup.py +1 -1
  10. llms_py-3.0.12/tests/test_core_tools_direct.py +38 -0
  11. llms_py-3.0.12/tests/test_gemini_tool_calling.py +101 -0
  12. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_provider_config.py +4 -0
  13. {llms_py-3.0.11 → llms_py-3.0.12}/LICENSE +0 -0
  14. {llms_py-3.0.11 → llms_py-3.0.12}/MANIFEST.in +0 -0
  15. {llms_py-3.0.11 → llms_py-3.0.12}/llms/__init__.py +0 -0
  16. {llms_py-3.0.11 → llms_py-3.0.12}/llms/__main__.py +0 -0
  17. {llms_py-3.0.11 → llms_py-3.0.12}/llms/db.py +0 -0
  18. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/analytics/ui/index.mjs +0 -0
  19. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/README.md +0 -0
  20. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/__init__.py +0 -0
  21. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/db.py +0 -0
  22. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/ui/Recents.mjs +0 -0
  23. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/ui/index.mjs +0 -0
  24. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/app/ui/threadStore.mjs +0 -0
  25. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/CALCULATOR.md +0 -0
  26. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/__init__.py +0 -0
  27. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -0
  28. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -0
  29. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -0
  30. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -0
  31. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -0
  32. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -0
  33. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -0
  34. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -0
  35. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -0
  36. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/codemirror.css +0 -0
  37. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/codemirror.js +0 -0
  38. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -0
  39. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  40. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -0
  41. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -0
  42. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -0
  43. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -0
  44. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -0
  45. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -0
  46. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -0
  47. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/core_tools/ui/index.mjs +0 -0
  48. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/gallery/README.md +0 -0
  49. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/gallery/__init__.py +0 -0
  50. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/gallery/db.py +0 -0
  51. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/gallery/ui/index.mjs +0 -0
  52. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/README.md +0 -0
  53. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/__init__.py +0 -0
  54. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/README.md +0 -0
  55. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/auto-render.js +0 -0
  56. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/auto-render.min.js +0 -0
  57. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/auto-render.mjs +0 -0
  58. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/copy-tex.js +0 -0
  59. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -0
  60. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -0
  61. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -0
  62. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -0
  63. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -0
  64. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mhchem.js +0 -0
  65. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mhchem.min.js +0 -0
  66. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/mhchem.mjs +0 -0
  67. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -0
  68. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -0
  69. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -0
  70. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  71. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  72. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  73. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  74. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  75. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  76. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  77. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  78. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  79. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  80. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  81. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  82. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  83. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  84. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  85. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  86. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  87. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  88. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  89. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  90. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  91. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  92. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  93. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  94. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  95. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  96. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  97. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  98. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  99. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  100. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  101. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  102. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  103. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  104. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  105. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  106. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  107. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  108. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  109. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  110. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  111. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  112. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  113. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  114. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  115. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  116. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  117. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  118. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  119. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  120. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  121. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  122. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  123. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  124. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  125. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  126. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  127. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  128. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  129. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  130. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/index.mjs +0 -0
  131. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex-swap.css +0 -0
  132. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex-swap.min.css +0 -0
  133. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.css +0 -0
  134. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.js +0 -0
  135. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.min.css +0 -0
  136. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.min.js +0 -0
  137. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.min.mjs +0 -0
  138. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/katex/ui/katex.mjs +0 -0
  139. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/__init__.py +0 -0
  140. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/anthropic.py +0 -0
  141. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/cerebras.py +0 -0
  142. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/chutes.py +0 -0
  143. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/nvidia.py +0 -0
  144. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/openai.py +0 -0
  145. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/openrouter.py +0 -0
  146. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/providers/zai.py +0 -0
  147. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/system_prompts/README.md +0 -0
  148. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/system_prompts/__init__.py +0 -0
  149. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/system_prompts/ui/index.mjs +0 -0
  150. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/system_prompts/ui/prompts.json +0 -0
  151. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/tools/__init__.py +0 -0
  152. {llms_py-3.0.11 → llms_py-3.0.12}/llms/extensions/tools/ui/index.mjs +0 -0
  153. {llms_py-3.0.11 → llms_py-3.0.12}/llms/index.html +0 -0
  154. {llms_py-3.0.11 → llms_py-3.0.12}/llms/llms.json +0 -0
  155. {llms_py-3.0.11 → llms_py-3.0.12}/llms/providers-extra.json +0 -0
  156. {llms_py-3.0.11 → llms_py-3.0.12}/llms/providers.json +0 -0
  157. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/App.mjs +0 -0
  158. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/app.css +0 -0
  159. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/ctx.mjs +0 -0
  160. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/fav.svg +0 -0
  161. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/index.mjs +0 -0
  162. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/chart.js +0 -0
  163. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/charts.mjs +0 -0
  164. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/color.js +0 -0
  165. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/highlight.min.mjs +0 -0
  166. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/idb.min.mjs +0 -0
  167. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/marked.min.mjs +0 -0
  168. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/servicestack-client.mjs +0 -0
  169. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/servicestack-vue.mjs +0 -0
  170. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/vue-router.min.mjs +0 -0
  171. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/vue.min.mjs +0 -0
  172. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/lib/vue.mjs +0 -0
  173. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/markdown.mjs +0 -0
  174. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/chat/ChatBody.mjs +0 -0
  175. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/chat/SettingsDialog.mjs +0 -0
  176. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/chat/index.mjs +0 -0
  177. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/icons.mjs +0 -0
  178. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/layout.mjs +0 -0
  179. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/modules/model-selector.mjs +0 -0
  180. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/tailwind.input.css +0 -0
  181. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/typography.css +0 -0
  182. {llms_py-3.0.11 → llms_py-3.0.12}/llms/ui/utils.mjs +0 -0
  183. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/dependency_links.txt +0 -0
  184. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/entry_points.txt +0 -0
  185. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/not-zip-safe +0 -0
  186. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/requires.txt +0 -0
  187. {llms_py-3.0.11 → llms_py-3.0.12}/llms_py.egg-info/top_level.txt +0 -0
  188. {llms_py-3.0.11 → llms_py-3.0.12}/requirements.txt +0 -0
  189. {llms_py-3.0.11 → llms_py-3.0.12}/setup.cfg +0 -0
  190. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_async.py +0 -0
  191. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_config.py +0 -0
  192. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_extensions.py +0 -0
  193. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_gemini_upload.py +0 -0
  194. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_integration.py +0 -0
  195. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_interleaved_thinking.py +0 -0
  196. {llms_py-3.0.11 → llms_py-3.0.12}/tests/test_provider_checks.py +0 -0
  197. {llms_py-3.0.11 → llms_py-3.0.12}/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.11
3
+ Version: 3.0.12
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
@@ -44,6 +44,6 @@ Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing mu
44
44
 
45
45
  [llmspy.org](https://llmspy.org)
46
46
 
47
- [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home.webp?raw=true)](https://llmspy.org)
47
+ [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home-v3.webp?raw=true)](https://llmspy.org)
48
48
 
49
49
  GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
@@ -4,6 +4,6 @@ Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing mu
4
4
 
5
5
  [llmspy.org](https://llmspy.org)
6
6
 
7
- [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home.webp?raw=true)](https://llmspy.org)
7
+ [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home-v3.webp?raw=true)](https://llmspy.org)
8
8
 
9
9
  GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
@@ -63,6 +63,50 @@ def install_google(ctx):
63
63
  to[k] = v
64
64
  return to
65
65
 
66
+ def sanitize_parameters(params):
67
+ """Sanitize tool parameters for Google provider."""
68
+
69
+ if not isinstance(params, dict):
70
+ return params
71
+
72
+ # Create a copy to avoid modifying original tool definition
73
+ p = params.copy()
74
+
75
+ # Remove forbidden fields
76
+ for forbidden in ["$schema", "additionalProperties"]:
77
+ if forbidden in p:
78
+ del p[forbidden]
79
+
80
+ # Recursively sanitize known nesting fields
81
+ # 1. Properties (dict of schemas)
82
+ if "properties" in p:
83
+ for k, v in p["properties"].items():
84
+ p["properties"][k] = sanitize_parameters(v)
85
+
86
+ # 2. Items (schema or list of schemas)
87
+ if "items" in p:
88
+ if isinstance(p["items"], list):
89
+ p["items"] = [sanitize_parameters(i) for i in p["items"]]
90
+ else:
91
+ p["items"] = sanitize_parameters(p["items"])
92
+
93
+ # 3. Combinators (list of schemas)
94
+ for combinator in ["allOf", "anyOf", "oneOf"]:
95
+ if combinator in p:
96
+ p[combinator] = [sanitize_parameters(i) for i in p[combinator]]
97
+
98
+ # 4. Not (schema)
99
+ if "not" in p:
100
+ p["not"] = sanitize_parameters(p["not"])
101
+
102
+ # 5. Definitions (dict of schemas)
103
+ for def_key in ["definitions", "$defs"]:
104
+ if def_key in p:
105
+ for k, v in p[def_key].items():
106
+ p[def_key][k] = sanitize_parameters(v)
107
+
108
+ return p
109
+
66
110
  class GoogleProvider(OpenAiCompatible):
67
111
  sdk = "@ai-sdk/google"
68
112
 
@@ -112,11 +156,12 @@ def install_google(ctx):
112
156
  for tool in chat["tools"]:
113
157
  if tool["type"] == "function":
114
158
  f = tool["function"]
159
+
115
160
  function_declarations.append(
116
161
  {
117
162
  "name": f["name"],
118
163
  "description": f.get("description"),
119
- "parameters": f.get("parameters"),
164
+ "parameters": sanitize_parameters(f.get("parameters")),
120
165
  }
121
166
  )
122
167
  elif tool["type"] == "file_search":
@@ -183,13 +228,19 @@ def install_google(ctx):
183
228
  if name:
184
229
  # content is the string response
185
230
  # Some implementations pass the content directly.
186
- # Google docs say: response: { "name": "...", "content": { ... } }
187
- # Actually "response" field in functionResponse is a Struct/Map.
231
+ # Google docs say: response: { "key": "value" }
232
+ try:
233
+ response_data = json.loads(message["content"])
234
+ if not isinstance(response_data, dict):
235
+ response_data = {"content": message["content"]}
236
+ except Exception:
237
+ response_data = {"content": message["content"]}
238
+
188
239
  parts.append(
189
240
  {
190
241
  "functionResponse": {
191
242
  "name": name,
192
- "response": {"name": name, "content": message["content"]},
243
+ "response": response_data,
193
244
  }
194
245
  }
195
246
  )
@@ -43,7 +43,7 @@ try:
43
43
  except ImportError:
44
44
  HAS_PIL = False
45
45
 
46
- VERSION = "3.0.11"
46
+ VERSION = "3.0.12"
47
47
  _ROOT = None
48
48
  DEBUG = os.getenv("DEBUG") == "1"
49
49
  MOCK = os.getenv("MOCK") == "1"
@@ -1628,10 +1628,15 @@ async def g_chat_completion(chat, context=None):
1628
1628
  tool_history = []
1629
1629
  final_response = None
1630
1630
 
1631
- for _ in range(max_iterations):
1631
+ for request_count in range(max_iterations):
1632
1632
  if should_cancel_thread(context):
1633
1633
  return
1634
1634
 
1635
+ if DEBUG:
1636
+ messages = current_chat.get("messages", [])
1637
+ last_message = messages[-1] if messages else None
1638
+ _dbg(f"Provider {provider_name}, request {request_count}:\n{json.dumps(last_message, indent=2)}")
1639
+
1635
1640
  response = await provider.chat(current_chat, context=context)
1636
1641
 
1637
1642
  if should_cancel_thread(context):
@@ -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.11',
9
+ version: '3.0.12',
10
10
  base,
11
11
  prefsKey,
12
12
  welcome: 'Welcome to llms.py',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llms-py
3
- Version: 3.0.11
3
+ Version: 3.0.12
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
@@ -44,6 +44,6 @@ Lightweight CLI, API and ChatGPT-like alternative to Open WebUI for accessing mu
44
44
 
45
45
  [llmspy.org](https://llmspy.org)
46
46
 
47
- [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home.webp?raw=true)](https://llmspy.org)
47
+ [![](https://github.com/ServiceStack/llmspy.org/blob/main/public/img/llmspy-home-v3.webp?raw=true)](https://llmspy.org)
48
48
 
49
49
  GitHub: [llmspy.org](https://github.com/ServiceStack/llmspy.org)
@@ -184,7 +184,9 @@ llms_py.egg-info/requires.txt
184
184
  llms_py.egg-info/top_level.txt
185
185
  tests/test_async.py
186
186
  tests/test_config.py
187
+ tests/test_core_tools_direct.py
187
188
  tests/test_extensions.py
189
+ tests/test_gemini_tool_calling.py
188
190
  tests/test_gemini_upload.py
189
191
  tests/test_integration.py
190
192
  tests/test_interleaved_thinking.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "llms-py"
7
- version = "3.0.11"
7
+ version = "3.0.12"
8
8
  description = "A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers"
9
9
  readme = "README.md"
10
10
  license = "BSD-3-Clause"
@@ -15,7 +15,7 @@ with open(os.path.join(this_directory, "requirements.txt"), encoding="utf-8") as
15
15
 
16
16
  setup(
17
17
  name="llms-py",
18
- version="3.0.11",
18
+ version="3.0.12",
19
19
  author="ServiceStack",
20
20
  author_email="team@servicestack.net",
21
21
  description="A lightweight CLI tool and OpenAI-compatible server for querying multiple Large Language Model (LLM) providers",
@@ -0,0 +1,38 @@
1
+ import os
2
+ import sys
3
+ import unittest
4
+ from unittest.mock import MagicMock
5
+
6
+ # Add project root to path
7
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
8
+
9
+ import llms.extensions.core_tools as core_tools
10
+
11
+ # Mock g_ctx
12
+ core_tools.g_ctx = MagicMock()
13
+
14
+
15
+ class TestCoreToolsDirect(unittest.TestCase):
16
+ def test_calc(self):
17
+ print("Running tests...")
18
+
19
+ # Simple list comprehension
20
+ res = core_tools.calc("sum([x * 2 for x in [1, 2, 3]])")
21
+ print(f"sum([x * 2 for x in [1, 2, 3]]) = {res}")
22
+ assert res == 12
23
+
24
+ # List comprehension with condition
25
+ res = core_tools.calc("sum([x for x in [1, 2, 3, 4] if x > 2])")
26
+ print(f"sum([x for x in [1, 2, 3, 4] if x > 2]) = {res}")
27
+ assert res == 7
28
+
29
+ # Range support (I added range support too)
30
+ res = core_tools.calc("sum([x for x in range(5)])")
31
+ print(f"sum([x for x in range(5)]) = {res}")
32
+ assert res == 10
33
+
34
+ print("All tests passed!")
35
+
36
+
37
+ if __name__ == "__main__":
38
+ unittest.main()
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Unit tests for utility functions in llms.main module.
4
+ """
5
+
6
+ import inspect
7
+ import json
8
+ import os
9
+ import sys
10
+ import unittest
11
+
12
+ # from dotenv import load_dotenv
13
+
14
+ # Add parent directory to path to import llms module
15
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
16
+
17
+ from llms.main import (
18
+ cli,
19
+ get_app,
20
+ load_config,
21
+ )
22
+
23
+ # Load environment variables from .env file
24
+ # load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
25
+
26
+ config_path = os.path.join(os.path.dirname(__file__), "..", "llms", "llms.json")
27
+ with open(config_path) as f:
28
+ config = json.load(f)
29
+ providers_path = os.path.join(os.path.dirname(__file__), "..", "llms", "providers.json")
30
+ with open(providers_path) as f:
31
+ providers = json.load(f)
32
+
33
+ g_app = None
34
+
35
+
36
+ class TestGeminiToolCalling(unittest.IsolatedAsyncioTestCase):
37
+ """Test Gemini Tool Calling."""
38
+
39
+ def setUp(self):
40
+ load_config(
41
+ config, providers, debug=True, verbose=True, disable_extensions=["duckduckgo", "fast_mcp", "gemini", "xmas"]
42
+ )
43
+ cli("ls minimax")
44
+ global g_app
45
+ g_app = get_app()
46
+
47
+ @classmethod
48
+ def tearDownClass(cls):
49
+ g_app.shutdown()
50
+
51
+ async def test_gemini_tool_calling(self):
52
+ provider = g_app.get_providers()["google"]
53
+ chat = {
54
+ "model": "Gemini Flash-Lite Latest",
55
+ "messages": [
56
+ {"role": "user", "content": "Calculate 12 * 34 + 56"},
57
+ ],
58
+ }
59
+ response = await provider.chat(chat)
60
+ print("GEMINI RESPONSE:")
61
+ print(json.dumps(response, indent=2))
62
+ print("GEMINI RESULT:")
63
+ content = response["choices"][0]["message"]["content"]
64
+ print(content)
65
+ self.assertIn("464", content)
66
+
67
+ async def test_gemini_multi_step_tool_calling(self):
68
+ # provider = g_app.get_providers()["google"]
69
+ filename = "test_multi_step.txt"
70
+ content_to_write = "Multi-step test content"
71
+
72
+ # Ensure cleanup
73
+ if os.path.exists(filename):
74
+ os.remove(filename)
75
+
76
+ chat = {
77
+ "model": "Gemini Flash-Lite Latest",
78
+ "messages": [
79
+ {
80
+ "role": "user",
81
+ "content": f"Create a file named '{filename}' with the content '{content_to_write}', and then read it back to me.",
82
+ },
83
+ ],
84
+ }
85
+
86
+ response = await g_app.chat_completion(chat)
87
+
88
+ last_message = response["choices"][0]["message"]
89
+ print(f"GEMINI RESULT (Multi-step):\n{json.dumps(last_message, indent=2)}")
90
+
91
+ # Verify file exists and content
92
+ with open(filename) as f:
93
+ self.assertEqual(f.read(), content_to_write)
94
+
95
+ # Cleanup
96
+ if os.path.exists(filename):
97
+ os.remove(filename)
98
+
99
+
100
+ if __name__ == "__main__":
101
+ unittest.main()
@@ -229,3 +229,7 @@ class TestProviderConfiguration(unittest.TestCase):
229
229
  provider = create_provider(create_provider_kwargs(config["providers"]["openrouter"], providers["openrouter"]))
230
230
  self.assertEqual(provider.__class__.__name__, "OpenAiCompatible")
231
231
  self.assertEqual(len(provider.models), 96)
232
+
233
+
234
+ if __name__ == "__main__":
235
+ unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes