mbxai 0.6.11__tar.gz → 0.6.13__tar.gz
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-0.6.11 → mbxai-0.6.13}/PKG-INFO +1 -1
- {mbxai-0.6.11 → mbxai-0.6.13}/pyproject.toml +1 -1
- {mbxai-0.6.11 → mbxai-0.6.13}/setup.py +1 -1
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/openrouter/client.py +29 -31
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/tools/client.py +16 -9
- {mbxai-0.6.11 → mbxai-0.6.13}/uv.lock +7 -7
- {mbxai-0.6.11 → mbxai-0.6.13}/.gitignore +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/LICENSE +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/README.md +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/__init__.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/core.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/mcp/__init__.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/mcp/client.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/mcp/example.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/mcp/server.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/openrouter/__init__.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/openrouter/config.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/openrouter/models.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/tools/__init__.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/tools/example.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/src/mbxai/tools/types.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/tests/test_core.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/tests/test_mcp.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/tests/test_openrouter.py +0 -0
- {mbxai-0.6.11 → mbxai-0.6.13}/tests/test_tools.py +0 -0
@@ -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(
|
@@ -327,6 +327,7 @@ class ToolClient:
|
|
327
327
|
Returns:
|
328
328
|
The parsed response from the model
|
329
329
|
"""
|
330
|
+
# First, use chat to handle any tool calls
|
330
331
|
tools = [tool.to_openai_function() for tool in self._tools.values()]
|
331
332
|
|
332
333
|
if tools:
|
@@ -335,9 +336,6 @@ class ToolClient:
|
|
335
336
|
kwargs["tool_choice"] = "auto"
|
336
337
|
|
337
338
|
while True:
|
338
|
-
# Log messages before sending to OpenRouter
|
339
|
-
self._log_messages(messages)
|
340
|
-
|
341
339
|
# Get the model's response
|
342
340
|
response = self._client.chat_completion_parse(
|
343
341
|
messages=messages,
|
@@ -354,7 +352,8 @@ class ToolClient:
|
|
354
352
|
# Add the assistant's message with tool calls
|
355
353
|
assistant_message = {
|
356
354
|
"role": "assistant",
|
357
|
-
"content": message.content or None,
|
355
|
+
"content": message.content or None,
|
356
|
+
"parsed": message.parsed or None,
|
358
357
|
}
|
359
358
|
if message.tool_calls:
|
360
359
|
assistant_message["tool_calls"] = [
|
@@ -393,10 +392,18 @@ class ToolClient:
|
|
393
392
|
|
394
393
|
# Call the tool
|
395
394
|
logger.info(f"Calling tool: {tool.name} with args: {self._truncate_dict(arguments)}")
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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)}"}
|
400
407
|
|
401
408
|
# Convert result to JSON string if it's not already
|
402
409
|
if not isinstance(result, str):
|
@@ -422,4 +429,4 @@ class ToolClient:
|
|
422
429
|
self._log_messages(messages, validate_responses=False)
|
423
430
|
|
424
431
|
# Continue the loop to get the next response
|
425
|
-
continue
|
432
|
+
continue
|
@@ -292,11 +292,11 @@ wheels = [
|
|
292
292
|
|
293
293
|
[[package]]
|
294
294
|
name = "httpx-sse"
|
295
|
-
version = "0.6.
|
295
|
+
version = "0.6.13"
|
296
296
|
source = { registry = "https://pypi.org/simple" }
|
297
|
-
sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.6.
|
297
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.6.13.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
|
298
298
|
wheels = [
|
299
|
-
{ url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.6.
|
299
|
+
{ url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.6.13-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
|
300
300
|
]
|
301
301
|
|
302
302
|
[[package]]
|
@@ -446,7 +446,7 @@ wheels = [
|
|
446
446
|
|
447
447
|
[[package]]
|
448
448
|
name = "mbxai"
|
449
|
-
version = "0.6.
|
449
|
+
version = "0.6.13"
|
450
450
|
source = { editable = "." }
|
451
451
|
dependencies = [
|
452
452
|
{ name = "fastapi" },
|
@@ -980,14 +980,14 @@ wheels = [
|
|
980
980
|
|
981
981
|
[[package]]
|
982
982
|
name = "typing-inspection"
|
983
|
-
version = "0.6.
|
983
|
+
version = "0.6.13"
|
984
984
|
source = { registry = "https://pypi.org/simple" }
|
985
985
|
dependencies = [
|
986
986
|
{ name = "typing-extensions" },
|
987
987
|
]
|
988
|
-
sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.6.
|
988
|
+
sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.6.13.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
|
989
989
|
wheels = [
|
990
|
-
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.6.
|
990
|
+
{ url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.6.13-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
|
991
991
|
]
|
992
992
|
|
993
993
|
[[package]]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|