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.
- 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-311-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,607 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apple-foundation-models
|
|
3
|
+
Version: 0.1.9
|
|
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
|
+
Python bindings for Apple's FoundationModels framework - Direct access to on-device Apple Intelligence.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **High-level Pythonic API**: Context managers, async/await, type hints
|
|
47
|
+
- **Structured Outputs**: JSON Schema and Pydantic model support
|
|
48
|
+
- **Async Streaming**: Native `async for` support for streaming responses
|
|
49
|
+
- **Type Safety**: Full type annotations with mypy support
|
|
50
|
+
- **Memory Safe**: Automatic resource cleanup, no manual memory management
|
|
51
|
+
- **Thread Safe**: All operations are thread-safe
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- macOS 26.0+ (macOS Sequoia or later)
|
|
56
|
+
- Python 3.9 or higher
|
|
57
|
+
- Apple Intelligence enabled on your device
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
### From PyPI
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install apple-foundation-models
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Optional dependencies:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# For Pydantic model support in structured outputs
|
|
71
|
+
pip install apple-foundation-models[pydantic]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### From Source
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Clone the repository
|
|
78
|
+
git clone https://github.com/btucker/apple-foundation-models-py.git
|
|
79
|
+
cd apple-foundation-models-py
|
|
80
|
+
|
|
81
|
+
# Install (automatically builds Swift dylib and Cython extension)
|
|
82
|
+
pip install -e .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Requirements:**
|
|
86
|
+
|
|
87
|
+
- macOS 26.0+ (Sequoia) with Apple Intelligence enabled
|
|
88
|
+
- Xcode command line tools (`xcode-select --install`)
|
|
89
|
+
- Python 3.9 or higher
|
|
90
|
+
|
|
91
|
+
**Note:** The Swift dylib is built automatically during installation.
|
|
92
|
+
|
|
93
|
+
## Quick Start
|
|
94
|
+
|
|
95
|
+
### Basic Usage
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from applefoundationmodels import Client
|
|
99
|
+
|
|
100
|
+
# Create a client (library auto-initializes)
|
|
101
|
+
with Client() as client:
|
|
102
|
+
# Check if Apple Intelligence is available
|
|
103
|
+
if not client.is_ready():
|
|
104
|
+
print("Apple Intelligence is not available")
|
|
105
|
+
print(client.get_availability_reason())
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
# Create a session
|
|
109
|
+
session = client.create_session(
|
|
110
|
+
instructions="You are a helpful assistant.",
|
|
111
|
+
enable_guardrails=True
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Generate a response
|
|
115
|
+
response = session.generate("What is the capital of France?")
|
|
116
|
+
print(response)
|
|
117
|
+
|
|
118
|
+
# Get conversation history
|
|
119
|
+
history = session.get_history()
|
|
120
|
+
for msg in history:
|
|
121
|
+
print(f"{msg['role']}: {msg['content']}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Async Streaming
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import asyncio
|
|
128
|
+
from applefoundationmodels import Client
|
|
129
|
+
|
|
130
|
+
async def main():
|
|
131
|
+
with Client() as client:
|
|
132
|
+
session = client.create_session()
|
|
133
|
+
|
|
134
|
+
# Stream response chunks as they arrive
|
|
135
|
+
async for chunk in session.generate_stream("Tell me a story about a robot"):
|
|
136
|
+
print(chunk, end='', flush=True)
|
|
137
|
+
print() # Newline after stream
|
|
138
|
+
|
|
139
|
+
asyncio.run(main())
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Structured Output
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from applefoundationmodels import Client
|
|
146
|
+
|
|
147
|
+
with Client() as client:
|
|
148
|
+
session = client.create_session()
|
|
149
|
+
|
|
150
|
+
# Define a JSON schema
|
|
151
|
+
schema = {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"properties": {
|
|
154
|
+
"name": {"type": "string"},
|
|
155
|
+
"age": {"type": "integer"},
|
|
156
|
+
"city": {"type": "string"}
|
|
157
|
+
},
|
|
158
|
+
"required": ["name", "age", "city"]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# Generate structured response
|
|
162
|
+
result = session.generate_structured(
|
|
163
|
+
"Extract person info: Alice is 28 and lives in Paris",
|
|
164
|
+
schema=schema
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
print(result) # {'name': 'Alice', 'age': 28, 'city': 'Paris'}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Using Pydantic Models
|
|
171
|
+
|
|
172
|
+
You can also use Pydantic models for structured outputs (requires `pip install pydantic>=2.0`):
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from applefoundationmodels import Client
|
|
176
|
+
from pydantic import BaseModel
|
|
177
|
+
|
|
178
|
+
class Person(BaseModel):
|
|
179
|
+
name: str
|
|
180
|
+
age: int
|
|
181
|
+
city: str
|
|
182
|
+
|
|
183
|
+
with Client() as client:
|
|
184
|
+
session = client.create_session()
|
|
185
|
+
|
|
186
|
+
# Pass Pydantic model directly - no need for JSON schema!
|
|
187
|
+
result = session.generate_structured(
|
|
188
|
+
"Extract person info: Alice is 28 and lives in Paris",
|
|
189
|
+
schema=Person
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
print(result) # {'name': 'Alice', 'age': 28, 'city': 'Paris'}
|
|
193
|
+
|
|
194
|
+
# Parse directly into a Pydantic model for validation
|
|
195
|
+
person = Person(**result)
|
|
196
|
+
print(person.name, person.age, person.city) # Alice 28 Paris
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Tool Calling
|
|
200
|
+
|
|
201
|
+
Tool calling allows the model to call your Python functions to access real-time data, perform actions, or integrate with external systems. Tools work with a simple decorator-based API:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from applefoundationmodels import Client
|
|
205
|
+
|
|
206
|
+
with Client() as client:
|
|
207
|
+
session = client.create_session()
|
|
208
|
+
|
|
209
|
+
# Register a tool with the @session.tool decorator
|
|
210
|
+
@session.tool(description="Get current weather for a location")
|
|
211
|
+
def get_weather(location: str, units: str = "celsius") -> str:
|
|
212
|
+
"""Fetch weather information from your weather API."""
|
|
213
|
+
# Your implementation here
|
|
214
|
+
return f"Weather in {location}: 22°{units[0].upper()}, sunny"
|
|
215
|
+
|
|
216
|
+
@session.tool()
|
|
217
|
+
def calculate(expression: str) -> float:
|
|
218
|
+
"""Evaluate a mathematical expression safely."""
|
|
219
|
+
# Your implementation here
|
|
220
|
+
return eval(expression) # Use safe_eval in production!
|
|
221
|
+
|
|
222
|
+
# The model will automatically call tools when needed
|
|
223
|
+
response = session.generate(
|
|
224
|
+
"What's the weather in Paris and what's 15 times 23?"
|
|
225
|
+
)
|
|
226
|
+
print(response)
|
|
227
|
+
# "The weather in Paris is 22°C and sunny. 15 times 23 equals 345."
|
|
228
|
+
|
|
229
|
+
# View the full conversation including tool calls
|
|
230
|
+
for entry in session.transcript:
|
|
231
|
+
print(f"{entry['type']}: {entry.get('content', '')}")
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Features:**
|
|
235
|
+
- **Automatic schema generation** from Python type hints
|
|
236
|
+
- **Parallel tool execution** when the model calls multiple tools
|
|
237
|
+
- **Full transcript access** showing all tool calls and outputs
|
|
238
|
+
- **Error handling** with detailed error information
|
|
239
|
+
- **Type-safe** with complete type annotations
|
|
240
|
+
|
|
241
|
+
**Schema Extraction:**
|
|
242
|
+
|
|
243
|
+
The library automatically extracts JSON schemas from your Python functions:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
@session.tool(description="Search documentation")
|
|
247
|
+
def search_docs(query: str, limit: int = 10, category: str = "all") -> list:
|
|
248
|
+
"""Search the documentation database."""
|
|
249
|
+
# Implementation...
|
|
250
|
+
return results
|
|
251
|
+
|
|
252
|
+
# Automatically generates:
|
|
253
|
+
# {
|
|
254
|
+
# "name": "search_docs",
|
|
255
|
+
# "description": "Search documentation",
|
|
256
|
+
# "parameters": {
|
|
257
|
+
# "type": "object",
|
|
258
|
+
# "properties": {
|
|
259
|
+
# "query": {"type": "string"},
|
|
260
|
+
# "limit": {"type": "integer"},
|
|
261
|
+
# "category": {"type": "string"}
|
|
262
|
+
# },
|
|
263
|
+
# "required": ["query"]
|
|
264
|
+
# }
|
|
265
|
+
# }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Transcript Access:**
|
|
269
|
+
|
|
270
|
+
View the complete conversation history including tool interactions:
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
# After generating with tools
|
|
274
|
+
for entry in session.transcript:
|
|
275
|
+
match entry['type']:
|
|
276
|
+
case 'prompt':
|
|
277
|
+
print(f"User: {entry['content']}")
|
|
278
|
+
case 'tool_calls':
|
|
279
|
+
for call in entry['tool_calls']:
|
|
280
|
+
print(f"Calling tool: {call['id']}")
|
|
281
|
+
case 'tool_output':
|
|
282
|
+
print(f"Tool result: {entry['content']}")
|
|
283
|
+
case 'response':
|
|
284
|
+
print(f"Assistant: {entry['content']}")
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Supported Parameter Types:**
|
|
288
|
+
|
|
289
|
+
Tool calling works with various parameter signatures:
|
|
290
|
+
- No parameters
|
|
291
|
+
- Single parameters (string, int, float, bool)
|
|
292
|
+
- Multiple parameters with mixed types
|
|
293
|
+
- Optional parameters with default values
|
|
294
|
+
- Lists and nested objects
|
|
295
|
+
|
|
296
|
+
See `examples/tool_calling_comprehensive.py` for complete examples of all supported patterns.
|
|
297
|
+
|
|
298
|
+
### Generation Parameters
|
|
299
|
+
|
|
300
|
+
```python
|
|
301
|
+
# Control generation with parameters
|
|
302
|
+
response = session.generate(
|
|
303
|
+
"Write a creative story",
|
|
304
|
+
temperature=1.5, # Higher = more creative (0.0-2.0)
|
|
305
|
+
max_tokens=500, # Limit response length
|
|
306
|
+
seed=42 # Reproducible outputs
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Session Management
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
with Client() as client:
|
|
314
|
+
# Create multiple sessions
|
|
315
|
+
chat_session = client.create_session(
|
|
316
|
+
instructions="You are a friendly chatbot"
|
|
317
|
+
)
|
|
318
|
+
code_session = client.create_session(
|
|
319
|
+
instructions="You are a code review assistant"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Each session maintains separate conversation history
|
|
323
|
+
chat_response = chat_session.generate("Hello!")
|
|
324
|
+
code_response = code_session.generate("Review this code: ...")
|
|
325
|
+
|
|
326
|
+
# Clear history while keeping session
|
|
327
|
+
chat_session.clear_history()
|
|
328
|
+
|
|
329
|
+
# Manually add messages
|
|
330
|
+
chat_session.add_message("system", "Be concise")
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Statistics
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
with Client() as client:
|
|
337
|
+
session = client.create_session()
|
|
338
|
+
|
|
339
|
+
# Generate some responses
|
|
340
|
+
for i in range(5):
|
|
341
|
+
session.generate(f"Question {i}")
|
|
342
|
+
|
|
343
|
+
# Get statistics
|
|
344
|
+
stats = client.get_stats()
|
|
345
|
+
print(f"Total requests: {stats['total_requests']}")
|
|
346
|
+
print(f"Success rate: {stats['successful_requests'] / stats['total_requests'] * 100:.1f}%")
|
|
347
|
+
print(f"Avg response time: {stats['average_response_time']:.2f}s")
|
|
348
|
+
|
|
349
|
+
# Reset statistics
|
|
350
|
+
client.reset_stats()
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## API Reference
|
|
354
|
+
|
|
355
|
+
### Client
|
|
356
|
+
|
|
357
|
+
The main entry point for using libai.
|
|
358
|
+
|
|
359
|
+
```python
|
|
360
|
+
class Client:
|
|
361
|
+
def __init__() -> None: ...
|
|
362
|
+
def __enter__() -> Client: ...
|
|
363
|
+
def __exit__(...) -> None: ...
|
|
364
|
+
|
|
365
|
+
@staticmethod
|
|
366
|
+
def check_availability() -> Availability: ...
|
|
367
|
+
@staticmethod
|
|
368
|
+
def get_availability_reason() -> str: ...
|
|
369
|
+
@staticmethod
|
|
370
|
+
def is_ready() -> bool: ...
|
|
371
|
+
@staticmethod
|
|
372
|
+
def get_version() -> str: ...
|
|
373
|
+
@staticmethod
|
|
374
|
+
def get_supported_languages() -> List[str]: ...
|
|
375
|
+
|
|
376
|
+
def create_session(...) -> Session: ...
|
|
377
|
+
def get_stats() -> Stats: ...
|
|
378
|
+
def reset_stats() -> None: ...
|
|
379
|
+
def close() -> None: ...
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### Session
|
|
383
|
+
|
|
384
|
+
Manages conversation state and text generation.
|
|
385
|
+
|
|
386
|
+
```python
|
|
387
|
+
class Session:
|
|
388
|
+
def __enter__() -> Session: ...
|
|
389
|
+
def __exit__(...) -> None: ...
|
|
390
|
+
|
|
391
|
+
def generate(prompt: str, **params) -> str: ...
|
|
392
|
+
def generate_structured(prompt: str, schema: dict, **params) -> dict: ...
|
|
393
|
+
async def generate_stream(prompt: str, **params) -> AsyncIterator[str]: ...
|
|
394
|
+
|
|
395
|
+
def tool(description: str = None, name: str = None) -> Callable: ...
|
|
396
|
+
@property
|
|
397
|
+
def transcript() -> List[dict]: ...
|
|
398
|
+
|
|
399
|
+
def get_history() -> List[dict]: ...
|
|
400
|
+
def clear_history() -> None: ...
|
|
401
|
+
def add_message(role: str, content: str) -> None: ...
|
|
402
|
+
def close() -> None: ...
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Types
|
|
406
|
+
|
|
407
|
+
```python
|
|
408
|
+
class Availability(IntEnum):
|
|
409
|
+
AVAILABLE = 1
|
|
410
|
+
DEVICE_NOT_ELIGIBLE = -1
|
|
411
|
+
NOT_ENABLED = -2
|
|
412
|
+
MODEL_NOT_READY = -3
|
|
413
|
+
|
|
414
|
+
class SessionConfig(TypedDict):
|
|
415
|
+
instructions: Optional[str]
|
|
416
|
+
tools_json: Optional[str]
|
|
417
|
+
enable_guardrails: bool
|
|
418
|
+
prewarm: bool
|
|
419
|
+
|
|
420
|
+
class GenerationParams(TypedDict):
|
|
421
|
+
temperature: float
|
|
422
|
+
max_tokens: int
|
|
423
|
+
seed: int
|
|
424
|
+
|
|
425
|
+
class Stats(TypedDict):
|
|
426
|
+
total_requests: int
|
|
427
|
+
successful_requests: int
|
|
428
|
+
failed_requests: int
|
|
429
|
+
total_tokens_generated: int
|
|
430
|
+
average_response_time: float
|
|
431
|
+
total_processing_time: float
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Exceptions
|
|
435
|
+
|
|
436
|
+
All exceptions inherit from `FoundationModelsError`:
|
|
437
|
+
|
|
438
|
+
- `InitializationError` - Library initialization failed
|
|
439
|
+
- `NotAvailableError` - Apple Intelligence not available
|
|
440
|
+
- `InvalidParametersError` - Invalid parameters
|
|
441
|
+
- `MemoryError` - Memory allocation failed
|
|
442
|
+
- `JSONParseError` - JSON parsing error
|
|
443
|
+
- `GenerationError` - Text generation failed
|
|
444
|
+
- `TimeoutError` - Operation timeout
|
|
445
|
+
- `SessionNotFoundError` - Session not found
|
|
446
|
+
- `StreamNotFoundError` - Stream not found
|
|
447
|
+
- `GuardrailViolationError` - Content blocked by safety filters
|
|
448
|
+
- `ToolNotFoundError` - Tool not registered
|
|
449
|
+
- `ToolExecutionError` - Tool execution failed
|
|
450
|
+
- `ToolCallError` - Tool call error (validation, schema, etc.)
|
|
451
|
+
- `UnknownError` - Unknown error
|
|
452
|
+
|
|
453
|
+
## Examples
|
|
454
|
+
|
|
455
|
+
See the `examples/` directory for complete working examples:
|
|
456
|
+
|
|
457
|
+
- `basic_chat.py` - Simple conversation
|
|
458
|
+
- `streaming_chat.py` - Async streaming
|
|
459
|
+
- `structured_output.py` - JSON schema validation
|
|
460
|
+
- `tool_calling_comprehensive.py` - Complete tool calling demonstration with all parameter types
|
|
461
|
+
|
|
462
|
+
## Development
|
|
463
|
+
|
|
464
|
+
### Building from Source
|
|
465
|
+
|
|
466
|
+
This project uses [uv](https://docs.astral.sh/uv/) for fast, reliable builds and dependency management:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# Install uv (if not already installed)
|
|
470
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
471
|
+
|
|
472
|
+
# Install development dependencies
|
|
473
|
+
uv sync --extra dev
|
|
474
|
+
|
|
475
|
+
# Run tests
|
|
476
|
+
uv run pytest
|
|
477
|
+
|
|
478
|
+
# Type checking
|
|
479
|
+
uv run mypy applefoundationmodels
|
|
480
|
+
|
|
481
|
+
# Format code
|
|
482
|
+
uv run black applefoundationmodels examples
|
|
483
|
+
|
|
484
|
+
# Build wheels
|
|
485
|
+
uv build --wheel
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
You can also use pip if preferred:
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
pip install -e ".[dev]"
|
|
492
|
+
pytest
|
|
493
|
+
mypy applefoundationmodels
|
|
494
|
+
black applefoundationmodels examples
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Project Structure
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
apple-foundation-models-py/
|
|
501
|
+
├── applefoundationmodels/ # Python package
|
|
502
|
+
│ ├── __init__.py # Public API
|
|
503
|
+
│ ├── _foundationmodels.pyx # Cython bindings
|
|
504
|
+
│ ├── _foundationmodels.pxd # C declarations
|
|
505
|
+
│ ├── client.py # High-level Client
|
|
506
|
+
│ ├── session.py # Session management
|
|
507
|
+
│ ├── types.py # Type definitions
|
|
508
|
+
│ ├── exceptions.py # Exception classes
|
|
509
|
+
│ └── swift/ # Swift FoundationModels bindings
|
|
510
|
+
│ ├── foundation_models.swift # Swift implementation
|
|
511
|
+
│ └── foundation_models.h # C FFI header
|
|
512
|
+
├── lib/ # Swift dylib and modules (auto-generated)
|
|
513
|
+
│ └── libfoundation_models.dylib # Compiled Swift library
|
|
514
|
+
├── examples/ # Example scripts
|
|
515
|
+
└── tests/ # Unit tests
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Architecture
|
|
519
|
+
|
|
520
|
+
apple-foundation-models-py uses a layered architecture for optimal performance:
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
Python API (client.py, session.py)
|
|
524
|
+
↓
|
|
525
|
+
Cython FFI (_foundationmodels.pyx)
|
|
526
|
+
↓
|
|
527
|
+
C FFI Layer (foundation_models.h)
|
|
528
|
+
↓
|
|
529
|
+
Swift Implementation (foundation_models.swift)
|
|
530
|
+
↓
|
|
531
|
+
FoundationModels Framework (Apple Intelligence)
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Key Design Decisions:**
|
|
535
|
+
|
|
536
|
+
- **Direct FoundationModels Integration**: No intermediate C library - Swift calls FoundationModels directly
|
|
537
|
+
- **Minimal Overhead**: C FFI layer provides thin wrapper for Python/Swift communication
|
|
538
|
+
- **Async Coordination**: Uses semaphores to bridge Swift's async/await with synchronous C calls
|
|
539
|
+
- **Streaming**: Real-time delta calculation from FoundationModels snapshot-based streaming
|
|
540
|
+
|
|
541
|
+
## Performance
|
|
542
|
+
|
|
543
|
+
- Cython-compiled for near-C performance
|
|
544
|
+
- Direct Swift → FoundationModels calls (no intermediate libraries)
|
|
545
|
+
- Async streaming with delta-based chunk delivery
|
|
546
|
+
- No GIL during Swift library calls (when possible)
|
|
547
|
+
|
|
548
|
+
## Troubleshooting
|
|
549
|
+
|
|
550
|
+
### Apple Intelligence not available
|
|
551
|
+
|
|
552
|
+
If you get `NotAvailableError`:
|
|
553
|
+
|
|
554
|
+
1. Ensure you're running macOS 26.0 (Sequoia) or later
|
|
555
|
+
2. Check System Settings → Apple Intelligence → Enable
|
|
556
|
+
3. Wait for models to download (check with `client.get_availability_reason()`)
|
|
557
|
+
|
|
558
|
+
### Import errors
|
|
559
|
+
|
|
560
|
+
If you get import errors after installation:
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
# Rebuild everything (Swift dylib + Cython extension)
|
|
564
|
+
pip install --force-reinstall --no-cache-dir -e .
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Compilation errors
|
|
568
|
+
|
|
569
|
+
Ensure you have Xcode command line tools:
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
xcode-select --install
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
If the Swift build fails during installation:
|
|
576
|
+
|
|
577
|
+
1. Verify macOS version: `sw_vers -productVersion` (should be 26.0+)
|
|
578
|
+
2. Check Swift compiler: `swiftc --version`
|
|
579
|
+
3. Clean and reinstall: `pip install --force-reinstall --no-cache-dir -e .`
|
|
580
|
+
|
|
581
|
+
## License
|
|
582
|
+
|
|
583
|
+
MIT License - see LICENSE file for details
|
|
584
|
+
|
|
585
|
+
## Contributing
|
|
586
|
+
|
|
587
|
+
Contributions are welcome! Please:
|
|
588
|
+
|
|
589
|
+
1. Fork the repository
|
|
590
|
+
2. Create a feature branch
|
|
591
|
+
3. Add tests for new functionality
|
|
592
|
+
4. Ensure all tests pass
|
|
593
|
+
5. Submit a pull request
|
|
594
|
+
|
|
595
|
+
## Links
|
|
596
|
+
|
|
597
|
+
- [FoundationModels Framework](https://developer.apple.com/documentation/FoundationModels)
|
|
598
|
+
- [Apple Intelligence Documentation](https://developer.apple.com/apple-intelligence/)
|
|
599
|
+
- [Issue Tracker](https://github.com/btucker/apple-foundation-models-py/issues)
|
|
600
|
+
|
|
601
|
+
## Acknowledgments
|
|
602
|
+
|
|
603
|
+
This project was inspired by and learned from several excellent works:
|
|
604
|
+
|
|
605
|
+
- **[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.
|
|
606
|
+
|
|
607
|
+
- **[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,18 @@
|
|
|
1
|
+
apple_foundation_models-0.1.9.dist-info/licenses/LICENSE,sha256=W2K9kU7tXVwGlNefzUJxZwENt8wDeJ8yoG4kCTXzQBI,1072
|
|
2
|
+
applefoundationmodels/__init__.py,sha256=xFnFDBzTMwT6TNdNUDDmeE3QKXAECeahBPU99Bv_ruU,2433
|
|
3
|
+
applefoundationmodels/_foundationmodels.cpython-311-darwin.so,sha256=2Yjugm7hheDs-O2Ktaeb34dB3yZAJxW1tRPUJMNdR5k,175560
|
|
4
|
+
applefoundationmodels/_foundationmodels.pyi,sha256=eH5BT64JP11ZdaI2XReVPJSSCsT1IJCjOBh8lAykzTM,1412
|
|
5
|
+
applefoundationmodels/base.py,sha256=6IgmgcfMAhCD0qV_DgMW1hB68RPRKAnO-9cklt6fN_Y,873
|
|
6
|
+
applefoundationmodels/client.py,sha256=keYJMdOwz3kYYvJAyLaP5W9KNrgrNADU9nA5wY96Ujo,7040
|
|
7
|
+
applefoundationmodels/constants.py,sha256=2JsMVESQHp4k5SSqayPofx1l6oXtV-ycD33Tc4BiGtI,901
|
|
8
|
+
applefoundationmodels/exceptions.py,sha256=pZWthmqk_Njwl1foklo3NQ_Y8e_0JAKxEOD6g3uTbXQ,2909
|
|
9
|
+
applefoundationmodels/libfoundation_models.dylib,sha256=fKveZ0g_zfZuI3U7LuycMyovxcpfxavubJSFS17h5c0,164040
|
|
10
|
+
applefoundationmodels/py.typed,sha256=ixa8YukDZ3kLo0WsFJRGohLMyHzbMur1ALmmASML2cs,64
|
|
11
|
+
applefoundationmodels/pydantic_compat.py,sha256=ziHVapALsQZcAUFVrp8pB5CHWnPPo7g-Yc70vRTJGkY,3714
|
|
12
|
+
applefoundationmodels/session.py,sha256=spBz3lmZ0EQKjRTs6SV4l_NzZUH3xfKPd9Nz4_VweFY,15076
|
|
13
|
+
applefoundationmodels/tools.py,sha256=tMXRr4CT9jinNK4SNY8DyBgyp3cznyAv8nvgZVkulmY,8456
|
|
14
|
+
applefoundationmodels/types.py,sha256=flnk9PiTeUU91firo4RdWewsgiujfwp5V1uBDze1xxI,4959
|
|
15
|
+
apple_foundation_models-0.1.9.dist-info/METADATA,sha256=CRFKI9kWt-CwSfgfVM5aNKpd52GZPOZSty8aDU57Tg0,17987
|
|
16
|
+
apple_foundation_models-0.1.9.dist-info/WHEEL,sha256=qxQkdhERtGxJzqnVOBnucx1aUmU2n3HmuzYdln_LyOw,109
|
|
17
|
+
apple_foundation_models-0.1.9.dist-info/top_level.txt,sha256=6PGWTiHgh5vWT-cjbJImnO-ZJPKiKiGA-PGk2SJndYw,22
|
|
18
|
+
apple_foundation_models-0.1.9.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
|