mbxai 0.6.21__tar.gz → 0.6.23__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.
Files changed (26) hide show
  1. {mbxai-0.6.21 → mbxai-0.6.23}/PKG-INFO +1 -1
  2. {mbxai-0.6.21 → mbxai-0.6.23}/pyproject.toml +1 -1
  3. {mbxai-0.6.21 → mbxai-0.6.23}/setup.py +1 -1
  4. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/__init__.py +1 -1
  5. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/mcp/client.py +8 -2
  6. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/mcp/server.py +1 -1
  7. mbxai-0.6.23/src/mbxai/tools/types.py +166 -0
  8. {mbxai-0.6.21 → mbxai-0.6.23}/uv.lock +1 -1
  9. mbxai-0.6.21/src/mbxai/tools/types.py +0 -120
  10. {mbxai-0.6.21 → mbxai-0.6.23}/.gitignore +0 -0
  11. {mbxai-0.6.21 → mbxai-0.6.23}/LICENSE +0 -0
  12. {mbxai-0.6.21 → mbxai-0.6.23}/README.md +0 -0
  13. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/core.py +0 -0
  14. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/mcp/__init__.py +0 -0
  15. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/mcp/example.py +0 -0
  16. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/openrouter/__init__.py +0 -0
  17. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/openrouter/client.py +0 -0
  18. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/openrouter/config.py +0 -0
  19. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/openrouter/models.py +0 -0
  20. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/tools/__init__.py +0 -0
  21. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/tools/client.py +0 -0
  22. {mbxai-0.6.21 → mbxai-0.6.23}/src/mbxai/tools/example.py +0 -0
  23. {mbxai-0.6.21 → mbxai-0.6.23}/tests/test_core.py +0 -0
  24. {mbxai-0.6.21 → mbxai-0.6.23}/tests/test_mcp.py +0 -0
  25. {mbxai-0.6.21 → mbxai-0.6.23}/tests/test_openrouter.py +0 -0
  26. {mbxai-0.6.21 → mbxai-0.6.23}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mbxai
3
- Version: 0.6.21
3
+ Version: 0.6.23
4
4
  Summary: MBX AI SDK
5
5
  Project-URL: Homepage, https://www.mibexx.de
6
6
  Project-URL: Documentation, https://www.mibexx.de
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mbxai"
7
- version = "0.6.21"
7
+ version = "0.6.23"
8
8
  authors = [
9
9
  { name = "MBX AI" }
10
10
  ]
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="mbxai",
5
- version="0.6.21",
5
+ version="0.6.23",
6
6
  author="MBX AI",
7
7
  description="MBX AI SDK",
8
8
  long_description=open("README.md").read(),
@@ -2,4 +2,4 @@
2
2
  MBX AI package.
