mbxai 0.5.12__py3-none-any.whl → 0.5.13__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.12"
5
+ __version__ = "0.5.13"
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.12",
34
+ version="0.5.13",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
mbxai/tools/client.py CHANGED
@@ -48,7 +48,57 @@ class ToolClient:
48
48
  schema=schema,
49
49
  )
50
50
  self._tools[name] = tool
51
- logger.debug(f"Registered tool: {name}")
51
+ logger.info(f"Registered tool: {name}")
52
+
53
+ def _truncate_content(self, content: str | None, max_length: int = 100) -> str:
54
+ """Truncate content for logging."""
55
+ if not content:
56
+ return "None"
57
+ if len(content) <= max_length:
58
+ return content
59
+ return content[:max_length] + "..."
60
+
61
+ def _log_messages(self, messages: list[dict[str, Any]]) -> None:
62
+ """Log the messages being sent to OpenRouter."""
63
+ logger.info("Sending messages to OpenRouter:")
64
+ for msg in messages:
65
+ role = msg.get("role", "unknown")
66
+ content = self._truncate_content(msg.get("content"))
67
+ tool_calls = msg.get("tool_calls", [])
68
+ tool_call_id = msg.get("tool_call_id")
69
+
70
+ if tool_calls:
71
+ tool_call_info = [
72
+ f"{tc['function']['name']}(id={tc['id']})"
73
+ for tc in tool_calls
74
+ ]
75
+ logger.info(f" {role}: content='{content}', tool_calls={tool_call_info}")
76
+ elif tool_call_id:
77
+ logger.info(f" {role}: content='{content}', tool_call_id={tool_call_id}")
78
+ else:
79
+ logger.info(f" {role}: content='{content}'")
80
+
81
+ # Validate tool call responses
82
+ tool_call_ids = set()
83
+ tool_response_ids = set()
84
+
85
+ for msg in messages:
86
+ if msg.get("role") == "assistant" and "tool_calls" in msg:
87
+ for tc in msg["tool_calls"]:
88
+ tool_call_ids.add(tc["id"])
89
+ elif msg.get("role") == "tool":
90
+ tool_response_ids.add(msg["tool_call_id"])
91
+
92
+ missing_responses = tool_call_ids - tool_response_ids
93
+ if missing_responses:
94
+ logger.error(f"Missing tool responses for call IDs: {missing_responses}")
95
+ logger.error("Message sequence:")
96
+ for msg in messages:
97
+ role = msg.get("role", "unknown")
98
+ if role == "assistant" and "tool_calls" in msg:
99
+ logger.error(f" Assistant message with tool calls: {[tc['id'] for tc in msg['tool_calls']]}")
100
+ elif role == "tool":
101
+ logger.error(f" Tool response for call ID: {msg['tool_call_id']}")
52
102
 
53
103
  async def chat(
54
104
  self,
@@ -72,11 +122,14 @@ class ToolClient:
72
122
  tools = [tool.to_openai_function() for tool in self._tools.values()]
73
123
 
74
124
  if tools:
75
- logger.debug(f"Using tools: {tools}")
125
+ logger.info(f"Available tools: {[tool['function']['name'] for tool in tools]}")
76
126
  kwargs["tools"] = tools
77
127
  kwargs["tool_choice"] = "auto"
78
128
 
79
129
  while True:
130
+ # Log messages before sending to OpenRouter
131
+ self._log_messages(messages)
132
+
80
133
  # Get the model's response
81
134
  response = self._client.chat_completion(
82
135
  messages=messages,
@@ -107,7 +160,7 @@ class ToolClient:
107
160
  for tool_call in message.tool_calls
108
161
  ]
109
162
  messages.append(assistant_message)
110
- logger.debug(f"Added assistant message: {assistant_message}")
163
+ 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}")
111
164
 
112
165
  # If there are no tool calls, we're done
113
166
  if not message.tool_calls:
@@ -116,35 +169,25 @@ class ToolClient:
116
169
  # Handle all tool calls before getting the next model response
117
170
  tool_responses = []
118
171
  for tool_call in message.tool_calls:
