mbxai 0.5.16__tar.gz → 0.5.18__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.16 → mbxai-0.5.18}/PKG-INFO +1 -1
  2. {mbxai-0.5.16 → mbxai-0.5.18}/pyproject.toml +1 -1
  3. {mbxai-0.5.16 → mbxai-0.5.18}/setup.py +1 -1
  4. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/__init__.py +1 -1
  5. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/mcp/server.py +1 -1
  6. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/tools/client.py +60 -32
  7. {mbxai-0.5.16 → mbxai-0.5.18}/uv.lock +7 -7
  8. {mbxai-0.5.16 → mbxai-0.5.18}/.vscode/PythonImportHelper-v2-Completion.json +0 -0
  9. {mbxai-0.5.16 → mbxai-0.5.18}/LICENSE +0 -0
  10. {mbxai-0.5.16 → mbxai-0.5.18}/README.md +0 -0
  11. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/core.py +0 -0
  12. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/mcp/__init__.py +0 -0
  13. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/mcp/client.py +0 -0
  14. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/mcp/example.py +0 -0
  15. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/openrouter/__init__.py +0 -0
  16. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/openrouter/client.py +0 -0
  17. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/openrouter/config.py +0 -0
  18. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/openrouter/models.py +0 -0
  19. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/tools/__init__.py +0 -0
  20. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/tools/example.py +0 -0
  21. {mbxai-0.5.16 → mbxai-0.5.18}/src/mbxai/tools/types.py +0 -0
  22. {mbxai-0.5.16 → mbxai-0.5.18}/tests/test_core.py +0 -0
  23. {mbxai-0.5.16 → mbxai-0.5.18}/tests/test_mcp.py +0 -0
  24. {mbxai-0.5.16 → mbxai-0.5.18}/tests/test_openrouter.py +0 -0
  25. {mbxai-0.5.16 → mbxai-0.5.18}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.16
3
+ Version: 0.5.18
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.16"
7
+ version = "0.5.18"
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.16",
5
+ version="0.5.18",
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.16"
5
+ __version__ = "0.5.18"
@@ -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.16",
34
+ version="0.5.18",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
@@ -72,10 +72,54 @@ 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 _log_messages(self, messages: list[dict[str, Any]]) -> None:
76
- """Log the messages being sent to OpenRouter."""
75
+ def _validate_message_sequence(self, messages: list[dict[str, Any]], validate_responses: bool = True) -> None:
76
+ """Validate the message sequence for tool calls and responses.
77
+
78
+ Args:
79
+ messages: The message sequence to validate
80
+ validate_responses: Whether to validate that all tool calls have responses
81
+ """
82
+ tool_call_ids = set()
83
+ tool_response_ids = set()
84
+
85
+ for i, msg in enumerate(messages):
86
+ role = msg.get("role")
87
+ if role == "assistant" and "tool_calls" in msg:
88
+ # Track tool calls
89
+ for tc in msg["tool_calls"]:
90
+ tool_call_ids.add(tc["id"])
91
+ logger.info(f"Found tool call {tc['id']} for {tc['function']['name']} in message {i}")
92
+ elif role == "tool":
93
+ # Track tool responses
94
+ tool_response_ids.add(msg["tool_call_id"])
95
+ logger.info(f"Found tool response for call ID {msg['tool_call_id']} in message {i}")
96
+
97
+ # Only validate responses if requested
98
+ if validate_responses:
99
+ # Check for missing responses
100
+ missing_responses = tool_call_ids - tool_response_ids
101
+ if missing_responses:
102
+ logger.error(f"Missing tool responses for call IDs: {missing_responses}")
103
+ logger.error("Message sequence:")
104
+ for i, msg in enumerate(messages):
105
+ role = msg.get("role", "unknown")
106
+ if role == "assistant" and "tool_calls" in msg:
107
+ logger.error(f" Message {i} - Assistant with tool calls: {[tc['id'] for tc in msg['tool_calls']]}")
108
+ elif role == "tool":
109
+ logger.error(f" Message {i} - Tool response for call ID: {msg['tool_call_id']}")
110
+ else:
111
+ logger.error(f" Message {i} - {role}: {self._truncate_content(msg.get('content'))}")
112
+ raise ValueError(f"Invalid message sequence: missing responses for tool calls {missing_responses}")
113
+
114
+ def _log_messages(self, messages: list[dict[str, Any]], validate_responses: bool = True) -> None:
115
+ """Log the messages being sent to OpenRouter.
116
+
117
+ Args:
118
+ messages: The messages to log
119
+ validate_responses: Whether to validate that all tool calls have responses
120
+ """
77
121
  logger.info("Sending messages to OpenRouter:")