3
3
  """
4
4
 
5
- __version__ = "0.6.21"
5
+ __version__ = "0.6.23"
@@ -24,8 +24,8 @@ class MCPTool(Tool):
24
24
 
25
25
  def to_openai_function(self) -> dict[str, Any]:
26
26
  """Convert the tool to an OpenAI function definition."""
27
- # Use the base Tool's schema conversion
28
- strict_schema = convert_to_strict_schema(self.input_schema, strict=self.strict)
27
+ # Use the unified schema conversion with keep_input_wrapper=True for MCP tools
28
+ strict_schema = convert_to_strict_schema(self.input_schema, strict=self.strict, keep_input_wrapper=True)
29
29
  logger.info(f"Converted schema for {self.name}: {json.dumps(strict_schema, indent=2)}")
30
30
 
31
31
  return {
@@ -56,11 +56,13 @@ class MCPTool(Tool):
56
56
  # Handle input wrapper
57
57
  if "properties" in mcp_schema and "input" in mcp_schema["properties"]:
58
58
  input_schema = mcp_schema["properties"]["input"]
59
+ logger.info(f"Found input wrapper. Input schema: {json.dumps(input_schema, indent=2)}")
59
60
 
60
61
  # If input has a $ref, resolve it
61
62
  if "$ref" in input_schema:
62
63
  ref = input_schema["$ref"].split("/")[-1]
63
64
  input_schema = mcp_schema.get("$defs", {}).get(ref, {})
65
+ logger.info(f"Resolved $ref to: {json.dumps(input_schema, indent=2)}")
64
66
 
65
67
  # Create the input property schema
66
68
  input_prop_schema = {
@@ -88,10 +90,12 @@ class MCPTool(Tool):
88
90
  new_prop[key] = value
89
91
 
90
92
  input_prop_schema["properties"][prop_name] = new_prop
93
+ logger.info(f"Added property {prop_name}: {json.dumps(new_prop, indent=2)}")
91
94
 
92
95
  # Copy over required fields for input schema
93
96
  if "required" in input_schema:
94
97
  input_prop_schema["required"] = input_schema["required"]
98
+ logger.info(f"Added required fields for input schema: {input_prop_schema['required']}")
95
99
 
96
100
  # Add the input property to the main schema
97
101
  strict_schema["properties"]["input"] = input_prop_schema
@@ -99,7 +103,9 @@ class MCPTool(Tool):
99
103
  # Copy over required fields for main schema
100
104
  if "required" in mcp_schema:
101
105
  strict_schema["required"] = mcp_schema["required"]
106
+ logger.info(f"Added required fields for main schema: {strict_schema['required']}")
102
107
 
108
+ logger.info(f"Final strict schema: {json.dumps(strict_schema, indent=2)}")
103
109
  return strict_schema
104
110
 
105
111
 
@@ -31,7 +31,7 @@ class MCPServer:
31
31
  self.app = FastAPI(
32
32
  title=self.name,
33
33
  description=self.description,
34
- version="0.6.21",
34
+ version="0.6.23",
35
35
  )
36
36
 
37
37
  # Initialize MCP server
@@ -0,0 +1,166 @@
1
+ """
2
+ Type definitions for the tools package.
3
+ """
4
+
5
+ from typing import Any, Callable
6
+ from pydantic import BaseModel, Field
7
+ import logging
8
+ import json
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ def convert_to_strict_schema(schema: dict[str, Any], strict: bool = True, keep_input_wrapper: bool = False) -> dict[str, Any]:
13
+ """Convert a schema to strict format required by OpenAI.
14
+
15
+ Args:
16
+ schema: The input schema to validate and convert
17
+ strict: Whether to enforce strict validation with additionalProperties: false
18
+ keep_input_wrapper: Whether to keep the input wrapper (for MCP tools)
19
+
20
+ Returns:
21
+ A schema in strict format
22
+ """
23
+ logger.info(f"Converting schema to strict format. Input schema: {json.dumps(schema, indent=2)}")
24
+ logger.info(f"Strict mode: {strict}, Keep input wrapper: {keep_input_wrapper}")
25
+
26
+ if not schema:
27
+ return {"type": "object", "properties": {}, "required": []}
28
+
29
+ # Create a new schema object to ensure we have all required fields
30
+ strict_schema = {
31
+ "type": "object",
32
+ "properties": {},
33
+ "required": []
34
+ }
35
+
36
+ # Add additionalProperties: false for strict validation
37
+ if strict:
38
+ strict_schema["additionalProperties"] = False
39
+
40
+ # Handle input wrapper
41
+ if "properties" in schema and "input" in schema["properties"]:
42
+ input_schema = schema["properties"]["input"]
43
+ logger.info(f"Found input wrapper. Input schema: {json.dumps(input_schema, indent=2)}")
44
+
45
+ # If input has a $ref, resolve it
46
+ if "$ref" in input_schema:
47
+ ref = input_schema["$ref"].split("/")[-1]
48
+ input_schema = schema.get("$defs", {}).get(ref, {})
49
+ logger.info(f"Resolved $ref to: {json.dumps(input_schema, indent=2)}")
50
+
51
+ if keep_input_wrapper:
52
+ # Create the input property schema
53
+ input_prop_schema = {
54
+ "type": "object",
55
+ "properties": {},
56
+ "required": []
57
+ }
58
+
59
+ # Add additionalProperties: false for input schema
60
+ if strict:
61
+ input_prop_schema["additionalProperties"] = False
62
+
63
+ # Copy over input properties
64
+ if "properties" in input_schema:
65
+ for prop_name, prop in input_schema["properties"].items():
66
+ # Create a new property object with required fields
67
+ new_prop = {
68
+ "type": prop.get("type", "string"),
69
+ "description": prop.get("description", f"The {prop_name} parameter")
70
+ }
71
+
72
+ # Copy over any additional fields that might be useful
73
+ for key, value in prop.items():
74
+ if key not in new_prop:
75
+ new_prop[key] = value
76
+
77
+ input_prop_schema["properties"][prop_name] = new_prop
78
+ logger.info(f"Added property {prop_name}: {json.dumps(new_prop, indent=2)}")
79
+
80
+ # Copy over required fields for input schema
81
+ if "required" in input_schema:
82
+ input_prop_schema["required"] = input_schema["required"]
83
+ logger.info(f"Added required fields for input schema: {input_prop_schema['required']}")
84
+
85
+ # Add the input property to the main schema
86
+ strict_schema["properties"]["input"] = input_prop_schema
87
+
88
+ # Copy over required fields for main schema
89
+ if "required" in schema:
90
+ strict_schema["required"] = schema["required"]
91
+ logger.info(f"Added required fields for main schema: {strict_schema['required']}")
92
+ else:
93
+ # If not keeping input wrapper, use input schema directly
94
+ if "properties" in input_schema:
95
+ for prop_name, prop in input_schema["properties"].items():
96
+ # Create a new property object with required fields
97
+ new_prop = {
98
+ "type": prop.get("type", "string"),
99
+ "description": prop.get("description", f"The {prop_name} parameter")
100
+ }
101
+
102
+ # Copy over any additional fields that might be useful
103
+ for key, value in prop.items():
104
+ if key not in new_prop:
105
+ new_prop[key] = value
106
+
107
+ strict_schema["properties"][prop_name] = new_prop
108
+ logger.info(f"Added property {prop_name}: {json.dumps(new_prop, indent=2)}")
109
+
110
+ # Copy over required fields
111
+ if "required" in input_schema:
112
+ strict_schema["required"] = input_schema["required"]
113
+ logger.info(f"Added required fields: {strict_schema['required']}")
114
+ else:
115
+ # If no input wrapper, use the schema as is
116
+ if "properties" in schema:
117
+ for prop_name, prop in schema["properties"].items():
118
+ # Create a new property object with required fields
119
+ new_prop = {
120
+ "type": prop.get("type", "string"),
121
+ "description": prop.get("description", f"The {prop_name} parameter")
122
+ }
123
+
124
+ # Copy over any additional fields that might be useful
125
+ for key, value in prop.items():
126
+ if key not in new_prop:
127
+ new_prop[key] = value
128
+
129
+ strict_schema["properties"][prop_name] = new_prop
130
+ logger.info(f"Added property {prop_name}: {json.dumps(new_prop, indent=2)}")
131
+
132
+ # Copy over required fields
133
+ if "required" in schema:
134
+ strict_schema["required"] = schema["required"]
135
+ logger.info(f"Added required fields: {strict_schema['required']}")
136
+
137
+ logger.info(f"Final strict schema: {json.dumps(strict_schema, indent=2)}")
138
+ return strict_schema
139
+
140
+ class ToolCall(BaseModel):
141
+ """A tool call from the model."""
142
+ id: str
143
+ name: str
144
+ arguments: dict[str, Any]
145
+
146
+ class Tool(BaseModel):
147
+ """A tool that can be used by the model."""
148
+ name: str
149
+ description: str
150
+ function: Callable[..., Any] | None = None # Make function optional
151
+ schema: dict[str, Any]
152
+
153
+ def to_openai_function(self) -> dict[str, Any]:
154
+ """Convert the tool to an OpenAI function definition."""
155
+ # Ensure schema is in strict format
156
+ strict_schema = convert_to_strict_schema(self.schema)
157
+ logger.info(f"Converted schema for {self.name}: {json.dumps(strict_schema, indent=2)}")
158
+
159
+ return {
160
+ "type": "function",
161
+ "function": {
162
+ "name": self.name,
163
+ "description": self.description,
164
+ "parameters": strict_schema
165
+ }
166
+ }
@@ -446,7 +446,7 @@ wheels = [
446
446
 
447
447
  [[package]]
448
448
  name = "mbxai"
449
- version = "0.6.21"
449
+ version = "0.6.23"
450
450
  source = { editable = "." }
451
451
  dependencies = [
452
452
  { name = "fastapi" },
@@ -1,120 +0,0 @@
1
- """
2
- Type definitions for the tools package.
3
- """
4
-
5
- from typing import Any, Callable
6
- from pydantic import BaseModel, Field
7
- import logging
8
- import json
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
- def convert_to_strict_schema(schema: dict[str, Any], strict: bool = True) -> dict[str, Any]:
13
- """Convert a schema to strict format required by OpenAI.
14
-
15
- Args:
16
- schema: The input schema to validate and convert
17
- strict: Whether to enforce strict validation with additionalProperties: false
18
-
19
- Returns:
20
- A schema in strict format
21
- """
22
- logger.info(f"Converting schema to strict format. Input schema: {json.dumps(schema, indent=2)}")
23
- logger.info(f"Strict mode: {strict}")
24
-
25
- if not schema:
26
- return {"type": "object", "properties": {}, "required": []}
27
-
28
- # Create a new schema object to ensure we have all required fields
29
- strict_schema = {
30
- "type": "object",
31
- "properties": {},
32
- "required": []
33
- }
34
-
35
- # Add additionalProperties: false for strict validation
36
- if strict:
37
- strict_schema["additionalProperties"] = False
38
-
39
- # Handle input wrapper
40
- if "properties" in schema and "input" in schema["properties"]:
41
- input_schema = schema["properties"]["input"]
42
- logger.info(f"Found input wrapper. Input schema: {json.dumps(input_schema, indent=2)}")
43
-
44
- # If input has a $ref, resolve it
45
- if "$ref" in input_schema:
46
- ref = input_schema["$ref"].split("/")[-1]
47
- input_schema = schema.get("$defs", {}).get(ref, {})
48
- logger.info(f"Resolved $ref to: {json.dumps(input_schema, indent=2)}")
49
-
50
- # Create the input property schema
51
- input_prop_schema = {
52
- "type": "object",
53
- "properties": {},
54
- "required": []
55
- }
56
-
57
- # Add additionalProperties: false for input schema
58
- if strict:
59
- input_prop_schema["additionalProperties"] = False
60
-
61
- # Copy over input properties
62
- if "properties" in input_schema:
63
- for prop_name, prop in input_schema["properties"].items():
64
- # Create a new property object with required fields
65
- new_prop = {
66
- "type": prop.get("type", "string"),
67
- "description": prop.get("description", f"The {prop_name} parameter")
68
- }
69
-
70
- # Copy over any additional fields that might be useful
71
- for key, value in prop.items():
72
- if key not in new_prop:
73
- new_prop[key] = value
74
-
75
- input_prop_schema["properties"][prop_name] = new_prop
76
- logger.info(f"Added property {prop_name}: {json.dumps(new_prop, indent=2)}")
77
-
78
- # Copy over required fields for input schema
79
- if "required" in input_schema:
80
- input_prop_schema["required"] = input_schema["required"]
81
- logger.info(f"Added required fields for input schema: {input_prop_schema['required']}")
82
-
83
- # Add the input property to the main schema
84
- strict_schema["properties"]["input"] = input_prop_schema
85
-
86
- # Copy over required fields for main schema
87
- if "required" in schema:
88
- strict_schema["required"] = schema["required"]
89
- logger.info(f"Added required fields for main schema: {strict_schema['required']}")
90
-
91
- logger.info(f"Final strict schema: {json.dumps(strict_schema, indent=2)}")
92
- return strict_schema
93
-
94
- class ToolCall(BaseModel):
95
- """A tool call from the model."""
96
- id: str
97
- name: str
98
- arguments: dict[str, Any]
99
-
100
- class Tool(BaseModel):
101
- """A tool that can be used by the model."""
102
- name: str
103
- description: str
104
- function: Callable[..., Any] | None = None # Make function optional
105
- schema: dict[str, Any]
106
-
107
- def to_openai_function(self) -> dict[str, Any]:
108
- """Convert the tool to an OpenAI function definition."""
109
- # Ensure schema is in strict format
110
- strict_schema = convert_to_strict_schema(self.schema)
111
- logger.info(f"Converted schema for {self.name}: {json.dumps(strict_schema, indent=2)}")
112
-
113
- return {
114
- "type": "function",
115
- "function": {
116
- "name": self.name,
117
- "description": self.description,
118
- "parameters": strict_schema
119
- }
120
- }
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