mbxai 0.5.19__py3-none-any.whl → 0.5.20__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.19"
5
+ __version__ = "0.5.20"
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.19",
34
+ version="0.5.20",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
mbxai/tools/client.py CHANGED
@@ -139,6 +139,62 @@ class ToolClient:
139
139
  # Validate message sequence
140
140
  self._validate_message_sequence(messages, validate_responses)
141
141
 
142
+ async def _process_tool_calls(self, message: Any, messages: list[dict[str, Any]]) -> None:
143
+ """Process all tool calls in a message.
144
+
145
+ Args:
146
+ message: The message containing tool calls
147
+ messages: The list of messages to add responses to
148
+ """
149
+ if not message.tool_calls:
150
+ return
151
+
152
+ # Process all tool calls first
153
+ tool_responses = []
154
+ for tool_call in message.tool_calls:
155
+ tool = self._tools.get(tool_call.function.name)
156
+ if not tool:
157
+ raise ValueError(f"Unknown tool: {tool_call.function.name}")
158
+
159
+ # Parse arguments if they're a string
160
+ arguments = tool_call.function.arguments
161
+ if isinstance(arguments, str):
162
+ try:
163
+ arguments = json.loads(arguments)
164
+ except json.JSONDecodeError as e:
165
+ logger.error(f"Failed to parse tool arguments: {e}")
166
+ raise ValueError(f"Invalid tool arguments format: {arguments}")
167
+
168
+ # Call the tool
169
+ logger.info(f"Calling tool: {tool.name} with args: {self._truncate_dict(arguments)}")
170
+ if inspect.iscoroutinefunction(tool.function):
171
+ result = await tool.function(**arguments)
172
+ else:
173
+ result = tool.function(**arguments)
174
+
175
+ # Convert result to JSON string if it's not already
176
+ if not isinstance(result, str):
177
+ result = json.dumps(result)
178
+
179
+ # Create the tool response
180
+ tool_response = {
181
+ "role": "tool",
182
+ "tool_call_id": tool_call.id,
183
+ "content": result,
184
+ }
185
+ tool_responses.append(tool_response)
186
+ logger.info(f"Created tool response for call ID {tool_call.id}")
187
+
188
+ # Add all tool responses to the messages
189
+ messages.extend(tool_responses)
190
+ logger.info(f"Message count: {len(messages)}, Added {len(tool_responses)} tool responses to messages")
191
+
192
+ # Validate the message sequence
193
+ self._validate_message_sequence(messages, validate_responses=True)
194
+
195
+ # Log the messages we're about to send
196
+ self._log_messages(messages, validate_responses=False)
197
+
142
198
  async def chat(
143
199
  self,
144
200
  messages: list[dict[str, Any]],
@@ -186,13 +242,13 @@ class ToolClient:
186
242
  for tool_call in message.tool_calls
187
243
  ]
188
244
  messages.append(assistant_message)
189
- logger.info(f"Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
245
+ logger.info(f"Message count: {len(messages)}, Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
190
246
 
191
247
  # If there are no tool calls, we're done
192
248
  if not message.tool_calls:
193
249
  return response
194
250
 
195
- # Process all tool calls first
251
+ # Process all tool calls
196
252
  tool_responses = []
197
253
  for tool_call in message.tool_calls:
198
254
  tool = self._tools.get(tool_call.function.name)
@@ -230,7 +286,7 @@ class ToolClient:
230
286
 
231
287
  # Add all tool responses to the messages
232
288
  messages.extend(tool_responses)
233
- logger.info(f"Added {len(tool_responses)} tool responses to messages")
289
+ logger.info(f"Message count: {len(messages)}, Added {len(tool_responses)} tool responses to messages")
234
290
 
235
291
  # Validate the message sequence
236
292
  self._validate_message_sequence(messages, validate_responses=True)
@@ -238,41 +294,8 @@ class ToolClient:
238
294
  # Log the messages we're about to send
239
295
  self._log_messages(messages, validate_responses=False)
240
296
 
241
- # Get a new response from the model with all tool results
242
- response = self._client.chat_completion(
243
- messages=messages,
244
- model=model,
245
- stream=stream,
246
- **kwargs,
247
- )
248
-
249
- if stream:
250
- return response
251
-
252
- message = response.choices[0].message
253
- # Add the assistant's message with tool calls
254
- assistant_message = {
255
- "role": "assistant",
256
- "content": message.content or None, # Ensure content is None if empty
257
- }
258
- if message.tool_calls:
259
- assistant_message["tool_calls"] = [
260
- {
261
- "id": tool_call.id,
262
- "type": "function",
263
- "function": {
264
- "name": tool_call.function.name,
265
- "arguments": tool_call.function.arguments,
266
- },
267
- }
268
- for tool_call in message.tool_calls
269
- ]
270
- messages.append(assistant_message)
271
- logger.info(f"Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
272
-
273
- # If there are no more tool calls, we're done
274
- if not message.tool_calls:
275
- return response
297
+ # Continue the loop to get the next response
298
+ continue
276
299
 
277
300
  async def parse(
278
301
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.19
3
+ Version: 0.5.20
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=fBHYV_6ZmWukpBBQkb6diHAP4h69-vDoHNwmywdi4G4,48
1
+ mbxai/__init__.py,sha256=PUJe90d--hNgAyBIE5Y18DZDiuCdN7znT-cSzdXGpWM,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=rjDCSR3IZOm5Xb-A2eMm-aMuJANSuRmq8uHmy85hjT8,3463
6
+ mbxai/mcp/server.py,sha256=8dfQSFdA0LN0kwSws-9our6vqq5L0F6U-s3gkB9-zME,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=6FWaM80OFeHR2tI6vhsUcqln8dObfglRiM36vPAyUB8,16733
12
+ mbxai/tools/client.py,sha256=chTzGN6Tyi8Z428e5sR3BklieH8vBSKlx95SZ56GKg8,17729
13
13
  mbxai/tools/example.py,sha256=1HgKK39zzUuwFbnp3f0ThyWVfA_8P28PZcTwaUw5K78,2232
14
14
  mbxai/tools/types.py,sha256=fo5t9UbsHGynhA88vD_ecgDqL8iLvt2E1h1ym43Rrgk,745
15
- mbxai-0.5.19.dist-info/METADATA,sha256=CZyWoVf5vd8iu0_kgCDNYWypoD_MyxggF3xYe9Xy6BE,4108
16
- mbxai-0.5.19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- mbxai-0.5.19.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
- mbxai-0.5.19.dist-info/RECORD,,
15
+ mbxai-0.5.20.dist-info/METADATA,sha256=cm360CdwmXEwZnwFNj8pS2ssbonWyl_zFCiKyCTMrrM,4108
16
+ mbxai-0.5.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
+ mbxai-0.5.20.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
+ mbxai-0.5.20.dist-info/RECORD,,
File without changes