apple-foundation-models 0.2.2__cp312-cp312-macosx_26_0_universal2.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.2.2.dist-info/METADATA +620 -0
- apple_foundation_models-0.2.2.dist-info/RECORD +20 -0
- apple_foundation_models-0.2.2.dist-info/WHEEL +5 -0
- apple_foundation_models-0.2.2.dist-info/licenses/LICENSE +21 -0
- apple_foundation_models-0.2.2.dist-info/top_level.txt +1 -0
- applefoundationmodels/__init__.py +124 -0
- applefoundationmodels/_foundationmodels.cpython-312-darwin.so +0 -0
- applefoundationmodels/_foundationmodels.pyi +43 -0
- applefoundationmodels/async_session.py +296 -0
- applefoundationmodels/base.py +65 -0
- applefoundationmodels/base_session.py +659 -0
- applefoundationmodels/constants.py +30 -0
- applefoundationmodels/error_codes.json +163 -0
- applefoundationmodels/exceptions.py +122 -0
- applefoundationmodels/libfoundation_models.dylib +0 -0
- applefoundationmodels/py.typed +2 -0
- applefoundationmodels/pydantic_compat.py +144 -0
- applefoundationmodels/session.py +265 -0
- applefoundationmodels/tools.py +285 -0
- applefoundationmodels/types.py +284 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apple-foundation-models
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Python bindings for Apple's FoundationModels framework - on-device AI (requires macOS 26.0+)
|
|
5
|
+
Author: Ben Tucker
|
|
6
|
+
Maintainer: Ben Tucker
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/btucker/apple-foundation-models-py
|
|
9
|
+
Project-URL: Repository, https://github.com/btucker/apple-foundation-models-py
|
|
10
|
+
Project-URL: Documentation, https://github.com/btucker/apple-foundation-models-py#readme
|
|
11
|
+
Project-URL: Issues, https://github.com/btucker/apple-foundation-models-py/issues
|
|
12
|
+
Keywords: apple,intelligence,ai,llm,foundation-models,on-device
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Cython
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.20; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
35
|
+
Requires-Dist: mypy>=0.991; extra == "dev"
|
|
36
|
+
Provides-Extra: pydantic
|
|
37
|
+
Requires-Dist: pydantic>=2.0; extra == "pydantic"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# apple-foundation-models
|
|
41
|
+
|
|
42
|
+
Unofficial Python bindings for Apple's [Foundation Models framework](https://developer.apple.com/documentation/FoundationModels) - Direct access to the on-device LLM available in macOS 26+.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Async Support**: Full async/await support with `AsyncSession`
|
|
47
|
+
- **Tool Calling**: Register Python functions as tools for the model to call
|
|
48
|
+
- **Structured Outputs**: JSON Schema and Pydantic model support
|
|
49
|
+
- **Streaming**: Real-time token-by-token response generation
|
|
50
|
+
|
|
51
|
+
## Limitations
|
|
52
|
+
|
|
53
|
+
- **Context Window**: 4,096 tokens per session (includes instructions, prompts, and outputs)
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
- macOS 26.0+ (macOS Sequoia or later)
|
|
58
|
+
- Python 3.9 or higher
|
|
59
|
+
- Apple Intelligence enabled on your device
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
### From PyPI
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install apple-foundation-models
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Optional dependencies:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# For Pydantic model support in structured outputs
|
|
73
|
+
pip install apple-foundation-models[pydantic]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### From Source
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Clone the repository
|
|
80
|
+
git clone https://github.com/btucker/apple-foundation-models-py.git
|
|
81
|
+
cd apple-foundation-models-py
|
|
82
|
+
|
|
83
|
+
# Install (automatically builds Swift dylib and Cython extension)
|
|
84
|
+
pip install -e .
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Requirements:**
|
|
88
|
+
|
|
89
|
+
- macOS 26.0+ (Sequoia) with Apple Intelligence enabled
|
|
90
|
+
- Xcode command line tools (`xcode-select --install`)
|
|
91
|
+
- Python 3.9 or higher
|
|
92
|
+
|
|
93
|
+
**Note:** The Swift dylib is built automatically during installation.
|
|
94
|
+
|
|
95
|
+
## Quick Start
|
|
96
|
+
|
|
97
|
+
### Check Availability
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from applefoundationmodels import apple_intelligence_available
|
|
101
|
+
|
|
102
|
+
if apple_intelligence_available():
|
|
103
|
+
print("Apple Intelligence is ready!")
|
|
104
|
+
else:
|
|
105
|
+
print("Apple Intelligence is not available")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Basic Usage
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from applefoundationmodels import Session
|
|
112
|
+
|
|
113
|
+
with Session(instructions="You are a helpful assistant.") as session:
|
|
114
|
+
response = session.generate("What is the capital of France?")
|
|
115
|
+
print(response.text)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Streaming
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# Sync streaming
|
|
122
|
+
with Session() as session:
|
|
123
|
+
for chunk in session.generate("Tell me a story", stream=True):
|
|
124
|
+
print(chunk.content, end='', flush=True)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Async/Await
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
import asyncio
|
|
131
|
+
from applefoundationmodels import AsyncSession
|
|
132
|
+
|
|
133
|
+
async def main():
|
|
134
|
+
async with AsyncSession() as session:
|
|
135
|
+
# Use await for non-streaming
|
|
136
|
+
response = await session.generate("What is 2 + 2?")
|
|
137
|
+
print(response.text)
|
|
138
|
+
|
|
139
|
+
# Use async for with streaming
|
|
140
|
+
async for chunk in session.generate("Tell a story", stream=True):
|
|
141
|
+
print(chunk.content, end='', flush=True)
|
|
142
|
+
|
|
143
|
+
asyncio.run(main())
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Structured Output
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from applefoundationmodels import Session
|
|
150
|
+
|
|
151
|
+
with Session() as session:
|
|
152
|
+
# Define a JSON schema
|
|
153
|
+
schema = {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"properties": {
|
|
156
|
+
"name": {"type": "string"},
|
|
157
|
+
"age": {"type": "integer"},
|
|
158
|
+
"city": {"type": "string"}
|
|
159
|
+
},
|
|
160
|
+
"required": ["name", "age", "city"]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Generate structured response
|
|
164
|
+
response = session.generate(
|
|
165
|
+
"Extract person info: Alice is 28 and lives in Paris",
|
|
166
|
+
schema=schema
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
print(response.parsed) # {'name': 'Alice', 'age': 28, 'city': 'Paris'}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Using Pydantic Models
|
|
173
|
+
|
|
174
|
+
You can also use Pydantic models for structured outputs (requires `pip install pydantic>=2.0`):
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from applefoundationmodels import Session
|
|
178
|
+
from pydantic import BaseModel
|
|
179
|
+
|
|
180
|
+
class Person(BaseModel):
|
|
181
|
+
name: str
|
|
182
|
+
age: int
|
|
183
|
+
city: str
|
|
184
|
+
|
|
185
|
+
with Session() as session:
|
|
186
|
+
# Pass Pydantic model directly - no need for JSON schema!
|
|
187
|
+
response = session.generate(
|
|
188
|
+
"Extract person info: Alice is 28 and lives in Paris",
|
|
189
|
+
schema=Person
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
print(response.parsed) # {'name': 'Alice', 'age': 28, 'city': 'Paris'}
|
|
193
|
+
|
|
194
|
+
# Parse directly into a Pydantic model for validation
|
|
195
|
+
person = Person(**response.parsed)
|
|
196
|
+
# Or use the convenience method:
|
|
197
|
+
person = response.parse_as(Person)
|
|
198
|
+
print(person.name, person.age, person.city) # Alice 28 Paris
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Tool Calling
|
|
202
|
+
|
|
203
|
+
Tool calling allows the model to call your Python functions to access real-time data, perform actions, or integrate with external systems. Tools are registered when you create a session and remain available for all generate() calls on that session:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from applefoundationmodels import Session
|
|
207
|
+
|
|
208
|
+
# Define tools as regular Python functions with docstrings
|
|
209
|
+
def get_weather(location: str, units: str = "celsius") -> str:
|
|
210
|
+
"""Get current weather for a location."""
|
|
211
|
+
# Your implementation here
|
|
212
|
+
return f"Weather in {location}: 22°{units[0].upper()}, sunny"
|
|
213
|
+
|
|
214
|
+
def calculate(expression: str) -> float:
|
|
215
|
+
"""Evaluate a mathematical expression safely."""
|
|
216
|
+
# Your implementation here
|
|
217
|
+
return eval(expression) # Use safe_eval in production!
|
|
218
|
+
|
|
219
|
+
# Register tools at session creation - they'll be available for all generate() calls
|
|
220
|
+
with Session(tools=[get_weather, calculate]) as session:
|
|
221
|
+
# The model will automatically call tools when needed
|
|
222
|
+
response = session.generate("What's the weather in Paris and what's 15 times 23?")
|
|
223
|
+
print(response.text)
|
|
224
|
+
# "The weather in Paris is 22°C and sunny. 15 times 23 equals 345."
|
|
225
|
+
|
|
226
|
+
# Check if tools were called using the tool_calls property
|
|
227
|
+
if response.tool_calls:
|
|
228
|
+
print(f"Tools called: {len(response.tool_calls)}")
|
|
229
|
+
for tool_call in response.tool_calls:
|
|
230
|
+
print(f" - {tool_call.function.name}")
|
|
231
|
+
print(f" ID: {tool_call.id}")
|
|
232
|
+
print(f" Args: {tool_call.function.arguments}")
|
|
233
|
+
|
|
234
|
+
# Check why generation stopped
|
|
235
|
+
print(f"Finish reason: {response.finish_reason}")
|
|
236
|
+
# "tool_calls" if tools were called, "stop" otherwise
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
See `examples/tool_calling_comprehensive.py` for complete examples of all supported patterns.
|
|
240
|
+
|
|
241
|
+
### Generation Parameters
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from applefoundationmodels import Session
|
|
245
|
+
|
|
246
|
+
with Session() as session:
|
|
247
|
+
# Control generation with parameters
|
|
248
|
+
response = session.generate(
|
|
249
|
+
"Write a creative story",
|
|
250
|
+
temperature=1.5, # Higher = more creative (0.0-2.0)
|
|
251
|
+
max_tokens=500 # Limit response length
|
|
252
|
+
)
|
|
253
|
+
print(response.text)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Session Management
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from applefoundationmodels import Session
|
|
260
|
+
|
|
261
|
+
# Create multiple sessions
|
|
262
|
+
chat_session = Session(
|
|
263
|
+
instructions="You are a friendly chatbot"
|
|
264
|
+
)
|
|
265
|
+
code_session = Session(
|
|
266
|
+
instructions="You are a code review assistant"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Each session maintains separate conversation history
|
|
270
|
+
chat_response = chat_session.generate("Hello!")
|
|
271
|
+
print(chat_response.text)
|
|
272
|
+
|
|
273
|
+
code_response = code_session.generate("Review this code: ...")
|
|
274
|
+
print(code_response.text)
|
|
275
|
+
|
|
276
|
+
# Clear history while keeping session
|
|
277
|
+
chat_session.clear_history()
|
|
278
|
+
|
|
279
|
+
# Clean up
|
|
280
|
+
chat_session.close()
|
|
281
|
+
code_session.close()
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## API Reference
|
|
285
|
+
|
|
286
|
+
### Sync vs Async Pattern
|
|
287
|
+
|
|
288
|
+
We provide separate session classes for sync and async:
|
|
289
|
+
|
|
290
|
+
- **`Session`**: Synchronous operations with context manager support
|
|
291
|
+
- **`AsyncSession`**: Async operations with async context manager support
|
|
292
|
+
|
|
293
|
+
Both have identical method signatures - just use `await` with `AsyncSession`.
|
|
294
|
+
|
|
295
|
+
### Convenience Functions
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
def apple_intelligence_available() -> bool:
|
|
299
|
+
"""Check if Apple Intelligence is available and ready for use."""
|
|
300
|
+
...
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Session
|
|
304
|
+
|
|
305
|
+
Manages conversation state and text generation (synchronous).
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
class Session:
|
|
309
|
+
def __init__(
|
|
310
|
+
instructions: Optional[str] = None,
|
|
311
|
+
tools: Optional[List[Callable]] = None
|
|
312
|
+
) -> None: ...
|
|
313
|
+
|
|
314
|
+
def __enter__() -> Session: ...
|
|
315
|
+
def __exit__(...) -> None: ...
|
|
316
|
+
|
|
317
|
+
# Static utility methods
|
|
318
|
+
@staticmethod
|
|
319
|
+
def check_availability() -> Availability: ...
|
|
320
|
+
@staticmethod
|
|
321
|
+
def get_availability_reason() -> str: ...
|
|
322
|
+
@staticmethod
|
|
323
|
+
def is_ready() -> bool: ...
|
|
324
|
+
@staticmethod
|
|
325
|
+
def get_version() -> str: ...
|
|
326
|
+
|
|
327
|
+
# Unified generation method
|
|
328
|
+
def generate(
|
|
329
|
+
prompt: str,
|
|
330
|
+
schema: Optional[Union[dict, Type[BaseModel]]] = None,
|
|
331
|
+
stream: bool = False,
|
|
332
|
+
temperature: Optional[float] = None,
|
|
333
|
+
max_tokens: Optional[int] = None
|
|
334
|
+
) -> Union[GenerationResponse, Iterator[StreamChunk]]: ...
|
|
335
|
+
|
|
336
|
+
@property
|
|
337
|
+
def transcript() -> List[dict]: ...
|
|
338
|
+
@property
|
|
339
|
+
def last_generation_transcript() -> List[dict]: ...
|
|
340
|
+
|
|
341
|
+
def get_history() -> List[dict]: ...
|
|
342
|
+
def clear_history() -> None: ...
|
|
343
|
+
def close() -> None: ...
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### AsyncSession
|
|
347
|
+
|
|
348
|
+
Manages conversation state and text generation (asynchronous).
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
class AsyncSession:
|
|
352
|
+
def __init__(
|
|
353
|
+
instructions: Optional[str] = None,
|
|
354
|
+
tools: Optional[List[Callable]] = None
|
|
355
|
+
) -> None: ...
|
|
356
|
+
|
|
357
|
+
async def __aenter__() -> AsyncSession: ...
|
|
358
|
+
async def __aexit__(...) -> None: ...
|
|
359
|
+
|
|
360
|
+
# Static utility methods (inherited from Session)
|
|
361
|
+
@staticmethod
|
|
362
|
+
def check_availability() -> Availability: ...
|
|
363
|
+
@staticmethod
|
|
364
|
+
def get_availability_reason() -> str: ...
|
|
365
|
+
@staticmethod
|
|
366
|
+
def is_ready() -> bool: ...
|
|
367
|
+
@staticmethod
|
|
368
|
+
def get_version() -> str: ...
|
|
369
|
+
|
|
370
|
+
# Unified async generation method
|
|
371
|
+
def generate(
|
|
372
|
+
prompt: str,
|
|
373
|
+
schema: Optional[Union[dict, Type[BaseModel]]] = None,
|
|
374
|
+
stream: bool = False,
|
|
375
|
+
temperature: Optional[float] = None,
|
|
376
|
+
max_tokens: Optional[int] = None
|
|
377
|
+
) -> Union[Coroutine[GenerationResponse], AsyncIterator[StreamChunk]]: ...
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def transcript() -> List[dict]: ...
|
|
381
|
+
@property
|
|
382
|
+
def last_generation_transcript() -> List[dict]: ...
|
|
383
|
+
|
|
384
|
+
async def get_history() -> List[dict]: ...
|
|
385
|
+
async def clear_history() -> None: ...
|
|
386
|
+
async def close() -> None: ...
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Response Types
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
@dataclass
|
|
393
|
+
class GenerationResponse:
|
|
394
|
+
"""Response from non-streaming generation."""
|
|
395
|
+
content: Union[str, Dict[str, Any]]
|
|
396
|
+
is_structured: bool
|
|
397
|
+
|
|
398
|
+
@property
|
|
399
|
+
def text() -> str: ... # For text responses
|
|
400
|
+
@property
|
|
401
|
+
def parsed() -> Dict[str, Any]: ... # For structured responses
|
|
402
|
+
def parse_as(model: Type[BaseModel]) -> BaseModel: ... # Parse into Pydantic
|
|
403
|
+
|
|
404
|
+
@dataclass
|
|
405
|
+
class StreamChunk:
|
|
406
|
+
"""Chunk from streaming generation."""
|
|
407
|
+
content: str # Text delta
|
|
408
|
+
finish_reason: Optional[str] = None
|
|
409
|
+
index: int = 0
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Types
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
class Availability(IntEnum):
|
|
416
|
+
AVAILABLE = 1
|
|
417
|
+
DEVICE_NOT_ELIGIBLE = -1
|
|
418
|
+
NOT_ENABLED = -2
|
|
419
|
+
MODEL_NOT_READY = -3
|
|
420
|
+
|
|
421
|
+
class SessionConfig(TypedDict):
|
|
422
|
+
instructions: Optional[str]
|
|
423
|
+
|
|
424
|
+
class GenerationParams(TypedDict):
|
|
425
|
+
temperature: float
|
|
426
|
+
max_tokens: int
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Exceptions
|
|
430
|
+
|
|
431
|
+
All exceptions inherit from `FoundationModelsError`:
|
|
432
|
+
|
|
433
|
+
**System Errors:**
|
|
434
|
+
|
|
435
|
+
- `InitializationError` - Library initialization failed
|
|
436
|
+
- `NotAvailableError` - Apple Intelligence not available
|
|
437
|
+
- `InvalidParametersError` - Invalid parameters
|
|
438
|
+
- `MemoryError` - Memory allocation failed
|
|
439
|
+
- `JSONParseError` - JSON parsing error
|
|
440
|
+
- `TimeoutError` - Operation timeout
|
|
441
|
+
- `SessionNotFoundError` - Session not found
|
|
442
|
+
- `StreamNotFoundError` - Stream not found
|
|
443
|
+
- `UnknownError` - Unknown error
|
|
444
|
+
|
|
445
|
+
**Generation Errors** (all inherit from `GenerationError`):
|
|
446
|
+
|
|
447
|
+
- `GenerationError` - Generic text generation error
|
|
448
|
+
- `ContextWindowExceededError` - Context window limit exceeded (4096 tokens)
|
|
449
|
+
- `AssetsUnavailableError` - Required model assets are unavailable
|
|
450
|
+
- `DecodingFailureError` - Failed to deserialize model output
|
|
451
|
+
- `GuardrailViolationError` - Content blocked by safety filters
|
|
452
|
+
- `RateLimitedError` - Session has been rate limited
|
|
453
|
+
- `RefusalError` - Session refused the request
|
|
454
|
+
- `ConcurrentRequestsError` - Multiple concurrent requests to same session
|
|
455
|
+
- `UnsupportedGuideError` - Unsupported generation guide pattern
|
|
456
|
+
- `UnsupportedLanguageError` - Unsupported language or locale
|
|
457
|
+
|
|
458
|
+
**Tool Errors:**
|
|
459
|
+
|
|
460
|
+
- `ToolNotFoundError` - Tool not registered
|
|
461
|
+
- `ToolExecutionError` - Tool execution failed
|
|
462
|
+
- `ToolCallError` - Tool call error (validation, schema, etc.)
|
|
463
|
+
|
|
464
|
+
## Examples
|
|
465
|
+
|
|
466
|
+
See the `examples/` directory for complete working examples:
|
|
467
|
+
|
|
468
|
+
- `basic_chat.py` - Simple synchronous conversation
|
|
469
|
+
- `basic_async_chat.py` - Async conversation with await (no streaming)
|
|
470
|
+
- `streaming_chat.py` - Async streaming responses
|
|
471
|
+
- `structured_output.py` - JSON schema validation
|
|
472
|
+
- `tool_calling_comprehensive.py` - Complete tool calling demonstration with all parameter types
|
|
473
|
+
|
|
474
|
+
## Development
|
|
475
|
+
|
|
476
|
+
### Building from Source
|
|
477
|
+
|
|
478
|
+
This project uses [uv](https://docs.astral.sh/uv/) for fast, reliable builds and dependency management:
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
# Install uv (if not already installed)
|
|
482
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
483
|
+
|
|
484
|
+
# Install development dependencies
|
|
485
|
+
uv sync --extra dev
|
|
486
|
+
|
|
487
|
+
# Run tests
|
|
488
|
+
uv run pytest
|
|
489
|
+
|
|
490
|
+
# Type checking
|
|
491
|
+
uv run mypy applefoundationmodels
|
|
492
|
+
|
|
493
|
+
# Format code
|
|
494
|
+
uv run black applefoundationmodels examples
|
|
495
|
+
|
|
496
|
+
# Build wheels
|
|
497
|
+
uv build --wheel
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
You can also use pip if preferred:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
pip install -e ".[dev]"
|
|
504
|
+
pytest
|
|
505
|
+
mypy applefoundationmodels
|
|
506
|
+
black applefoundationmodels examples
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Project Structure
|
|
510
|
+
|
|
511
|
+
```
|
|
512
|
+
apple-foundation-models-py/
|
|
513
|
+
├── applefoundationmodels/ # Python package
|
|
514
|
+
│ ├── __init__.py # Public API
|
|
515
|
+
│ ├── _foundationmodels.pyx # Cython bindings
|
|
516
|
+
│ ├── _foundationmodels.pxd # C declarations
|
|
517
|
+
│ ├── _foundationmodels.pyi # Type stubs
|
|
518
|
+
│ ├── base.py # Base context manager classes
|
|
519
|
+
│ ├── base_session.py # Shared session logic
|
|
520
|
+
│ ├── session.py # Synchronous session
|
|
521
|
+
│ ├── async_session.py # Asynchronous session
|
|
522
|
+
│ ├── types.py # Type definitions
|
|
523
|
+
│ ├── constants.py # Constants and defaults
|
|
524
|
+
│ ├── tools.py # Tool calling support
|
|
525
|
+
│ ├── pydantic_compat.py # Pydantic compatibility layer
|
|
526
|
+
│ ├── exceptions.py # Exception classes
|
|
527
|
+
│ └── swift/ # Swift FoundationModels bindings
|
|
528
|
+
│ ├── foundation_models.swift # Swift implementation
|
|
529
|
+
│ └── foundation_models.h # C FFI header
|
|
530
|
+
├── lib/ # Swift dylib and modules (auto-generated)
|
|
531
|
+
│ ├── libfoundation_models.dylib # Compiled Swift library
|
|
532
|
+
│ └── foundation_models.swiftmodule # Swift module
|
|
533
|
+
├── examples/ # Example scripts
|
|
534
|
+
│ └── utils.py # Shared utilities
|
|
535
|
+
└── tests/ # Unit tests
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## Architecture
|
|
539
|
+
|
|
540
|
+
apple-foundation-models-py uses a layered architecture for optimal performance:
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
Python API (session.py, async_session.py)
|
|
544
|
+
↓
|
|
545
|
+
Cython FFI (_foundationmodels.pyx)
|
|
546
|
+
↓
|
|
547
|
+
C FFI Layer (foundation_models.h)
|
|
548
|
+
↓
|
|
549
|
+
Swift Implementation (foundation_models.swift)
|
|
550
|
+
↓
|
|
551
|
+
FoundationModels Framework (Apple Intelligence)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Performance
|
|
555
|
+
|
|
556
|
+
- Cython-compiled for near-C performance
|
|
557
|
+
- Direct Swift → FoundationModels calls (no intermediate libraries)
|
|
558
|
+
- Async streaming with delta-based chunk delivery
|
|
559
|
+
- No GIL during Swift library calls (when possible)
|
|
560
|
+
|
|
561
|
+
## Troubleshooting
|
|
562
|
+
|
|
563
|
+
### Apple Intelligence not available
|
|
564
|
+
|
|
565
|
+
If you get `NotAvailableError`:
|
|
566
|
+
|
|
567
|
+
1. Ensure you're running macOS 26.0 (Sequoia) or later
|
|
568
|
+
2. Check System Settings → Apple Intelligence → Enable
|
|
569
|
+
3. Wait for models to download (check with `Session.get_availability_reason()`)
|
|
570
|
+
|
|
571
|
+
### Import errors
|
|
572
|
+
|
|
573
|
+
If you get import errors after installation:
|
|
574
|
+
|
|
575
|
+
```bash
|
|
576
|
+
# Rebuild everything (Swift dylib + Cython extension)
|
|
577
|
+
pip install --force-reinstall --no-cache-dir -e .
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Compilation errors
|
|
581
|
+
|
|
582
|
+
Ensure you have Xcode command line tools:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
xcode-select --install
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
If the Swift build fails during installation:
|
|
589
|
+
|
|
590
|
+
1. Verify macOS version: `sw_vers -productVersion` (should be 26.0+)
|
|
591
|
+
2. Check Swift compiler: `swiftc --version`
|
|
592
|
+
3. Clean and reinstall: `pip install --force-reinstall --no-cache-dir -e .`
|
|
593
|
+
|
|
594
|
+
## License
|
|
595
|
+
|
|
596
|
+
MIT License - see LICENSE file for details
|
|
597
|
+
|
|
598
|
+
## Contributing
|
|
599
|
+
|
|
600
|
+
Contributions are welcome! Please:
|
|
601
|
+
|
|
602
|
+
1. Fork the repository
|
|
603
|
+
2. Create a feature branch
|
|
604
|
+
3. Add tests for new functionality
|
|
605
|
+
4. Ensure all tests pass
|
|
606
|
+
5. Submit a pull request
|
|
607
|
+
|
|
608
|
+
## Links
|
|
609
|
+
|
|
610
|
+
- [FoundationModels Framework](https://developer.apple.com/documentation/FoundationModels)
|
|
611
|
+
- [Apple Intelligence Documentation](https://developer.apple.com/apple-intelligence/)
|
|
612
|
+
- [Issue Tracker](https://github.com/btucker/apple-foundation-models-py/issues)
|
|
613
|
+
|
|
614
|
+
## Acknowledgments
|
|
615
|
+
|
|
616
|
+
This project was inspired by and learned from several excellent works:
|
|
617
|
+
|
|
618
|
+
- **[libai](https://github.com/6over3/libai)** by 6over3 Institute - The original C library wrapper for FoundationModels that demonstrated the possibility of non-Objective-C access to Apple Intelligence. While we ultimately chose a direct Swift integration approach, the libai project's API design and documentation heavily influenced our Python API structure.
|
|
619
|
+
|
|
620
|
+
- **[apple-on-device-ai](https://github.com/Meridius-Labs/apple-on-device-ai)** by Meridius Labs - The Node.js bindings that showed the path to direct FoundationModels integration via Swift. Their architecture of using Swift → C FFI → JavaScript inspired our Swift → C FFI → Cython → Python approach, and their code examples were invaluable for understanding the FoundationModels API.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
apple_foundation_models-0.2.2.dist-info/licenses/LICENSE,sha256=W2K9kU7tXVwGlNefzUJxZwENt8wDeJ8yoG4kCTXzQBI,1072
|
|
2
|
+
applefoundationmodels/__init__.py,sha256=qJbfW9mQrzP3KxMR4rvD7kBXhqOpRek75LfWKrcdi-g,3155
|
|
3
|
+
applefoundationmodels/_foundationmodels.cpython-312-darwin.so,sha256=Y3_6-YUuJJyo6Tx1id_6oUsGS-4ZsK4OtJjC-Kxztqo,155544
|
|
4
|
+
applefoundationmodels/_foundationmodels.pyi,sha256=8Be35H6GXomNmLIyrM46v2q6L0IXqiDuL8-yJZ5iNgc,1145
|
|
5
|
+
applefoundationmodels/async_session.py,sha256=7qq7t9Cdy1ZEupwkU1ACc-_0PyxCuchdIveVt8A_-x0,10639
|
|
6
|
+
applefoundationmodels/base.py,sha256=QF3R4MUqD5HBuXwQIG653JZWQvjKtsniBdhdDE31ymE,1696
|
|
7
|
+
applefoundationmodels/base_session.py,sha256=kt76tdIAQVCVuIX8gepCYxc-g--87CWPqc8flDjzVto,21825
|
|
8
|
+
applefoundationmodels/constants.py,sha256=2JsMVESQHp4k5SSqayPofx1l6oXtV-ycD33Tc4BiGtI,901
|
|
9
|
+
applefoundationmodels/error_codes.json,sha256=w7ug2ht3q1cBxfp73aILlcd-AoR6or6bCQ5ADsUWJMU,4513
|
|
10
|
+
applefoundationmodels/exceptions.py,sha256=iZwaAGcU1_M1z66lNQUJRTMjsR_Hr9PCmfrsrsvE_Ow,3475
|
|
11
|
+
applefoundationmodels/libfoundation_models.dylib,sha256=30mT6wZFehBDOjTNlCAMdug3CD8zYptM7n0wd11Dd-0,169080
|
|
12
|
+
applefoundationmodels/py.typed,sha256=ixa8YukDZ3kLo0WsFJRGohLMyHzbMur1ALmmASML2cs,64
|
|
13
|
+
applefoundationmodels/pydantic_compat.py,sha256=ziHVapALsQZcAUFVrp8pB5CHWnPPo7g-Yc70vRTJGkY,3714
|
|
14
|
+
applefoundationmodels/session.py,sha256=mduSEzf6VB-1J_IVwhllYoxEY8MXHQb8RYJ93XPjZcc,9183
|
|
15
|
+
applefoundationmodels/tools.py,sha256=nfySJidXK1WjwmtFEw_Y3r6eBrIN70JI2tRPmABvXVo,7919
|
|
16
|
+
applefoundationmodels/types.py,sha256=Wk37qZ1hFdF3zzYaJiWpHwg5Cv8UnIHbzs6QdM8J9RE,7809
|
|
17
|
+
apple_foundation_models-0.2.2.dist-info/METADATA,sha256=mjX-Hut9_Ymu7sTcdqx3NPtXKAUMwD4Zt26o9d8YRzY,18700
|
|
18
|
+
apple_foundation_models-0.2.2.dist-info/WHEEL,sha256=8_NoFkNIT9ESX1whb8xp7yk-WreskodI-zgMhxiWLhg,114
|
|
19
|
+
apple_foundation_models-0.2.2.dist-info/top_level.txt,sha256=6PGWTiHgh5vWT-cjbJImnO-ZJPKiKiGA-PGk2SJndYw,22
|
|
20
|
+
apple_foundation_models-0.2.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Benjamin Tucker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
applefoundationmodels
|