mbxai 0.5.15__tar.gz → 0.5.17__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.15 → mbxai-0.5.17}/PKG-INFO +1 -1
- {mbxai-0.5.15 → mbxai-0.5.17}/pyproject.toml +1 -1
- {mbxai-0.5.15 → mbxai-0.5.17}/setup.py +1 -1
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/__init__.py +1 -1
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/mcp/server.py +1 -1
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/tools/client.py +47 -35
- {mbxai-0.5.15 → mbxai-0.5.17}/uv.lock +7 -7
- {mbxai-0.5.15 → mbxai-0.5.17}/.vscode/PythonImportHelper-v2-Completion.json +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/LICENSE +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/README.md +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/core.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/mcp/__init__.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/mcp/client.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/mcp/example.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/openrouter/__init__.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/openrouter/client.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/openrouter/config.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/openrouter/models.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/tools/__init__.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/tools/example.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/src/mbxai/tools/types.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/tests/test_core.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/tests/test_mcp.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/tests/test_openrouter.py +0 -0
- {mbxai-0.5.15 → mbxai-0.5.17}/tests/test_tools.py +0 -0
@@ -72,10 +72,42 @@ class ToolClient:
|
|
72
72
|
truncated[k] = str(v)[:max_length] + "..." if len(str(v)) > max_length else v
|
73
73
|
return str(truncated)
|
74
74
|
|
75
|
+
def _validate_message_sequence(self, messages: list[dict[str, Any]]) -> None:
|
76
|
+
"""Validate the message sequence for tool calls and responses."""
|
77
|
+
tool_call_ids = set()
|
78
|
+
tool_response_ids = set()
|
79
|
+
|
80
|
+
for i, msg in enumerate(messages):
|
81
|
+
role = msg.get("role")
|
82
|
+
if role == "assistant" and "tool_calls" in msg:
|
83
|
+
# Track tool calls
|
84
|
+
for tc in msg["tool_calls"]:
|
85
|
+
tool_call_ids.add(tc["id"])
|
86
|
+
logger.info(f"Found tool call {tc['id']} for {tc['function']['name']} in message {i}")
|
87
|
+
elif role == "tool":
|
88
|
+
# Track tool responses
|
89
|
+
tool_response_ids.add(msg["tool_call_id"])
|
90
|
+
logger.info(f"Found tool response for call ID {msg['tool_call_id']} in message {i}")
|
91
|
+
|
92
|
+
# Check for missing responses
|
93
|
+
missing_responses = tool_call_ids - tool_response_ids
|
94
|
+
if missing_responses:
|
95
|
+
logger.error(f"Missing tool responses for call IDs: {missing_responses}")
|
96
|
+
logger.error("Message sequence:")
|
97
|
+
for i, msg in enumerate(messages):
|
98
|
+
role = msg.get("role", "unknown")
|
99
|
+
if role == "assistant" and "tool_calls" in msg:
|
100
|
+
logger.error(f" Message {i} - Assistant with tool calls: {[tc['id'] for tc in msg['tool_calls']]}")
|
101
|
+
elif role == "tool":
|
102
|
+
logger.error(f" Message {i} - Tool response for call ID: {msg['tool_call_id']}")
|
103
|
+
else:
|
104
|
+
logger.error(f" Message {i} - {role}: {self._truncate_content(msg.get('content'))}")
|
105
|
+
raise ValueError(f"Invalid message sequence: missing responses for tool calls {missing_responses}")
|
106
|
+
|
75
107
|
def _log_messages(self, messages: list[dict[str, Any]]) -> None:
|
76
108
|
"""Log the messages being sent to OpenRouter."""
|
77
109
|
logger.info("Sending messages to OpenRouter:")
|
78
|
-
for msg in messages:
|
110
|
+
for i, msg in enumerate(messages):
|
79
111
|
role = msg.get("role", "unknown")
|
80
112
|
content = self._truncate_content(msg.get("content"))
|
81
113
|
tool_calls = msg.get("tool_calls", [])
|
@@ -86,33 +118,14 @@ class ToolClient:
|
|
86
118
|
f"{tc['function']['name']}(id={tc['id']})"
|
87
119
|
for tc in tool_calls
|
88
120
|
]
|
89
|
-
logger.info(f" {role}: content='{content}', tool_calls={tool_call_info}")
|
121
|
+
logger.info(f" Message {i} - {role}: content='{content}', tool_calls={tool_call_info}")
|
90
122
|
elif tool_call_id:
|
91
|
-
logger.info(f" {role}: content='{content}', tool_call_id={tool_call_id}")
|
123
|
+
logger.info(f" Message {i} - {role}: content='{content}', tool_call_id={tool_call_id}")
|
92
124
|
else:
|
93
|
-
logger.info(f" {role}: content='{content}'")
|
94
|
-
|
95
|
-
# Validate tool call responses
|
96
|
-
tool_call_ids = set()
|
97
|
-
tool_response_ids = set()
|
125
|
+
logger.info(f" Message {i} - {role}: content='{content}'")
|
98
126
|
|
99
|
-
|
100
|
-
|
101
|
-
for tc in msg["tool_calls"]:
|
102
|
-
tool_call_ids.add(tc["id"])
|
103
|
-
elif msg.get("role") == "tool":
|
104
|
-
tool_response_ids.add(msg["tool_call_id"])
|
105
|
-
|
106
|
-
missing_responses = tool_call_ids - tool_response_ids
|
107
|
-
if missing_responses:
|
108
|
-
logger.error(f"Missing tool responses for call IDs: {missing_responses}")
|
109
|
-
logger.error("Message sequence:")
|
110
|
-
for msg in messages:
|
111
|
-
role = msg.get("role", "unknown")
|
112
|
-
if role == "assistant" and "tool_calls" in msg:
|
113
|
-
logger.error(f" Assistant message with tool calls: {[tc['id'] for tc in msg['tool_calls']]}")
|
114
|
-
elif role == "tool":
|
115
|
-
logger.error(f" Tool response for call ID: {msg['tool_call_id']}")
|
127
|
+
# Validate message sequence
|
128
|
+
self._validate_message_sequence(messages)
|
116
129
|
|
117
130
|
async def chat(
|
118
131
|
self,
|
@@ -174,7 +187,7 @@ class ToolClient:
|
|
174
187
|
for tool_call in message.tool_calls
|
175
188
|
]
|
176
189
|
messages.append(assistant_message)
|
177
|
-
logger.info(f"
|
190
|
+
logger.info(f"Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
|
178
191
|
|
179
192
|
# If there are no tool calls, we're done
|
180
193
|
if not message.tool_calls:
|
@@ -213,7 +226,10 @@ class ToolClient:
|
|
213
226
|
"content": result,
|
214
227
|
}
|
215
228
|
messages.append(tool_response)
|
216
|
-
logger.info(f"
|
229
|
+
logger.info(f"Added tool response for call ID {tool_call.id}")
|
230
|
+
|
231
|
+
# Validate message sequence before getting next response
|
232
|
+
self._validate_message_sequence(messages)
|
217
233
|
|
218
234
|
# Get a new response from the model with all tool results
|
219
235
|
response = self._client.chat_completion(
|
@@ -245,7 +261,7 @@ class ToolClient:
|
|
245
261
|
for tool_call in message.tool_calls
|
246
262
|
]
|
247
263
|
messages.append(assistant_message)
|
248
|
-
logger.info(f"
|
264
|
+
logger.info(f"Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
|
249
265
|
|
250
266
|
# If there are no more tool calls, we're done
|
251
267
|
if not message.tool_calls:
|
@@ -321,7 +337,6 @@ class ToolClient:
|
|
321
337
|
return response
|
322
338
|
|
323
339
|
# Handle all tool calls before getting the next model response
|
324
|
-
tool_responses = []
|
325
340
|
for tool_call in message.tool_calls:
|
326
341
|
tool = self._tools.get(tool_call.function.name)
|
327
342
|
if not tool:
|
@@ -347,17 +362,14 @@ class ToolClient:
|
|
347
362
|
if not isinstance(result, str):
|
348
363
|
result = json.dumps(result)
|
349
364
|
|
350
|
-
# Create the tool response
|
365
|
+
# Create and add the tool response immediately
|
351
366
|
tool_response = {
|
352
367
|
"role": "tool",
|
353
368
|
"tool_call_id": tool_call.id,
|
354
369
|
"content": result,
|
355
370
|
}
|
356
|
-
|
357
|
-
logger.info(f"Tool response: {self._truncate_content(result)}")
|
358
|
-
|
359
|
-
# Add all tool responses to the messages
|
360
|
-
messages.extend(tool_responses)
|
371
|
+
messages.append(tool_response)
|
372
|
+
logger.info(f"Tool response for call ID {tool_call.id}: {self._truncate_content(result)}")
|
361
373
|
|
362
374
|
# Get a new response from the model with all tool results
|
363
375
|
response = self._client.chat_completion_parse(
|
@@ -292,11 +292,11 @@ wheels = [
|
|
292
292
|
|
293
293
|
[[package]]
|
294
294
|
name = "httpx-sse"
|
295
|
-
version = "0.5.
|
295
|
+
version = "0.5.17"
|
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.17.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.17-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.17"
|
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.17"
|
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.17.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.17-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
|
File without changes
|