apple-foundation-models 0.1.9__cp311-cp311-macosx_11_0_arm64.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.

Potentially problematic release.


This version of apple-foundation-models might be problematic. Click here for more details.

@@ -0,0 +1,304 @@
1
+ """
2
+ Tool calling utilities for applefoundationmodels.
3
+
4
+ Provides utilities for extracting JSON schemas from Python functions
5
+ and managing tool registrations.
6
+ """
7
+
8
+ import inspect
9
+ from typing import (
10
+ Callable,
11
+ Dict,
12
+ Any,
13
+ Optional,
14
+ get_type_hints,
15
+ get_origin,
16
+ get_args,
17
+ Union,
18
+ )
19
+ from .exceptions import ToolCallError
20
+
21
+
22
+ # Type mapping tables for efficient schema generation
23
+ _BASIC_TYPE_MAP = {
24
+ str: "string",
25
+ int: "integer",
26
+ float: "number",
27
+ bool: "boolean",
28
+ }
29
+
30
+ _STRING_TYPE_MAP = {
31
+ "str": "string",
32
+ "int": "integer",
33
+ "float": "number",
34
+ "bool": "boolean",
35
+ }
36
+
37
+
38
+ def is_optional_type(python_type: Any) -> bool:
39
+ """
40
+ Check if a type is Optional (Union[X, None]).
41
+
42
+ Args:
43
+ python_type: Python type hint
44
+
45
+ Returns:
46
+ True if the type is Optional, False otherwise
47
+ """
48
+ origin = get_origin(python_type)
49
+ if origin is Union:
50
+ args = get_args(python_type)
51
+ return type(None) in args
52
+ return False
53
+
54
+
55
+ def unwrap_optional(python_type: Any) -> Any:
56
+ """
57
+ Extract the non-None type from Optional[X] or Union[X, None].
58
+
59
+ Args:
60
+ python_type: Python type hint that may be Optional
61
+
62
+ Returns:
63
+ The unwrapped type (X from Optional[X])
64
+ """
65
+ origin = get_origin(python_type)
66
+ if origin is Union:
67
+ args = get_args(python_type)
68
+ # Filter out None and return the first non-None type
69
+ non_none_types = [arg for arg in args if arg is not type(None)]
70
+ if non_none_types:
71
+ return non_none_types[0]
72
+ return python_type
73
+
74
+
75
+ def _handle_list_type(python_type: Any) -> Dict[str, Any]:
76
+ """
77
+ Extract list/array type with optional item schema.
78
+
79
+ Args:
80
+ python_type: List or generic list type hint
81
+
82
+ Returns:
83
+ JSON Schema for array type
84
+ """
85
+ args = get_args(python_type)
86
+ if args:
87
+ items_schema = python_type_to_json_schema(args[0])
88
+ return {"type": "array", "items": items_schema}
89
+ return {"type": "array"}
90
+
91
+
92
+ def _handle_dict_type(python_type: Any) -> Dict[str, Any]:
93
+ """
94
+ Extract dict/object type with optional value schema.
95
+
96
+ Args:
97
+ python_type: Dict or generic dict type hint
98
+
99
+ Returns:
100
+ JSON Schema for object type
101
+ """
102
+ args = get_args(python_type)
103
+ if len(args) == 2 and args[0] is str:
104
+ value_schema = python_type_to_json_schema(args[1])
105
+ return {"type": "object", "additionalProperties": value_schema}
106
+ return {"type": "object"}
107
+
108
+
109
+ def python_type_to_json_schema(python_type: Any) -> Dict[str, Any]:
110
+ """
111
+ Convert a Python type hint to a JSON Schema type definition.
112
+
113
+ Args:
114
+ python_type: Python type hint
115
+
116
+ Returns:
117
+ JSON Schema type definition
118
+
119
+ Raises:
120
+ ToolCallError: If type cannot be converted
121
+ """
122
+ # Handle None type
123
+ if python_type is type(None):
124
+ return {"type": "null"}
125
+
126
+ # Handle Optional[X] / Union[X, None] - unwrap and process the inner type
127
+ if is_optional_type(python_type):
128
+ inner_type = unwrap_optional(python_type)
129
+ return python_type_to_json_schema(inner_type)
130
+
131
+ # Check basic types via lookup table
132
+ if python_type in _BASIC_TYPE_MAP:
133
+ return {"type": _BASIC_TYPE_MAP[python_type]}
134
+
135
+ # Check string type annotations
136
+ if isinstance(python_type, str) and python_type in _STRING_TYPE_MAP:
137
+ return {"type": _STRING_TYPE_MAP[python_type]}
138
+
139
+ # Get origin for generic types
140
+ origin = get_origin(python_type)
141
+
142
+ # Handle container types
143
+ if python_type is list or origin is list:
144
+ return _handle_list_type(python_type)
145
+
146
+ if python_type is dict or origin is dict:
147
+ return _handle_dict_type(python_type)
148
+
149
+ # Default fallback for unknown types
150
+ return {"type": "string"}
151
+
152
+
153
+ def extract_function_schema(func: Callable) -> Dict[str, Any]:
154
+ """
155
+ Extract JSON Schema from a Python function's signature and docstring.
156
+
157
+ Args:
158
+ func: Python function to extract schema from
159
+
160
+ Returns:
161
+ Dictionary containing:
162
+ - name: Function name
163
+ - description: Function description from docstring
164
+ - parameters: JSON Schema for function parameters
165
+
166
+ Raises:
167
+ ToolCallError: If schema cannot be extracted
168
+ """
169
+ try:
170
+ # Get function signature
171
+ sig = inspect.signature(func)
172
+
173
+ # Get type hints
174
+ try:
175
+ type_hints = get_type_hints(func)
176
+ except Exception:
177
+ # If type hints fail (e.g., forward references), inspect parameters directly
178
+ type_hints = {}
179
+
180
+ # Extract parameter schemas
181
+ properties = {}
182
+ required = []
183
+
184
+ for param_name, param in sig.parameters.items():
185
+ # Skip self and cls parameters
186
+ if param_name in ("self", "cls"):
187
+ continue
188
+
189
+ # Get type annotation
190
+ param_type = type_hints.get(param_name, param.annotation)
191
+
192
+ # Handle parameters without type hints
193
+ if param_type is inspect.Parameter.empty:
194
+ # Default to string type if no annotation
195
+ param_schema = {"type": "string"}
196
+ else:
197
+ param_schema = python_type_to_json_schema(param_type)
198
+
199
+ properties[param_name] = param_schema
200
+
201
+ # Mark as required if no default value
202
+ if param.default is inspect.Parameter.empty:
203
+ required.append(param_name)
204
+
205
+ # Build parameters schema
206
+ parameters_schema = {
207
+ "type": "object",
208
+ "properties": properties,
209
+ }
210
+
211
+ if required:
212
+ parameters_schema["required"] = required
213
+
214
+ # Extract description from docstring
215
+ description = ""
216
+ if func.__doc__:
217
+ # Get the first line or paragraph of the docstring
218
+ lines = func.__doc__.strip().split("\n")
219
+ description = lines[0].strip()
220
+
221
+ # Get function name
222
+ name = func.__name__
223
+
224
+ return {
225
+ "name": name,
226
+ "description": description,
227
+ "parameters": parameters_schema,
228
+ }
229
+
230
+ except Exception as e:
231
+ raise ToolCallError(
232
+ f"Failed to extract schema from function '{func.__name__}': {e}"
233
+ ) from e
234
+
235
+
236
+ def attach_tool_metadata(
237
+ func: Callable,
238
+ schema: Dict[str, Any],
239
+ description: Optional[str] = None,
240
+ name: Optional[str] = None,
241
+ ) -> Dict[str, Any]:
242
+ """
243
+ Attach tool metadata to a function and return final schema.
244
+
245
+ This is a shared helper used by both the standalone @tool decorator
246
+ and the Session.tool() method to avoid code duplication.
247
+
248
+ Args:
249
+ func: Function to attach metadata to
250
+ schema: Base schema from extract_function_schema
251
+ description: Optional override for description
252
+ name: Optional override for name
253
+
254
+ Returns:
255
+ Final schema with overrides applied
256
+ """
257
+ # Override with provided values
258
+ if description is not None:
259
+ schema["description"] = description
260
+ if name is not None:
261
+ schema["name"] = name
262
+
263
+ # Attach metadata to function
264
+ func._tool_name = schema["name"] # type: ignore[attr-defined]
265
+ func._tool_description = schema["description"] # type: ignore[attr-defined]
266
+ func._tool_parameters = schema["parameters"] # type: ignore[attr-defined]
267
+
268
+ return schema
269
+
270
+
271
+ def tool(
272
+ description: Optional[str] = None,
273
+ name: Optional[str] = None,
274
+ ) -> Callable:
275
+ """
276
+ Decorator to mark a function as a tool and attach metadata.
277
+
278
+ Args:
279
+ description: Optional tool description (uses docstring if not provided)
280
+ name: Optional tool name (uses function name if not provided)
281
+
282
+ Returns:
283
+ Decorated function with tool metadata attached
284
+
285
+ Note:
286
+ Tool output size limits:
287
+ - Initial buffer: 16KB
288
+ - Maximum size: 1MB (automatically retried with larger buffers)
289
+ - Tools returning outputs larger than 1MB will raise an error
290
+ - For large outputs, consider returning references or summaries
291
+
292
+ Example:
293
+ @tool(description="Get the current weather")
294
+ def get_weather(location: str) -> str:
295
+ return f"Weather in {location}: sunny"
296
+ """
297
+
298
+ def decorator(func: Callable) -> Callable:
299
+ # Extract schema and attach metadata
300
+ schema = extract_function_schema(func)
301
+ attach_tool_metadata(func, schema, description, name)
302
+ return func
303
+
304
+ return decorator
@@ -0,0 +1,171 @@
1
+ """
2
+ Type definitions for libai Python bindings.
3
+
4
+ This module provides TypedDicts, enums, and type aliases for type-safe
5
+ interaction with the library.
6
+ """
7
+
8
+ from dataclasses import dataclass
9
+ from enum import IntEnum
10
+ from typing import TypedDict, Optional, Callable, Any
11
+ from typing_extensions import NotRequired
12
+
13
+
14
+ class Result(IntEnum):
15
+ """
16
+ Result codes for AI operations.
17
+
18
+ These codes indicate the success or failure state of library operations.
19
+ """
20
+
21
+ SUCCESS = 0
22
+ INIT_FAILED = -1
23
+ NOT_AVAILABLE = -2
24
+ INVALID_PARAMS = -3
25
+ MEMORY = -4
26
+ JSON_PARSE = -5
27
+ GENERATION = -6
28
+ TIMEOUT = -7
29
+ SESSION_NOT_FOUND = -8
30
+ STREAM_NOT_FOUND = -9
31
+ GUARDRAIL_VIOLATION = -10
32
+ TOOL_NOT_FOUND = -11
33
+ TOOL_EXECUTION = -12
34
+ BUFFER_TOO_SMALL = -13
35
+ UNKNOWN = -99
36
+
37
+
38
+ class Availability(IntEnum):
39
+ """
40
+ Apple Intelligence availability status.
41
+
42
+ Indicates whether Apple Intelligence is available and ready for use
43
+ on the current device and system configuration.
44
+ """
45
+
46
+ AVAILABLE = 1
47
+ DEVICE_NOT_ELIGIBLE = -1
48
+ NOT_ENABLED = -2
49
+ MODEL_NOT_READY = -3
50
+ AVAILABILITY_UNKNOWN = -99
51
+
52
+
53
+ class SessionConfig(TypedDict, total=False):
54
+ """
55
+ Session configuration options.
56
+
57
+ Configuration for creating an AI session. Sessions maintain conversation
58
+ state and can be configured with tools and instructions.
59
+
60
+ Attributes:
61
+ instructions: Optional system instructions to guide AI behavior
62
+ tools_json: Optional JSON array of tool definitions in Claude format
63
+ enable_guardrails: Whether to enable content safety filtering (default: True)
64
+ prewarm: Whether to preload session resources for faster first response (default: False)
65
+ """
66
+
67
+ instructions: NotRequired[Optional[str]]
68
+ tools_json: NotRequired[Optional[str]]
69
+ enable_guardrails: NotRequired[bool]
70
+ prewarm: NotRequired[bool]
71
+
72
+
73
+ class GenerationParams(TypedDict, total=False):
74
+ """
75
+ Text generation parameters.
76
+
77
+ Controls various aspects of AI text generation including randomness,
78
+ length limits, and reproducibility.
79
+
80
+ Attributes:
81
+ temperature: Generation randomness (0.0 = deterministic, 2.0 = very random)
82
+ max_tokens: Maximum response tokens (0 = use system default)
83
+ include_reasoning: Include reasoning in response (reserved for future use)
84
+ seed: Random seed for reproducibility (0 = use random seed)
85
+ """
86
+
87
+ temperature: NotRequired[float]
88
+ max_tokens: NotRequired[int]
89
+ include_reasoning: NotRequired[bool]
90
+ seed: NotRequired[int]
91
+
92
+
93
+ @dataclass
94
+ class NormalizedGenerationParams:
95
+ """
96
+ Normalized generation parameters with defaults applied.
97
+
98
+ This dataclass ensures all generation parameters have concrete values,
99
+ making it easier to pass to FFI functions without None checks.
100
+
101
+ Attributes:
102
+ temperature: Generation randomness (0.0-2.0)
103
+ max_tokens: Maximum response tokens
104
+ """
105
+
106
+ temperature: float
107
+ max_tokens: int
108
+
109
+ @classmethod
110
+ def from_optional(
111
+ cls,
112
+ temperature: Optional[float] = None,
113
+ max_tokens: Optional[int] = None,
114
+ ) -> "NormalizedGenerationParams":
115
+ """
116
+ Create normalized params from optional values.
117
+
118
+ Args:
119
+ temperature: Optional temperature override
120
+ max_tokens: Optional max_tokens override
121
+
122
+ Returns:
123
+ NormalizedGenerationParams with defaults applied
124
+ """
125
+ from .constants import DEFAULT_TEMPERATURE, DEFAULT_MAX_TOKENS
126
+
127
+ return cls(
128
+ temperature=temperature if temperature is not None else DEFAULT_TEMPERATURE,
129
+ max_tokens=max_tokens if max_tokens is not None else DEFAULT_MAX_TOKENS,
130
+ )
131
+
132
+
133
+ class Stats(TypedDict):
134
+ """
135
+ Generation statistics and performance metrics.
136
+
137
+ Provides insights into the usage and performance of generation operations.
138
+
139
+ Attributes:
140
+ total_requests: Total number of generation requests initiated
141
+ successful_requests: Number of requests that completed successfully
142
+ failed_requests: Number of requests that failed or were cancelled
143
+ total_tokens_generated: Total tokens generated across all requests
144
+ average_response_time: Average response time in seconds
145
+ total_processing_time: Total processing time in seconds
146
+ """
147
+
148
+ total_requests: int
149
+ successful_requests: int
150
+ failed_requests: int
151
+ total_tokens_generated: int
152
+ average_response_time: float
153
+ total_processing_time: float
154
+
155
+
156
+ # Callback type aliases
157
+ StreamCallback = Callable[[Optional[str]], None]
158
+ """
159
+ Callback function for streaming text generation.
160
+
161
+ Called incrementally during streaming generation for each token or chunk.
162
+ None indicates completion or error.
163
+ """
164
+
165
+ ToolCallback = Callable[[dict], Any]
166
+ """
167
+ Callback function for tool execution.
168
+
169
+ Receives tool parameters as a dict and should return the tool result.
170
+ The result will be automatically JSON-serialized.
171
+ """