mbxai 0.5.10__tar.gz → 0.5.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 (25) hide show
  1. {mbxai-0.5.10 → mbxai-0.5.12}/.vscode/PythonImportHelper-v2-Completion.json +9 -9
  2. {mbxai-0.5.10 → mbxai-0.5.12}/PKG-INFO +1 -1
  3. {mbxai-0.5.10 → mbxai-0.5.12}/pyproject.toml +1 -1
  4. {mbxai-0.5.10 → mbxai-0.5.12}/setup.py +1 -1
  5. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/__init__.py +1 -1
  6. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/server.py +1 -1
  7. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/client.py +136 -49
  8. {mbxai-0.5.10 → mbxai-0.5.12}/uv.lock +7 -7
  9. {mbxai-0.5.10 → mbxai-0.5.12}/LICENSE +0 -0
  10. {mbxai-0.5.10 → mbxai-0.5.12}/README.md +0 -0
  11. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/core.py +0 -0
  12. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/__init__.py +0 -0
  13. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/client.py +0 -0
  14. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/example.py +0 -0
  15. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/__init__.py +0 -0
  16. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/client.py +0 -0
  17. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/config.py +0 -0
  18. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/models.py +0 -0
  19. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/__init__.py +0 -0
  20. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/example.py +0 -0
  21. {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/types.py +0 -0
  22. {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_core.py +0 -0
  23. {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_mcp.py +0 -0
  24. {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_openrouter.py +0 -0
  25. {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_tools.py +0 -0
@@ -439,6 +439,15 @@
439
439
  "detail": "inspect",
440
440
  "documentation": {}
441
441
  },
442
+ {
443
+ "label": "json",
444
+ "kind": 6,
445
+ "isExtraImport": true,
446
+ "importPath": "json",
447
+ "description": "json",
448
+ "detail": "json",
449
+ "documentation": {}
450
+ },
442
451
  {
443
452
  "label": "hello_world",
444
453
  "importPath": "mbxai.core",
@@ -520,15 +529,6 @@
520
529
  "detail": "fastapi.testclient",
521
530
  "documentation": {}
522
531
  },
523
- {
524
- "label": "json",
525
- "kind": 6,
526
- "isExtraImport": true,
527
- "importPath": "json",
528
- "description": "json",
529
- "detail": "json",
530
- "documentation": {}
531
- },
532
532
  {
533
533
  "label": "MCPClient",
534
534
  "importPath": "mbxai.mcp",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.10
3
+ Version: 0.5.12
4
4
  Summary: MBX AI SDK
5
5
  Project-URL: Homepage, https://www.mibexx.de
6
6
  Project-URL: Documentation, https://www.mibexx.de
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mbxai"
7
- version = "0.5.10"
7
+ version = "0.5.12"
8
8
  authors = [
9
9
  { name = "MBX AI" }
10
10
  ]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="mbxai",
5
- version="0.5.10",
5
+ version="0.5.12",
6
6
  author="MBX AI",
7
7
  description="MBX AI SDK",
8
8
  long_description=open("README.md").read(),
@@ -2,4 +2,4 @@
2
2
  MBX AI package.
3
3
  """
4
4
 
5
- __version__ = "0.5.10"
5
+ __version__ = "0.5.12"
@@ -31,7 +31,7 @@ class MCPServer:
31
31
  self.app = FastAPI(
32
32
  title=self.name,
33
33
  description=self.description,
34
- version="0.5.10",
34
+ version="0.5.12",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
@@ -5,6 +5,7 @@ ToolClient implementation for MBX AI.
5
5
  from typing import Any, Callable, TypeVar, cast
6
6
  import logging
7
7
  import inspect
8
+ import json
8
9
  from pydantic import BaseModel
9
10
  from ..openrouter import OpenRouterClient
10
11
  from .types import Tool, ToolCall
@@ -88,13 +89,32 @@ class ToolClient:
88
89
  return response
89
90
 
90
91
  message = response.choices[0].message
91
- messages.append({"role": "assistant", "content": message.content})
92
+ # Add the assistant's message with tool calls
93
+ assistant_message = {
94
+ "role": "assistant",
95
+ "content": message.content or None, # Ensure content is None if empty
96
+ }
97
+ if message.tool_calls:
98
+ assistant_message["tool_calls"] = [
99
+ {
100
+ "id": tool_call.id,
101
+ "type": "function",
102
+ "function": {
103
+ "name": tool_call.function.name,
104
+ "arguments": tool_call.function.arguments,
105
+ },
106
+ }
107
+ for tool_call in message.tool_calls
108
+ ]
109
+ messages.append(assistant_message)
110
+ logger.debug(f"Added assistant message: {assistant_message}")
92
111
 
93
112
  # If there are no tool calls, we're done
94
113
  if not message.tool_calls:
95
114
  return response
96
115
 
97
- # Handle each tool call
116
+ # Handle all tool calls before getting the next model response
117
+ tool_responses = []
98
118
  for tool_call in message.tool_calls:
99
119
  logger.debug(f"Processing tool call: {tool_call}")
100
120
  logger.debug(f"Tool call ID: {tool_call.id}")
@@ -111,7 +131,6 @@ class ToolClient:
111
131
  logger.debug(f"Raw arguments: {arguments}")
112
132
 
113
133
  if isinstance(arguments, str):
114
- import json
115
134
  try:
116
135
  arguments = json.loads(arguments)
117
136
  logger.debug(f"Parsed arguments: {arguments}")
@@ -127,33 +146,58 @@ class ToolClient:
127
146
  result = tool.function(**arguments)
128
147
  logger.debug(f"Tool result: {result}")
129
148
 
130
- # Add the tool response to the messages
149
+ # Convert result to JSON string if it's not already
150
+ if not isinstance(result, str):
151
+ result = json.dumps(result)
152
+
153
+ # Create the tool response
131
154
  tool_response = {
132
155
  "role": "tool",
133
156
  "tool_call_id": tool_call.id,
134
- "name": tool_call.function.name,
135
- "content": str(result),
157
+ "content": result,
136
158
  }
137
- messages.append(tool_response)
138
- logger.debug(f"Added tool response to messages: {tool_response}")
159
+ tool_responses.append(tool_response)
160
+ logger.debug(f"Created tool response: {tool_response}")
139
161
 
140
- # Get a new response from the model with this tool result
141
- response = self._client.chat_completion(
142
- messages=messages,
143
- model=model,
144
- stream=stream,
145
- **kwargs,
146
- )
162
+ # Add all tool responses to the messages
163
+ messages.extend(tool_responses)
164
+ logger.debug(f"Added all tool responses to messages: {tool_responses}")
147
165
 
148
- if stream:
149
- return response
166
+ # Get a new response from the model with all tool results
167
+ response = self._client.chat_completion(
168
+ messages=messages,
169
+ model=model,
170
+ stream=stream,
171
+ **kwargs,
172
+ )
150
173
 
151
- message = response.choices[0].message
152
- messages.append({"role": "assistant", "content": message.content})
174
+ if stream:
175
+ return response
153
176
 
154
- # If there are no more tool calls, we're done
155
- if not message.tool_calls:
156
- return response
177
+ message = response.choices[0].message
178
+ # Add the assistant's message with tool calls
179
+ assistant_message = {
180
+ "role": "assistant",
181
+ "content": message.content or None, # Ensure content is None if empty
182
+ }
183
+ if message.tool_calls:
184
+ assistant_message["tool_calls"] = [
185
+ {
186
+ "id": tool_call.id,
187
+ "type": "function",
188
+ "function": {
189
+ "name": tool_call.function.name,
190
+ "arguments": tool_call.function.arguments,
191
+ },
192
+ }
193
+ for tool_call in message.tool_calls
194
+ ]
195
+ messages.append(assistant_message)
196
+ logger.debug(f"Added assistant message: {assistant_message}")
197
+
198
+ # If there are no more tool calls, we're done
199
+ if not message.tool_calls:
200
+ return response
157
201
 
158
202
  async def parse(
159
203
  self,
@@ -197,13 +241,32 @@ class ToolClient:
197
241
  return response
198
242
 
199
243
  message = response.choices[0].message
200
- messages.append({"role": "assistant", "content": message.content})
244
+ # Add the assistant's message with tool calls
245
+ assistant_message = {
246
+ "role": "assistant",
247
+ "content": message.content or None, # Ensure content is None if empty
248
+ }
249
+ if message.tool_calls:
250
+ assistant_message["tool_calls"] = [
251
+ {
252
+ "id": tool_call.id,
253
+ "type": "function",
254
+ "function": {
255
+ "name": tool_call.function.name,
256
+ "arguments": tool_call.function.arguments,
257
+ },
258
+ }
259
+ for tool_call in message.tool_calls
260
+ ]
261
+ messages.append(assistant_message)
262
+ logger.debug(f"Added assistant message: {assistant_message}")
201
263
 
202
264
  # If there are no tool calls, we're done
203
265
  if not message.tool_calls:
204
266
  return response
205
267
 
206
- # Handle each tool call
268
+ # Handle all tool calls before getting the next model response
269
+ tool_responses = []
207
270
  for tool_call in message.tool_calls:
208
271
  logger.debug(f"Processing tool call: {tool_call}")
209
272
  logger.debug(f"Tool call ID: {tool_call.id}")
@@ -220,7 +283,6 @@ class ToolClient:
220
283
  logger.debug(f"Raw arguments: {arguments}")
221
284
 
222
285
  if isinstance(arguments, str):
223
- import json
224
286
  try:
225
287
  arguments = json.loads(arguments)
226
288
  logger.debug(f"Parsed arguments: {arguments}")
@@ -236,31 +298,56 @@ class ToolClient:
236
298
  result = tool.function(**arguments)
237
299
  logger.debug(f"Tool result: {result}")
238
300
 
239
- # Add the tool response to the messages
301
+ # Convert result to JSON string if it's not already
302
+ if not isinstance(result, str):
303
+ result = json.dumps(result)
304
+
305
+ # Create the tool response
240
306
  tool_response = {
241
307
  "role": "tool",
242
308
  "tool_call_id": tool_call.id,
243
- "name": tool_call.function.name,
244
- "content": str(result),
309
+ "content": result,
245
310
  }
246
- messages.append(tool_response)
247
- logger.debug(f"Added tool response to messages: {tool_response}")
248
-
249
- # Get a new response from the model with this tool result
250
- response = self._client.chat_completion_parse(
251
- messages=messages,
252
- response_format=response_format,
253
- model=model,
254
- stream=stream,
255
- **kwargs,
256
- )
257
-
258
- if stream:
259
- return response
260
-
261
- message = response.choices[0].message
262
- messages.append({"role": "assistant", "content": message.content})
263
-
264
- # If there are no more tool calls, we're done
265
- if not message.tool_calls:
266
- return response
311
+ tool_responses.append(tool_response)
312
+ logger.debug(f"Created tool response: {tool_response}")
313
+
314
+ # Add all tool responses to the messages
315
+ messages.extend(tool_responses)
316
+ logger.debug(f"Added all tool responses to messages: {tool_responses}")
317
+
318
+ # Get a new response from the model with all tool results
319
+ response = self._client.chat_completion_parse(
320
+ messages=messages,
321
+ response_format=response_format,
322
+ model=model,
323
+ stream=stream,
324
+ **kwargs,
325
+ )
326
+
327
+ if stream:
328
+ return response
329
+
330
+ message = response.choices[0].message
331
+ # Add the assistant's message with tool calls
332
+ assistant_message = {
333
+ "role": "assistant",
334
+ "content": message.content or None, # Ensure content is None if empty
335
+ }
336
+ if message.tool_calls:
337
+ assistant_message["tool_calls"] = [
338
+ {
339
+ "id": tool_call.id,
340
+ "type": "function",
341
+ "function": {
342
+ "name": tool_call.function.name,
343
+ "arguments": tool_call.function.arguments,
344
+ },
345
+ }
346
+ for tool_call in message.tool_calls
347
+ ]
348
+ messages.append(assistant_message)
349
+ logger.debug(f"Added assistant message: {assistant_message}")
350
+
351
+ # If there are no more tool calls, we're done
352
+ if not message.tool_calls:
353
+ return response
@@ -292,11 +292,11 @@ wheels = [
292
292
 
293
293
  [[package]]
294
294
  name = "httpx-sse"
295
- version = "0.5.10"
295
+ version = "0.5.12"
296
296
  source = { registry = "https://pypi.org/simple" }
297
- sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.10.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
297
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.12.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
298
298
  wheels = [
299
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.10-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
299
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.12-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
300
300
  ]
301
301
 
302
302
  [[package]]
@@ -446,7 +446,7 @@ wheels = [
446
446
 
447
447
  [[package]]
448
448
  name = "mbxai"
449
- version = "0.5.10"
449
+ version = "0.5.12"
450
450
  source = { editable = "." }
451
451
  dependencies = [
452
452
  { name = "fastapi" },
@@ -980,14 +980,14 @@ wheels = [
980
980
 
981
981
  [[package]]
982
982
  name = "typing-inspection"
983
- version = "0.5.10"
983
+ version = "0.5.12"
984
984
  source = { registry = "https://pypi.org/simple" }
985
985
  dependencies = [
986
986
  { name = "typing-extensions" },
987
987
  ]
988
- sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.10.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
988
+ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.12.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
989
989
  wheels = [
990
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.10-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
990
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.12-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
991
991
  ]
992
992
 
993
993
  [[package]]
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