78
- for msg in messages:
122
+ for i, msg in enumerate(messages):
79
123
  role = msg.get("role", "unknown")
80
124
  content = self._truncate_content(msg.get("content"))
81
125
  tool_calls = msg.get("tool_calls", [])
@@ -86,33 +130,14 @@ class ToolClient:
86
130
  f"{tc['function']['name']}(id={tc['id']})"
87
131
  for tc in tool_calls
88
132
  ]
89
- logger.info(f" {role}: content='{content}', tool_calls={tool_call_info}")
133
+ logger.info(f" Message {i} - {role}: content='{content}', tool_calls={tool_call_info}")
90
134
  elif tool_call_id:
91
- logger.info(f" {role}: content='{content}', tool_call_id={tool_call_id}")
135
+ logger.info(f" Message {i} - {role}: content='{content}', tool_call_id={tool_call_id}")
92
136
  else:
93
- logger.info(f" {role}: content='{content}'")
94
-
95
- # Validate tool call responses
96
- tool_call_ids = set()
97
- tool_response_ids = set()
98
-
99
- for msg in messages:
100
- if msg.get("role") == "assistant" and "tool_calls" in msg:
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"])
137
+ logger.info(f" Message {i} - {role}: content='{content}'")
105
138
 
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']}")
139
+ # Validate message sequence
140
+ self._validate_message_sequence(messages, validate_responses)
116
141
 
117
142
  async def chat(
118
143
  self,
@@ -141,8 +166,8 @@ class ToolClient:
141
166
  kwargs["tool_choice"] = "auto"
142
167
 
143
168
  while True:
144
- # Log messages before sending to OpenRouter
145
- self._log_messages(messages)
169
+ # Log messages before sending to OpenRouter, but don't validate responses yet
170
+ self._log_messages(messages, validate_responses=False)
146
171
 
147
172
  # Get the model's response
148
173
  response = self._client.chat_completion(
@@ -174,7 +199,7 @@ class ToolClient:
174
199
  for tool_call in message.tool_calls
175
200
  ]
176
201
  messages.append(assistant_message)
177
- logger.info(f"Assistant message: content='{self._truncate_content(message.content)}', tool_calls={[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
202
+ 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
203
 
179
204
  # If there are no tool calls, we're done
180
205
  if not message.tool_calls:
@@ -213,7 +238,10 @@ class ToolClient:
213
238
  "content": result,
214
239
  }
215
240
  messages.append(tool_response)
216
- logger.info(f"Tool response for call ID {tool_call.id}: {self._truncate_content(result)}")
241
+ logger.info(f"Added tool response for call ID {tool_call.id}")
242
+
243
+ # Now validate the message sequence after all tool calls have been processed
244
+ self._validate_message_sequence(messages, validate_responses=True)
217
245
 
218
246
  # Get a new response from the model with all tool results
219
247
  response = self._client.chat_completion(
@@ -245,7 +273,7 @@ class ToolClient:
245
273
  for tool_call in message.tool_calls
246
274
  ]
247
275
  messages.append(assistant_message)
248
- logger.info(f"Assistant message: content='{self._truncate_content(message.content)}', tool_calls={[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
276
+ 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
277
 
250
278
  # If there are no more tool calls, we're done
251
279
  if not message.tool_calls:
@@ -292,11 +292,11 @@ wheels = [
292
292
 
293
293
  [[package]]
294
294
  name = "httpx-sse"
295
- version = "0.5.16"
295
+ version = "0.5.18"
296
296
  source = { registry = "https://pypi.org/simple" }
297
- sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.16.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
297
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.18.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
298
298
  wheels = [
299
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.16-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
299
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.18-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.16"
449
+ version = "0.5.18"
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.16"
983
+ version = "0.5.18"
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.16.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
988
+ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.18.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
989
989
  wheels = [
990
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.16-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
990
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.18-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