mbxai 0.5.9__tar.gz → 0.5.11__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.9 → mbxai-0.5.11}/.vscode/PythonImportHelper-v2-Completion.json +9 -9
  2. {mbxai-0.5.9 → mbxai-0.5.11}/PKG-INFO +1 -1
  3. {mbxai-0.5.9 → mbxai-0.5.11}/pyproject.toml +1 -1
  4. {mbxai-0.5.9 → mbxai-0.5.11}/setup.py +1 -1
  5. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/__init__.py +1 -1
  6. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/mcp/server.py +1 -1
  7. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/tools/client.py +120 -43
  8. {mbxai-0.5.9 → mbxai-0.5.11}/uv.lock +7 -7
  9. {mbxai-0.5.9 → mbxai-0.5.11}/LICENSE +0 -0
  10. {mbxai-0.5.9 → mbxai-0.5.11}/README.md +0 -0
  11. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/core.py +0 -0
  12. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/mcp/__init__.py +0 -0
  13. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/mcp/client.py +0 -0
  14. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/mcp/example.py +0 -0
  15. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/openrouter/__init__.py +0 -0
  16. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/openrouter/client.py +0 -0
  17. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/openrouter/config.py +0 -0
  18. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/openrouter/models.py +0 -0
  19. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/tools/__init__.py +0 -0
  20. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/tools/example.py +0 -0
  21. {mbxai-0.5.9 → mbxai-0.5.11}/src/mbxai/tools/types.py +0 -0
  22. {mbxai-0.5.9 → mbxai-0.5.11}/tests/test_core.py +0 -0
  23. {mbxai-0.5.9 → mbxai-0.5.11}/tests/test_mcp.py +0 -0
  24. {mbxai-0.5.9 → mbxai-0.5.11}/tests/test_openrouter.py +0 -0
  25. {mbxai-0.5.9 → mbxai-0.5.11}/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.9
3
+ Version: 0.5.11
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.9"
7
+ version = "0.5.11"
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.9",
5
+ version="0.5.11",
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.9"
5
+ __version__ = "0.5.11"
@@ -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.9",
34
+ version="0.5.11",
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,7 +89,25 @@ 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:
@@ -111,7 +130,6 @@ class ToolClient:
111
130
  logger.debug(f"Raw arguments: {arguments}")
112
131
 
113
132
  if isinstance(arguments, str):
114
- import json
115
133
  try:
116
134
  arguments = json.loads(arguments)
117
135
  logger.debug(f"Parsed arguments: {arguments}")
@@ -127,33 +145,54 @@ class ToolClient:
127
145
  result = tool.function(**arguments)
128
146
  logger.debug(f"Tool result: {result}")
129
147
 
148
+ # Convert result to JSON string if it's not already
149
+ if not isinstance(result, str):
150
+ result = json.dumps(result)
151
+
130
152
  # Add the tool response to the messages
131
153
  tool_response = {
132
154
  "role": "tool",
133
155
  "tool_call_id": tool_call.id,
134
- "name": tool_call.function.name,
135
- "content": str(result),
156
+ "content": result,
136
157
  }
137
158
  messages.append(tool_response)
138
159
  logger.debug(f"Added tool response to messages: {tool_response}")
139
160
 
140
- # Get a new response from the model with the tool results
141
- response = self._client.chat_completion(
142
- messages=messages,
143
- model=model,
144
- stream=stream,
145
- **kwargs,
146
- )
147
-
148
- if stream:
149
- return response
150
-
151
- message = response.choices[0].message
152
- messages.append({"role": "assistant", "content": message.content})
153
-
154
- # If there are no more tool calls, we're done
155
- if not message.tool_calls:
156
- return response
161
+ # Get a new response from the model with this tool result
162
+ response = self._client.chat_completion(
163
+ messages=messages,
164
+ model=model,
165
+ stream=stream,
166
+ **kwargs,
167
+ )
168
+
169
+ if stream:
170
+ return response
171
+
172
+ message = response.choices[0].message
173
+ # Add the assistant's message with tool calls
174
+ assistant_message = {
175
+ "role": "assistant",
176
+ "content": message.content or None, # Ensure content is None if empty
177
+ }
178
+ if message.tool_calls:
179
+ assistant_message["tool_calls"] = [
180
+ {
181
+ "id": tool_call.id,
182
+ "type": "function",
183
+ "function": {
184
+ "name": tool_call.function.name,
185
+ "arguments": tool_call.function.arguments,
186
+ },
187
+ }
188
+ for tool_call in message.tool_calls
189
+ ]
190
+ messages.append(assistant_message)
191
+ logger.debug(f"Added assistant message: {assistant_message}")
192
+
193
+ # If there are no more tool calls, we're done
194
+ if not message.tool_calls:
195
+ return response
157
196
 
