mbxai 0.5.15__py3-none-any.whl → 0.5.17__py3-none-any.whl

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/__init__.py CHANGED
@@ -2,4 +2,4 @@
2
2
  MBX AI package.
3
3
  """
4
4
 
5
- __version__ = "0.5.15"
5
+ __version__ = "0.5.17"
mbxai/mcp/server.py CHANGED
@@ -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.15",
34
+ version="0.5.17",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
mbxai/tools/client.py CHANGED
@@ -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
- 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"])
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"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}")
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"Tool response for call ID {tool_call.id}: {self._truncate_content(result)}")
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"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}")
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
- tool_responses.append(tool_response)
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(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.15
3
+ Version: 0.5.17
4
4
  Summary: MBX AI SDK
5
5
  Project-URL: Homepage, https://www.mibexx.de
6
6
  Project-URL: Documentation, https://www.mibexx.de
@@ -1,18 +1,18 @@
1
- mbxai/__init__.py,sha256=bRHyO3paNdo9cp-TPVr7zZ4cW-RvHzQm5rwAp-WDYb0,48
1
+ mbxai/__init__.py,sha256=ZLQIr0rOpwAUUWES-qp9QdbVKYWSAdUb9L08NrI7SB4,48
2
2
  mbxai/core.py,sha256=WMvmU9TTa7M_m-qWsUew4xH8Ul6xseCZ2iBCXJTW-Bs,196
3
3
  mbxai/mcp/__init__.py,sha256=_ek9iYdYqW5saKetj4qDci11jxesQDiHPJRpHMKkxgU,175
4
4
  mbxai/mcp/client.py,sha256=hEAVWIrIq758C1zm9aWGf-FiITB3LxtuxZEZ0CcjJ4s,5343
5
5
  mbxai/mcp/example.py,sha256=oaol7AvvZnX86JWNz64KvPjab5gg1VjVN3G8eFSzuaE,2350
6
- mbxai/mcp/server.py,sha256=CMcgw3FKAZKmiKYfy09-Gs9vTGA9kWGR-O5nGw4F_Xo,3463
6
+ mbxai/mcp/server.py,sha256=apl2xuRSpFpqsgYNE42WCD-YfNVFguyUSYaYzngGj1Q,3463
7
7
  mbxai/openrouter/__init__.py,sha256=Ito9Qp_B6q-RLGAQcYyTJVWwR2YAZvNqE-HIYXxhtD8,298
8
8
  mbxai/openrouter/client.py,sha256=XLRMRNRJH96Jl6_af0KkzRDdLJnixh8I3RvEEcFuXyg,10840
9
9
  mbxai/openrouter/config.py,sha256=MTX_YHsFrM7JYqovJSkEF6JzVyIdajeI5Dja2CALH58,2874
10
10
  mbxai/openrouter/models.py,sha256=b3IjjtZAjeGOf2rLsdnCD1HacjTnS8jmv_ZXorc-KJQ,2604
11
11
  mbxai/tools/__init__.py,sha256=QUFaXhDm-UKcuAtT1rbKzhBkvyRBVokcQIOf9cxIuwc,160
12
- mbxai/tools/client.py,sha256=jkP7HLlr-0SVklNnQRU9A6XX8-_ObixplNjheWlfZwY,15486
12
+ mbxai/tools/client.py,sha256=SunEXOpJva6faGGxzKtMhgscHPq7PS4YFwV_Ll6VX-Q,16298
13
13
  mbxai/tools/example.py,sha256=1HgKK39zzUuwFbnp3f0ThyWVfA_8P28PZcTwaUw5K78,2232
14
14
  mbxai/tools/types.py,sha256=fo5t9UbsHGynhA88vD_ecgDqL8iLvt2E1h1ym43Rrgk,745
15
- mbxai-0.5.15.dist-info/METADATA,sha256=SE49yR-ukNm-dhRxwXrp1xyQYktQ7mZMWZ1O_hvDESU,4108
16
- mbxai-0.5.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- mbxai-0.5.15.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
- mbxai-0.5.15.dist-info/RECORD,,
15
+ mbxai-0.5.17.dist-info/METADATA,sha256=s2QPZsInPdzDQBc18Jg5qisz-srRA9qo4cZ82ew2TLc,4108
16
+ mbxai-0.5.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
+ mbxai-0.5.17.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
+ mbxai-0.5.17.dist-info/RECORD,,
File without changes