mbxai 0.5.18__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 +1 -1
- mbxai/mcp/server.py +1 -1
- mbxai/tools/client.py +72 -54
- {mbxai-0.5.18.dist-info → mbxai-0.5.20.dist-info}/METADATA +1 -1
- {mbxai-0.5.18.dist-info → mbxai-0.5.20.dist-info}/RECORD +7 -7
- {mbxai-0.5.18.dist-info → mbxai-0.5.20.dist-info}/WHEEL +0 -0
- {mbxai-0.5.18.dist-info → mbxai-0.5.20.dist-info}/licenses/LICENSE +0 -0
mbxai/__init__.py
CHANGED
mbxai/mcp/server.py
CHANGED
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]],
|
@@ -147,17 +203,7 @@ class ToolClient:
|
|
147
203
|
stream: bool = False,
|
148
204
|
**kwargs: Any,
|
149
205
|
) -> Any:
|
150
|
-
"""Chat with the model, handling tool calls.
|
151
|
-
|
152
|
-
Args:
|
153
|
-
messages: The conversation messages
|
154
|
-
model: Optional model override
|
155
|
-
stream: Whether to stream the response
|
156
|
-
**kwargs: Additional parameters for the chat completion
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
The final response from the model
|
160
|
-
"""
|
206
|
+
"""Chat with the model, handling tool calls."""
|
161
207
|
tools = [tool.to_openai_function() for tool in self._tools.values()]
|
162
208
|
|
163
209
|
if tools:
|
@@ -166,9 +212,6 @@ class ToolClient:
|
|
166
212
|
kwargs["tool_choice"] = "auto"
|
167
213
|
|
168
214
|
while True:
|
169
|
-
# Log messages before sending to OpenRouter, but don't validate responses yet
|
170
|
-
self._log_messages(messages, validate_responses=False)
|
171
|
-
|
172
215
|
# Get the model's response
|
173
216
|
response = self._client.chat_completion(
|
174
217
|
messages=messages,
|
@@ -199,13 +242,14 @@ class ToolClient:
|
|
199
242
|
for tool_call in message.tool_calls
|
200
243
|
]
|
201
244
|
messages.append(assistant_message)
|
202
|
-
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}")
|
203
246
|
|
204
247
|
# If there are no tool calls, we're done
|
205
248
|
if not message.tool_calls:
|
206
249
|
return response
|
207
250
|
|
208
|
-
#
|
251
|
+
# Process all tool calls
|
252
|
+
tool_responses = []
|
209
253
|
for tool_call in message.tool_calls:
|
210
254
|
tool = self._tools.get(tool_call.function.name)
|
211
255
|
if not tool:
|
@@ -231,53 +275,27 @@ class ToolClient:
|
|
231
275
|
if not isinstance(result, str):
|
232
276
|
result = json.dumps(result)
|
233
277
|
|
234
|
-
# Create
|
278
|
+
# Create the tool response
|
235
279
|
tool_response = {
|
236
280
|
"role": "tool",
|
237
281
|
"tool_call_id": tool_call.id,
|
238
282
|
"content": result,
|
239
283
|
}
|
240
|
-
|
241
|
-
logger.info(f"
|
242
|
-
|
243
|
-
# Now validate the message sequence after all tool calls have been processed
|
244
|
-
self._validate_message_sequence(messages, validate_responses=True)
|
284
|
+
tool_responses.append(tool_response)
|
285
|
+
logger.info(f"Created tool response for call ID {tool_call.id}")
|
245
286
|
|
246
|
-
#
|
247
|
-
|
248
|
-
|
249
|
-
model=model,
|
250
|
-
stream=stream,
|
251
|
-
**kwargs,
|
252
|
-
)
|
287
|
+
# Add all tool responses to the messages
|
288
|
+
messages.extend(tool_responses)
|
289
|
+
logger.info(f"Message count: {len(messages)}, Added {len(tool_responses)} tool responses to messages")
|
253
290
|
|
254
|
-
|
255
|
-
|
291
|
+
# Validate the message sequence
|
292
|
+
self._validate_message_sequence(messages, validate_responses=True)
|
256
293
|
|
257
|
-
|
258
|
-
|
259
|
-
assistant_message = {
|
260
|
-
"role": "assistant",
|
261
|
-
"content": message.content or None, # Ensure content is None if empty
|
262
|
-
}
|
263
|
-
if message.tool_calls:
|
264
|
-
assistant_message["tool_calls"] = [
|
265
|
-
{
|
266
|
-
"id": tool_call.id,
|
267
|
-
"type": "function",
|
268
|
-
"function": {
|
269
|
-
"name": tool_call.function.name,
|
270
|
-
"arguments": tool_call.function.arguments,
|
271
|
-
},
|
272
|
-
}
|
273
|
-
for tool_call in message.tool_calls
|
274
|
-
]
|
275
|
-
messages.append(assistant_message)
|
276
|
-
logger.info(f"Added assistant message with tool calls: {[tc.function.name for tc in message.tool_calls] if message.tool_calls else None}")
|
294
|
+
# Log the messages we're about to send
|
295
|
+
self._log_messages(messages, validate_responses=False)
|
277
296
|
|
278
|
-
#
|
279
|
-
|
280
|
-
return response
|
297
|
+
# Continue the loop to get the next response
|
298
|
+
continue
|
281
299
|
|
282
300
|
async def parse(
|
283
301
|
self,
|
@@ -1,18 +1,18 @@
|
|
1
|
-
mbxai/__init__.py,sha256=
|
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=
|
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=
|
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.
|
16
|
-
mbxai-0.5.
|
17
|
-
mbxai-0.5.
|
18
|
-
mbxai-0.5.
|
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
|
File without changes
|