mbxai 0.5.17__tar.gz → 0.5.19__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.17 → mbxai-0.5.19}/PKG-INFO +1 -1
  2. {mbxai-0.5.17 → mbxai-0.5.19}/pyproject.toml +1 -1
  3. {mbxai-0.5.17 → mbxai-0.5.19}/setup.py +1 -1
  4. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/__init__.py +1 -1
  5. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/mcp/server.py +1 -1
  6. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/tools/client.py +47 -40
  7. {mbxai-0.5.17 → mbxai-0.5.19}/uv.lock +7 -7
  8. {mbxai-0.5.17 → mbxai-0.5.19}/.vscode/PythonImportHelper-v2-Completion.json +0 -0
  9. {mbxai-0.5.17 → mbxai-0.5.19}/LICENSE +0 -0
  10. {mbxai-0.5.17 → mbxai-0.5.19}/README.md +0 -0
  11. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/core.py +0 -0
  12. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/mcp/__init__.py +0 -0
  13. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/mcp/client.py +0 -0
  14. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/mcp/example.py +0 -0
  15. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/openrouter/__init__.py +0 -0
  16. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/openrouter/client.py +0 -0
  17. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/openrouter/config.py +0 -0
  18. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/openrouter/models.py +0 -0
  19. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/tools/__init__.py +0 -0
  20. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/tools/example.py +0 -0
  21. {mbxai-0.5.17 → mbxai-0.5.19}/src/mbxai/tools/types.py +0 -0
  22. {mbxai-0.5.17 → mbxai-0.5.19}/tests/test_core.py +0 -0
  23. {mbxai-0.5.17 → mbxai-0.5.19}/tests/test_mcp.py +0 -0
  24. {mbxai-0.5.17 → mbxai-0.5.19}/tests/test_openrouter.py +0 -0
  25. {mbxai-0.5.17 → mbxai-0.5.19}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.17
3
+ Version: 0.5.19
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.17"
7
+ version = "0.5.19"
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.17",
5
+ version="0.5.19",
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.17"
5
+ __version__ = "0.5.19"
@@ -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.17",
34
+ version="0.5.19",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
@@ -72,8 +72,13 @@ 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."""
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
+ """
77
82
  tool_call_ids = set()
78
83
  tool_response_ids = set()
79
84
 
@@ -89,23 +94,30 @@ class ToolClient:
89
94
  tool_response_ids.add(msg["tool_call_id"])
90
95
  logger.info(f"Found tool response for call ID {msg['tool_call_id']} in message {i}")
91
96
 
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
-
107
- def _log_messages(self, messages: list[dict[str, Any]]) -> None:
108
- """Log the messages being sent to OpenRouter."""
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
+ """
109
121
  logger.info("Sending messages to OpenRouter:")
110
122
  for i, msg in enumerate(messages):
111
123
  role = msg.get("role", "unknown")
@@ -125,7 +137,7 @@ class ToolClient:
125
137
  logger.info(f" Message {i} - {role}: content='{content}'")
126
138
 
127
139
  # Validate message sequence
128
- self._validate_message_sequence(messages)
140
+ self._validate_message_sequence(messages, validate_responses)
129
141
 
130
142
  async def chat(
131
143
  self,
@@ -135,17 +147,7 @@ class ToolClient:
135
147
  stream: bool = False,
136
148
  **kwargs: Any,
137
149
  ) -> Any:
138
- """Chat with the model, handling tool calls.
139
-
140
- Args:
141
- messages: The conversation messages
142
- model: Optional model override
143
- stream: Whether to stream the response
144
- **kwargs: Additional parameters for the chat completion
145
-
146
- Returns:
147
- The final response from the model
148
- """
150
+ """Chat with the model, handling tool calls."""
149
151
  tools = [tool.to_openai_function() for tool in self._tools.values()]
150
152
 
151
153
  if tools:
@@ -154,9 +156,6 @@ class ToolClient:
154
156
  kwargs["tool_choice"] = "auto"
155
157
 
156
158
  while True:
157
- # Log messages before sending to OpenRouter
158
- self._log_messages(messages)
159
-
160
159
  # Get the model's response
161
160
  response = self._client.chat_completion(
162
161
  messages=messages,
@@ -193,7 +192,8 @@ class ToolClient:
193
192
  if not message.tool_calls:
194
193
  return response
195
194
 
196
- # Handle all tool calls before getting the next model response
195
+ # Process all tool calls first
196
+ tool_responses = []
197
197
  for tool_call in message.tool_calls:
198
198
  tool = self._tools.get(tool_call.function.name)
199
199
  if not tool:
@@ -219,17 +219,24 @@ class ToolClient:
219
219
  if not isinstance(result, str):
220
220
  result = json.dumps(result)
221
221
 
222
- # Create and add the tool response immediately
222
+ # Create the tool response
223
223
  tool_response = {
224
224
  "role": "tool",
225
225
  "tool_call_id": tool_call.id,
226
226
  "content": result,
227
227
  }
228
- messages.append(tool_response)
229
- logger.info(f"Added tool response for call ID {tool_call.id}")
228
+ tool_responses.append(tool_response)
229
+ logger.info(f"Created tool response for call ID {tool_call.id}")
230
+
231
+ # Add all tool responses to the messages
232
+ messages.extend(tool_responses)
233
+ logger.info(f"Added {len(tool_responses)} tool responses to messages")
234
+
235
+ # Validate the message sequence
236
+ self._validate_message_sequence(messages, validate_responses=True)
230
237
 
231
- # Validate message sequence before getting next response
232
- self._validate_message_sequence(messages)
238
+ # Log the messages we're about to send
239
+ self._log_messages(messages, validate_responses=False)
233
240
 
234
241
  # Get a new response from the model with all tool results
235
242
  response = self._client.chat_completion(
@@ -292,11 +292,11 @@ wheels = [
292
292
 
293
293
  [[package]]
294
294
  name = "httpx-sse"
295
- version = "0.5.17"
295
+ version = "0.5.19"
296
296
  source = { registry = "https://pypi.org/simple" }
297
- sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.17.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
297
+ sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.5.19.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
298
298
  wheels = [
299
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.17-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
299
+ { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.5.19-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.17"
449
+ version = "0.5.19"
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.17"
983
+ version = "0.5.19"
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.17.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
988
+ sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.5.19.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
989
989
  wheels = [
990
- { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.17-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
990
+ { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.5.19-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