158
197
  async def parse(
159
198
  self,
@@ -197,7 +236,25 @@ class ToolClient:
197
236
  return response
198
237
 
199
238
  message = response.choices[0].message
200
- messages.append({"role": "assistant", "content": message.content})
239
+ # Add the assistant's message with tool calls
240
+ assistant_message = {
241
+ "role": "assistant",
242
+ "content": message.content or None, # Ensure content is None if empty
243
+ }
244
+ if message.tool_calls:
245
+ assistant_message["tool_calls"] = [
246
+ {
247
+ "id": tool_call.id,
248
+ "type": "function",
249
+ "function": {
250
+ "name": tool_call.function.name,
251
+ "arguments": tool_call.function.arguments,
252
+ },
253
+ }
254
+ for tool_call in message.tool_calls
255
+ ]
256
+ messages.append(assistant_message)
257
+ logger.debug(f"Added assistant message: {assistant_message}")
201
258
 
202
259
  # If there are no tool calls, we're done
203
260
  if not message.tool_calls:
@@ -220,7 +277,6 @@ class ToolClient:
220
277
  logger.debug(f"Raw arguments: {arguments}")
221
278
 
222
279
  if isinstance(arguments, str):
223
- import json
224
280
  try:
225
281
  arguments = json.loads(arguments)
226
282
  logger.debug(f"Parsed arguments: {arguments}")
@@ -236,31 +292,52 @@ class ToolClient:
236
292
  result = tool.function(**arguments)
237
293
  logger.debug(f"Tool result: {result}")
238
294
 
295
+ # Convert result to JSON string if it's not already
296
+ if not isinstance(result, str):
297
+ result = json.dumps(result)
298
+
239
299
  # Add the tool response to the messages
240
300
  tool_response = {
241
301
  "role": "tool",
242
302
  "tool_call_id": tool_call.id,
243
- "name": tool_call.function.name,
244
- "content": str(result),
303
+ "content": result,
245
304
  }
246
305
  messages.append(tool_response)
247
306
  logger.debug(f"Added tool response to messages: {tool_response}")
248
307
 
249
- # Get a new response from the model with the tool results
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
308
+ # Get a new response from the model with this tool result
309
+ response = self._client.chat_completion_parse(
310
+ messages=messages,
311
+ response_format=response_format,
312
+ model=model,
313
+ stream=stream,
314
+ **kwargs,
315
+ )
316
+
317
+ if stream:
318
+ return response
319
+
320
+ message = response.choices[0].message
321
+ # Add the assistant's message with tool calls
322
+ assistant_message = {
323
+ "role": "assistant",
324
+ "content": message.content or None, # Ensure content is None if empty
325
+ }
326
+ if message.tool_calls:
327
+ assistant_message["tool_calls"] = [
328
+ {
329
+ "id": tool_call.id,
330
+ "type": "function",
331
+ "function": {
332
+ "name": tool_call.function.name,
333
+ "arguments": tool_call.function.arguments,
334
+ },
335
+ }
336
+ for tool_call in message.tool_calls
337
+ ]
338
+ messages.append(assistant_message)
339
+ logger.debug(f"Added assistant message: {assistant_message}")
340
+
341
+ # If there are no more tool calls, we're done
342
+ if not message.tool_calls:
343
+ return response
@@ -292,11 +292,11 @@ wheels = [
292
292
 
293
293
  [[package]]
294
294
  name = "httpx-sse"
295
- version = "0.5.9"
295
+ version = "0.5.11"
296
296
  source = { registry = "https://pypi.org/simple" }
297
- sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.9.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
297
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.11.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
298
298
  wheels = [
299
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.9-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
299
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.11-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.9"
449
+ version = "0.5.11"
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.9"
983
+ version = "0.5.11"
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.9.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
988
+ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.11.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
989
989
  wheels = [
990
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.9-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
990
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.11-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
File without changes