119
- logger.debug(f"Processing tool call: {tool_call}")
120
- logger.debug(f"Tool call ID: {tool_call.id}")
121
- logger.debug(f"Tool call function: {tool_call.function}")
122
- logger.debug(f"Tool call arguments: {tool_call.function.arguments}")
123
-
124
172
  tool = self._tools.get(tool_call.function.name)
125
173
  if not tool:
126
174
  raise ValueError(f"Unknown tool: {tool_call.function.name}")
127
175
 
128
176
  # Parse arguments if they're a string
129
177
  arguments = tool_call.function.arguments
130
- logger.debug(f"Raw arguments type: {type(arguments)}")
131
- logger.debug(f"Raw arguments: {arguments}")
132
-
133
178
  if isinstance(arguments, str):
134
179
  try:
135
180
  arguments = json.loads(arguments)
136
- logger.debug(f"Parsed arguments: {arguments}")
137
181
  except json.JSONDecodeError as e:
138
182
  logger.error(f"Failed to parse tool arguments: {e}")
139
183
  raise ValueError(f"Invalid tool arguments format: {arguments}")
140
184
 
141
185
  # Call the tool
142
- logger.debug(f"Calling tool {tool.name} with arguments: {arguments}")
186
+ logger.info(f"Calling tool: {tool.name} with args: {self._truncate_content(json.dumps(arguments))}")
143
187
  if inspect.iscoroutinefunction(tool.function):
144
188
  result = await tool.function(**arguments)
145
189
  else:
146
190
  result = tool.function(**arguments)
147
- logger.debug(f"Tool result: {result}")
148
191
 
149
192
  # Convert result to JSON string if it's not already
150
193
  if not isinstance(result, str):
@@ -157,11 +200,10 @@ class ToolClient:
157
200
  "content": result,
158
201
  }
159
202
  tool_responses.append(tool_response)
160
- logger.debug(f"Created tool response: {tool_response}")
203
+ logger.info(f"Tool response for call ID {tool_call.id}: {self._truncate_content(result)}")
161
204
 
162
205
  # Add all tool responses to the messages
163
206
  messages.extend(tool_responses)
164
- logger.debug(f"Added all tool responses to messages: {tool_responses}")
165
207
 
166
208
  # Get a new response from the model with all tool results
167
209
  response = self._client.chat_completion(
@@ -193,7 +235,7 @@ class ToolClient:
193
235
  for tool_call in message.tool_calls
194
236
  ]
195
237
  messages.append(assistant_message)
196
- logger.debug(f"Added assistant message: {assistant_message}")
238
+ 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}")
197
239
 
198
240
  # If there are no more tool calls, we're done
199
241
  if not message.tool_calls:
@@ -223,11 +265,14 @@ class ToolClient:
223
265
  tools = [tool.to_openai_function() for tool in self._tools.values()]
224
266
 
225
267
  if tools:
226
- logger.debug(f"Using tools: {tools}")
268
+ logger.info(f"Available tools: {[tool['function']['name'] for tool in tools]}")
227
269
  kwargs["tools"] = tools
228
270
  kwargs["tool_choice"] = "auto"
229
271
 
230
272
  while True:
273
+ # Log messages before sending to OpenRouter
274
+ self._log_messages(messages)
275
+
231
276
  # Get the model's response
232
277
  response = self._client.chat_completion_parse(
233
278
  messages=messages,
@@ -259,7 +304,7 @@ class ToolClient:
259
304
  for tool_call in message.tool_calls
260
305
  ]
261
306
  messages.append(assistant_message)
262
- logger.debug(f"Added assistant message: {assistant_message}")
307
+ 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}")
263
308
 
264
309
  # If there are no tool calls, we're done
265
310
  if not message.tool_calls:
@@ -268,35 +313,25 @@ class ToolClient:
268
313
  # Handle all tool calls before getting the next model response
269
314
  tool_responses = []
270
315
  for tool_call in message.tool_calls:
271
- logger.debug(f"Processing tool call: {tool_call}")
272
- logger.debug(f"Tool call ID: {tool_call.id}")
273
- logger.debug(f"Tool call function: {tool_call.function}")
274
- logger.debug(f"Tool call arguments: {tool_call.function.arguments}")
275
-
276
316
  tool = self._tools.get(tool_call.function.name)
