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.
- {mbxai-0.5.10 → mbxai-0.5.12}/.vscode/PythonImportHelper-v2-Completion.json +9 -9
- {mbxai-0.5.10 → mbxai-0.5.12}/PKG-INFO +1 -1
- {mbxai-0.5.10 → mbxai-0.5.12}/pyproject.toml +1 -1
- {mbxai-0.5.10 → mbxai-0.5.12}/setup.py +1 -1
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/__init__.py +1 -1
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/server.py +1 -1
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/client.py +136 -49
- {mbxai-0.5.10 → mbxai-0.5.12}/uv.lock +7 -7
- {mbxai-0.5.10 → mbxai-0.5.12}/LICENSE +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/README.md +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/core.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/__init__.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/client.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/mcp/example.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/__init__.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/client.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/config.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/openrouter/models.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/__init__.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/example.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/src/mbxai/tools/types.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_core.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_mcp.py +0 -0
- {mbxai-0.5.10 → mbxai-0.5.12}/tests/test_openrouter.py +0 -0
- {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",
|
@@ -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
|
-
|
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
|
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
|
-
#
|
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
|
-
"
|
135
|
-
"content": str(result),
|
157
|
+
"content": result,
|
136
158
|
}
|
137
|
-
|
138
|
-
logger.debug(f"
|
159
|
+
tool_responses.append(tool_response)
|
160
|
+
logger.debug(f"Created tool response: {tool_response}")
|
139
161
|
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
149
|
-
|
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
|
-
|
152
|
-
|
174
|
+
if stream:
|
175
|
+
return response
|
153
176
|
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
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
|
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
|
-
#
|
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
|
-
"
|
244
|
-
"content": str(result),
|
309
|
+
"content": result,
|
245
310
|
}
|
246
|
-
|
247
|
-
logger.debug(f"
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|