llms-py 3.0.11__py3-none-any.whl → 3.0.12__py3-none-any.whl

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.
@@ -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
  )
llms/main.py CHANGED
@@ -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):
llms/ui/ai.mjs CHANGED
@@ -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)
@@ -3,7 +3,7 @@ llms/__main__.py,sha256=hrBulHIt3lmPm1BCyAEVtB6DQ0Hvc3gnIddhHCmJasg,151
3
3
  llms/db.py,sha256=oozp5I5lECVO8oZEFwcZl3ES5mARqWeR1BkoqG5kSqM,11687
4
4
  llms/index.html,sha256=nGk1Djtn9p7l6LuKp4Kg0JIB9fCzxtTWXFfmDb4ggpc,1658
5
5
  llms/llms.json,sha256=NEr9kJRkUGZ2YZHbWC-haGPlVVL2Qtnx4kKZENGH1wk,11494
6
- llms/main.py,sha256=IoKt4rGaAKAcc-Y2M-cTbxyIqOy9tmeiWxnrHjzHHv0,173872
6
+ llms/main.py,sha256=HgQ_nS0OSqu8BRq_k4a1vpPhGzNiIgvIg-fxs7O6G44,174163
7
7
  llms/providers-extra.json,sha256=_6DmGBiQY9LM6_Y0zOiObYn7ba4g3akSNQfmHcYlENc,11101
8
8
  llms/providers.json,sha256=yjhDurlwo70xqfV0HNLiZaCpw3WvtIgkjoLahQIKX2w,282530
9
9
  llms/extensions/analytics/ui/index.mjs,sha256=m1XwaqYCLwK267JAUCAltkN_nOXep0GxfpvGNS5i4_w,69547
@@ -131,7 +131,7 @@ llms/extensions/providers/__init__.py,sha256=C5zOBQEOB2L96rAZdjV42fPVk_dZxSh2Dv3
131
131
  llms/extensions/providers/anthropic.py,sha256=V9mechnhyoX-5Z5AkwyQ-UzLax6cqG7j7GLvGTZF9no,10941
132
132
  llms/extensions/providers/cerebras.py,sha256=HaeFW0GwbD6V6Zrrwqyv78kQb0VXg9oHmykvJfIOOYE,1417
133
133
  llms/extensions/providers/chutes.py,sha256=5ZrfbqoOhgzKLQy_qULcp4jlvW5WXPR0jP9kN2Jzb9g,6229
134
- llms/extensions/providers/google.py,sha256=9P90bEefRA18tpjfEuAz1T5YpwzdOngrTFhw-LI3eXg,24434
134
+ llms/extensions/providers/google.py,sha256=oCCTE2KAw-WWE2v14XpKzgAMdFIWbjTBoa6GWuqT4dw,26215
135
135
  llms/extensions/providers/nvidia.py,sha256=C6cwqn3EufYDfRIgbc8MDkQNyD6w3c7hbjfYaHJSDik,4279
136
136
  llms/extensions/providers/openai.py,sha256=hkE-LVsw6M92_qEbpayuPo17Z1OWKHe7lm2wduLMng8,6138
137
137
  llms/extensions/providers/openrouter.py,sha256=5SfCJKo1aGKoDGez6HXYQe9elMMo9sSEDFqqdxamAgA,3330
@@ -143,7 +143,7 @@ llms/extensions/system_prompts/ui/prompts.json,sha256=t5DD3bird-87wFa4OlW-bC2wdo
143
143
  llms/extensions/tools/__init__.py,sha256=u76604Cn_sRFQRqeA_pkVEty27V688Mt9Z7Kh63yDr8,4825
144
144
  llms/extensions/tools/ui/index.mjs,sha256=IbGB2FQJ5VL4a8arwoR9C79vUCNrz8VIyQnHZ4vxU9o,34486
145
145
  llms/ui/App.mjs,sha256=CoUzO9mV__-jV19NKHYIbwHsjWMnO11jyNSbnJhe1gQ,7486
146
- llms/ui/ai.mjs,sha256=GVyxu3Thsuck0b1Akvqk2c5w-maSzONU06QN-TUWoJk,6541
146
+ llms/ui/ai.mjs,sha256=S-LmypGGmTrAteH9By37md6gRJw2ILbjXf36Glho5EY,6541
147
147
  llms/ui/app.css,sha256=vfXErYVdVlE3pL8oZ-2G_OC-_reJzmaL0p91EVv48uo,186490
148
148
  llms/ui/ctx.mjs,sha256=X4scgXEQ9bMUfQl36sM4A3o2Ufad3LRwItxfmSu1xwc,12838
149
149
  llms/ui/fav.svg,sha256=_R6MFeXl6wBFT0lqcUxYQIDWgm246YH_3hSTW0oO8qw,734
@@ -169,9 +169,9 @@ llms/ui/modules/model-selector.mjs,sha256=6U4rAZ7vmQELFRQGWk4YEtq02v3lyHdMq6yUOp
169
169
  llms/ui/modules/chat/ChatBody.mjs,sha256=5yWjo6tWmcKidDpRvKFeHqx3lXO3DB-3rTyXY72gB4U,49122
170
170
  llms/ui/modules/chat/SettingsDialog.mjs,sha256=HMBJTwrapKrRIAstIIqp0QlJL5O-ho4hzgvfagPfsX8,19930
171
171
  llms/ui/modules/chat/index.mjs,sha256=lfSbERMaM3bLsKhdJJPWwL4-FGr8U_ftlvqW5vC3T1s,39762
172
- llms_py-3.0.11.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
173
- llms_py-3.0.11.dist-info/METADATA,sha256=90LpikNrckf1EaRrQHKGLOGobshThyJsRcX75csIs1Y,2192
174
- llms_py-3.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
175
- llms_py-3.0.11.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
176
- llms_py-3.0.11.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
177
- llms_py-3.0.11.dist-info/RECORD,,
172
+ llms_py-3.0.12.dist-info/licenses/LICENSE,sha256=bus9cuAOWeYqBk2OuhSABVV1P4z7hgrEFISpyda_H5w,1532
173
+ llms_py-3.0.12.dist-info/METADATA,sha256=KVDU66mm6L_mF4uwzhWhSbPHqqydeOKSuuKHf0IRJ_w,2195
174
+ llms_py-3.0.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
175
+ llms_py-3.0.12.dist-info/entry_points.txt,sha256=WswyE7PfnkZMIxboC-MS6flBD6wm-CYU7JSUnMhqMfM,40
176
+ llms_py-3.0.12.dist-info/top_level.txt,sha256=gC7hk9BKSeog8gyg-EM_g2gxm1mKHwFRfK-10BxOsa4,5
177
+ llms_py-3.0.12.dist-info/RECORD,,