277
317
  if not tool:
278
318
  raise ValueError(f"Unknown tool: {tool_call.function.name}")
279
319
 
280
320
  # Parse arguments if they're a string
281
321
  arguments = tool_call.function.arguments
282
- logger.debug(f"Raw arguments type: {type(arguments)}")
283
- logger.debug(f"Raw arguments: {arguments}")
284
-
285
322
  if isinstance(arguments, str):
286
323
  try:
287
324
  arguments = json.loads(arguments)
288
- logger.debug(f"Parsed arguments: {arguments}")
289
325
  except json.JSONDecodeError as e:
290
326
  logger.error(f"Failed to parse tool arguments: {e}")
291
327
  raise ValueError(f"Invalid tool arguments format: {arguments}")
292
328
 
293
329
  # Call the tool
294
- logger.debug(f"Calling tool {tool.name} with arguments: {arguments}")
330
+ logger.info(f"Calling tool: {tool.name} with args: {self._truncate_content(json.dumps(arguments))}")
295
331
  if inspect.iscoroutinefunction(tool.function):
296
332
  result = await tool.function(**arguments)
297
333
  else:
298
334
  result = tool.function(**arguments)
299
- logger.debug(f"Tool result: {result}")
300
335
 
301
336
  # Convert result to JSON string if it's not already
302
337
  if not isinstance(result, str):
@@ -309,11 +344,10 @@ class ToolClient:
309
344
  "content": result,
310
345
  }
311
346
  tool_responses.append(tool_response)
312
- logger.debug(f"Created tool response: {tool_response}")
347
+ logger.info(f"Tool response: {self._truncate_content(result)}")
313
348
 
314
349
  # Add all tool responses to the messages
315
350
  messages.extend(tool_responses)
316
- logger.debug(f"Added all tool responses to messages: {tool_responses}")
317
351
 
318
352
  # Get a new response from the model with all tool results
319
353
  response = self._client.chat_completion_parse(
@@ -346,7 +380,7 @@ class ToolClient:
346
380
  for tool_call in message.tool_calls
347
381
  ]
348
382
  messages.append(assistant_message)
349
- logger.debug(f"Added assistant message: {assistant_message}")
383
+ 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}")
350
384
 
351
385
  # If there are no more tool calls, we're done
352
386
  if not message.tool_calls:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.5.12
3
+ Version: 0.5.13
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=IZikFZwxYq2ZzCj7jfjGct1is4gCcMAc590whjAThy4,48
1
+ mbxai/__init__.py,sha256=ntS2NXZZtMVTIOikxWyMC-297rihAZ-Ri99GSDnpEK4,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=MXGh1jn4tKLY49fAs_5rTaVMh1GT-azvwyXGHIDoSRg,3463
6
+ mbxai/mcp/server.py,sha256=53nE_XUzGBeaXRiayADIG4ikRfbqitR8qUVblKoHaNs,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=8aTrRSljnjrKIaBzLspSc8OpqRar6LZn__SVNOBpgHM,13190
12
+ mbxai/tools/client.py,sha256=DQYrvTWPfwRPQ0sazu_0ThDP_EowX77Kv5khy50koF4,15044
13
13
  mbxai/tools/example.py,sha256=1HgKK39zzUuwFbnp3f0ThyWVfA_8P28PZcTwaUw5K78,2232
14
14
  mbxai/tools/types.py,sha256=fo5t9UbsHGynhA88vD_ecgDqL8iLvt2E1h1ym43Rrgk,745
15
- mbxai-0.5.12.dist-info/METADATA,sha256=mga6ghbaA3DUBmlVAAGAyHiACU--F6dgiR038ausnec,4108
16
- mbxai-0.5.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- mbxai-0.5.12.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
- mbxai-0.5.12.dist-info/RECORD,,
15
+ mbxai-0.5.13.dist-info/METADATA,sha256=HYHa_ZngFYSvDCmmkoD3fL5iMQ836G9B4gcCwrnPLYA,4108
16
+ mbxai-0.5.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
+ mbxai-0.5.13.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
18
+ mbxai-0.5.13.dist-info/RECORD,,
File without changes