mbxai 0.6.12__py3-none-any.whl → 0.6.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/openrouter/client.py +29 -31
- mbxai/tools/client.py +97 -27
- {mbxai-0.6.12.dist-info → mbxai-0.6.13.dist-info}/METADATA +1 -1
- {mbxai-0.6.12.dist-info → mbxai-0.6.13.dist-info}/RECORD +6 -6
- {mbxai-0.6.12.dist-info → mbxai-0.6.13.dist-info}/WHEEL +0 -0
- {mbxai-0.6.12.dist-info → mbxai-0.6.13.dist-info}/licenses/LICENSE +0 -0
mbxai/openrouter/client.py
CHANGED
@@ -267,43 +267,41 @@ class OpenRouterClient:
|
|
267
267
|
ValueError: If response parsing fails
|
268
268
|
"""
|
269
269
|
try:
|
270
|
-
#
|
271
|
-
|
272
|
-
|
273
|
-
"role": "system",
|
274
|
-
"content": "You are a helpful assistant that responds in valid JSON format."
|
275
|
-
})
|
270
|
+
# Log the request details
|
271
|
+
logger.info(f"Sending chat completion request to OpenRouter with model: {model or self.model}")
|
272
|
+
logger.info(f"Message count: {len(messages)}")
|
276
273
|
|
277
|
-
#
|
278
|
-
|
279
|
-
|
280
|
-
format_desc = f"Respond with valid JSON matching this Pydantic model: {response_format.__name__}"
|
281
|
-
last_user_msg["content"] = f"{format_desc}\n\n{last_user_msg['content']}"
|
274
|
+
# Calculate total message size for logging
|
275
|
+
total_size = sum(len(str(msg)) for msg in messages)
|
276
|
+
logger.info(f"Total message size: {total_size} bytes")
|
282
277
|
|
283
|
-
response = self.
|
284
|
-
messages,
|
285
|
-
model=model,
|
278
|
+
response = self._client.beta.chat.completions.parse(
|
279
|
+
messages=messages,
|
280
|
+
model=model or self.model,
|
281
|
+
response_format=response_format,
|
286
282
|
stream=stream,
|
287
|
-
|
288
|
-
**kwargs
|
283
|
+
**kwargs,
|
289
284
|
)
|
290
285
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
parsed = adapter.validate_json(content)
|
299
|
-
response.choices[0].message.parsed = parsed
|
300
|
-
return response
|
301
|
-
except Exception as e:
|
302
|
-
raise ValueError(f"Failed to parse response as {response_format.__name__}: {str(e)}")
|
303
|
-
except ValueError as e:
|
304
|
-
raise e
|
286
|
+
# Log response details
|
287
|
+
logger.info("Received response from OpenRouter")
|
288
|
+
if hasattr(response, 'choices') and response.choices:
|
289
|
+
logger.info(f"Response has {len(response.choices)} choices")
|
290
|
+
|
291
|
+
return response
|
292
|
+
|
305
293
|
except Exception as e:
|
306
|
-
|
294
|
+
logger.error(f"Error in chat completion: {str(e)}")
|
295
|
+
if hasattr(e, 'response') and e.response is not None:
|
296
|
+
logger.error(f"Response status: {e.response.status_code}")
|
297
|
+
logger.error(f"Response headers: {e.response.headers}")
|
298
|
+
try:
|
299
|
+
content = e.response.text
|
300
|
+
logger.error(f"Response content length: {len(content)} bytes")
|
301
|
+
logger.error(f"Response content preview: {content[:1000]}...")
|
302
|
+
except:
|
303
|
+
logger.error("Could not read response content")
|
304
|
+
self._handle_api_error("chat completion", e)
|
307
305
|
|
308
306
|
@with_retry()
|
309
307
|
def embeddings(
|
mbxai/tools/client.py
CHANGED
@@ -328,35 +328,105 @@ class ToolClient:
|
|
328
328
|
The parsed response from the model
|
329
329
|
"""
|
330
330
|
# First, use chat to handle any tool calls
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
331
|
+
tools = [tool.to_openai_function() for tool in self._tools.values()]
|
332
|
+
|
333
|
+
if tools:
|
334
|
+
logger.info(f"Available tools: {[tool['function']['name'] for tool in tools]}")
|
335
|
+
kwargs["tools"] = tools
|
336
|
+
kwargs["tool_choice"] = "auto"
|
337
|
+
|
338
|
+
while True:
|
339
|
+
# Get the model's response
|
340
|
+
response = self._client.chat_completion_parse(
|
341
|
+
messages=messages,
|
342
|
+
response_format=response_format,
|
343
|
+
model=model,
|
344
|
+
stream=stream,
|
345
|
+
**kwargs,
|
346
|
+
)
|
340
347
|
|
341
|
-
|
342
|
-
|
348
|
+
if stream:
|
349
|
+
return response
|
343
350
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
{
|
351
|
+
message = response.choices[0].message
|
352
|
+
# Add the assistant's message with tool calls
|
353
|
+
assistant_message = {
|
348
354
|
"role": "assistant",
|
349
|
-
"content":
|
355
|
+
"content": message.content or None,
|
356
|
+
"parsed": message.parsed or None,
|
350
357
|
}
|
351
|
-
|
358
|
+
if message.tool_calls:
|
359
|
+
assistant_message["tool_calls"] = [
|
360
|
+
{
|
361
|
+
"id": tool_call.id,
|
362
|
+
"type": "function",
|
363
|
+
"function": {
|
364
|
+
"name": tool_call.function.name,
|
365
|
+
"arguments": tool_call.function.arguments,
|
366
|
+
},
|
367
|
+
}
|
368
|
+
for tool_call in message.tool_calls
|
369
|
+
]
|
370
|
+
messages.append(assistant_message)
|
371
|
+
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}")
|
352
372
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
373
|
+
# If there are no tool calls, we're done
|
374
|
+
if not message.tool_calls:
|
375
|
+
return response
|
376
|
+
|
377
|
+
# Process all tool calls
|
378
|
+
tool_responses = []
|
379
|
+
for tool_call in message.tool_calls:
|
380
|
+
tool = self._tools.get(tool_call.function.name)
|
381
|
+
if not tool:
|
382
|
+
raise ValueError(f"Unknown tool: {tool_call.function.name}")
|
383
|
+
|
384
|
+
# Parse arguments if they're a string
|
385
|
+
arguments = tool_call.function.arguments
|
386
|
+
if isinstance(arguments, str):
|
387
|
+
try:
|
388
|
+
arguments = json.loads(arguments)
|
389
|
+
except json.JSONDecodeError as e:
|
390
|
+
logger.error(f"Failed to parse tool arguments: {e}")
|
391
|
+
raise ValueError(f"Invalid tool arguments format: {arguments}")
|
392
|
+
|
393
|
+
# Call the tool
|
394
|
+
logger.info(f"Calling tool: {tool.name} with args: {self._truncate_dict(arguments)}")
|
395
|
+
try:
|
396
|
+
if inspect.iscoroutinefunction(tool.function):
|
397
|
+
result = await asyncio.wait_for(tool.function(**arguments), timeout=300.0) # 5 minutes timeout
|
398
|
+
else:
|
399
|
+
result = tool.function(**arguments)
|
400
|
+
logger.info(f"Tool {tool.name} completed successfully")
|
401
|
+
except asyncio.TimeoutError:
|
402
|
+
logger.error(f"Tool {tool.name} timed out after 5 minutes")
|
403
|
+
result = {"error": "Tool execution timed out after 5 minutes"}
|
404
|
+
except Exception as e:
|
405
|
+
logger.error(f"Error calling tool {tool.name}: {str(e)}")
|
406
|
+
result = {"error": f"Tool execution failed: {str(e)}"}
|
407
|
+
|
408
|
+
# Convert result to JSON string if it's not already
|
409
|
+
if not isinstance(result, str):
|
410
|
+
result = json.dumps(result)
|
411
|
+
|
412
|
+
# Create the tool response
|
413
|
+
tool_response = {
|
414
|
+
"role": "tool",
|
415
|
+
"tool_call_id": tool_call.id,
|
416
|
+
"content": result,
|
417
|
+
}
|
418
|
+
tool_responses.append(tool_response)
|
419
|
+
logger.info(f"Created tool response for call ID {tool_call.id}")
|
420
|
+
|
421
|
+
# Add all tool responses to the messages
|
422
|
+
messages.extend(tool_responses)
|
423
|
+
logger.info(f"Message count: {len(messages)}, Added {len(tool_responses)} tool responses to messages")
|
424
|
+
|
425
|
+
# Validate the message sequence
|
426
|
+
self._validate_message_sequence(messages, validate_responses=True)
|
427
|
+
|
428
|
+
# Log the messages we're about to send
|
429
|
+
self._log_messages(messages, validate_responses=False)
|
430
|
+
|
431
|
+
# Continue the loop to get the next response
|
432
|
+
continue
|
@@ -5,14 +5,14 @@ mbxai/mcp/client.py,sha256=B8ZpH-uecmTCgoDw65LwwVxsFWVoX-08t5ff0hOEPXk,6011
|
|
5
5
|
mbxai/mcp/example.py,sha256=oaol7AvvZnX86JWNz64KvPjab5gg1VjVN3G8eFSzuaE,2350
|
6
6
|
mbxai/mcp/server.py,sha256=T0-Y7FeHRFqSTp2ERU96fOQlQJKjMFxg8oqC4dzBmBA,3463
|
7
7
|
mbxai/openrouter/__init__.py,sha256=Ito9Qp_B6q-RLGAQcYyTJVWwR2YAZvNqE-HIYXxhtD8,298
|
8
|
-
mbxai/openrouter/client.py,sha256=
|
8
|
+
mbxai/openrouter/client.py,sha256=zcvs0LGFQy1WuPaW1aGi7MM5Ho-5yXrjhrgsfyZrO0A,13641
|
9
9
|
mbxai/openrouter/config.py,sha256=Ia93s-auim9Sq71eunVDbn9ET5xX2zusXpV4JBdHAzs,3251
|
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=MnBUjwb20oSSBrdj6pe6X_z6lKLXhSNyFTxP67mbBas,17801
|
13
13
|
mbxai/tools/example.py,sha256=1HgKK39zzUuwFbnp3f0ThyWVfA_8P28PZcTwaUw5K78,2232
|
14
14
|
mbxai/tools/types.py,sha256=fo5t9UbsHGynhA88vD_ecgDqL8iLvt2E1h1ym43Rrgk,745
|
15
|
-
mbxai-0.6.
|
16
|
-
mbxai-0.6.
|
17
|
-
mbxai-0.6.
|
18
|
-
mbxai-0.6.
|
15
|
+
mbxai-0.6.13.dist-info/METADATA,sha256=2f-u1N_77PMxGlWn39Gkyu9Y5tZ7yLH_vWtl_lwR9WI,4108
|
16
|
+
mbxai-0.6.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
17
|
+
mbxai-0.6.13.dist-info/licenses/LICENSE,sha256=hEyhc4FxwYo3NQ40yNgZ7STqwVk-1_XcTXOnAPbGJAw,1069
|
18
|
+
mbxai-0.6.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|