apple-foundation-models 0.1.9__cp310-cp310-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.
- apple_foundation_models-0.1.9.dist-info/METADATA +607 -0
- apple_foundation_models-0.1.9.dist-info/RECORD +18 -0
- apple_foundation_models-0.1.9.dist-info/WHEEL +5 -0
- apple_foundation_models-0.1.9.dist-info/licenses/LICENSE +21 -0
- apple_foundation_models-0.1.9.dist-info/top_level.txt +1 -0
- applefoundationmodels/__init__.py +108 -0
- applefoundationmodels/_foundationmodels.cpython-310-darwin.so +0 -0
- applefoundationmodels/_foundationmodels.pyi +52 -0
- applefoundationmodels/base.py +36 -0
- applefoundationmodels/client.py +231 -0
- applefoundationmodels/constants.py +30 -0
- applefoundationmodels/exceptions.py +134 -0
- applefoundationmodels/libfoundation_models.dylib +0 -0
- applefoundationmodels/py.typed +2 -0
- applefoundationmodels/pydantic_compat.py +144 -0
- applefoundationmodels/session.py +448 -0
- applefoundationmodels/tools.py +304 -0
- applefoundationmodels/types.py +171 -0
|
@@ -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
|
+
"""
|