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 +1 -1
- mbxai/mcp/server.py +1 -1
- mbxai/tools/client.py +67 -33
- {mbxai-0.5.12.dist-info → mbxai-0.5.13.dist-info}/METADATA +1 -1
- {mbxai-0.5.12.dist-info → mbxai-0.5.13.dist-info}/RECORD +7 -7
- {mbxai-0.5.12.dist-info → mbxai-0.5.13.dist-info}/WHEEL +0 -0
- {mbxai-0.5.12.dist-info → mbxai-0.5.13.dist-info}/licenses/LICENSE +0 -0
mbxai/__init__.py
CHANGED
mbxai/mcp/server.py
CHANGED
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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,18 +1,18 @@
|
|
1
|
-
mbxai/__init__.py,sha256=
|
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=
|
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=
|
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.
|
16
|
-
mbxai-0.5.
|
17
|
-
mbxai-0.5.
|
18
|
-
mbxai-0.5.
|
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
|
File without changes
|