Python bindings for Apple's FoundationModels framework - on-device AI
Project description
apple-foundation-models-py
Python bindings for Apple's FoundationModels framework - Direct access to on-device Apple Intelligence.
Features
- High-level Pythonic API: Context managers, async/await, type hints
- Async Streaming: Native
async forsupport for streaming responses - Type Safety: Full type annotations with mypy support
- Memory Safe: Automatic resource cleanup, no manual memory management
- Thread Safe: All operations are thread-safe
Requirements
- macOS 26.0+ (macOS Sequoia or later)
- Python 3.8 or higher
- Apple Intelligence enabled on your device
Installation
From PyPI
pip install apple-foundation-models
From Source
# Clone the repository
git clone https://github.com/btucker/apple-foundation-models-py.git
cd apple-foundation-models-py
# Install (automatically builds Swift dylib and Cython extension)
pip install -e .
Requirements:
- macOS 26.0+ (Sequoia) with Apple Intelligence enabled
- Xcode command line tools (
xcode-select --install) - Python 3.8 or higher
Note: The Swift dylib is built automatically during installation.
Quick Start
Basic Usage
from applefoundationmodels import Client
# Create a client (library auto-initializes)
with Client() as client:
# Check if Apple Intelligence is available
if not client.is_ready():
print("Apple Intelligence is not available")
print(client.get_availability_reason())
return
# Create a session
session = client.create_session(
instructions="You are a helpful assistant.",
enable_guardrails=True
)
# Generate a response
response = session.generate("What is the capital of France?")
print(response)
# Get conversation history
history = session.get_history()
for msg in history:
print(f"{msg['role']}: {msg['content']}")
Async Streaming
import asyncio
from applefoundationmodels import Client
async def main():
with Client() as client:
session = client.create_session()
# Stream response chunks as they arrive
async for chunk in session.generate_stream("Tell me a story about a robot"):
print(chunk, end='', flush=True)
print() # Newline after stream
asyncio.run(main())
Structured Output
from applefoundationmodels import Client
with Client() as client:
session = client.create_session()
# Define a JSON schema
schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"city": {"type": "string"}
},
"required": ["name", "age", "city"]
}
# Generate structured response
result = session.generate_structured(
"Extract person info: Alice is 28 and lives in Paris",
schema=schema
)
print(result['object']) # {'name': 'Alice', 'age': 28, 'city': 'Paris'}
Generation Parameters
# Control generation with parameters
response = session.generate(
"Write a creative story",
temperature=1.5, # Higher = more creative (0.0-2.0)
max_tokens=500, # Limit response length
seed=42 # Reproducible outputs
)
Session Management
with Client() as client:
# Create multiple sessions
chat_session = client.create_session(
instructions="You are a friendly chatbot"
)
code_session = client.create_session(
instructions="You are a code review assistant"
)
# Each session maintains separate conversation history
chat_response = chat_session.generate("Hello!")
code_response = code_session.generate("Review this code: ...")
# Clear history while keeping session
chat_session.clear_history()
# Manually add messages
chat_session.add_message("system", "Be concise")
Statistics
with Client() as client:
session = client.create_session()
# Generate some responses
for i in range(5):
session.generate(f"Question {i}")
# Get statistics
stats = client.get_stats()
print(f"Total requests: {stats['total_requests']}")
print(f"Success rate: {stats['successful_requests'] / stats['total_requests'] * 100:.1f}%")
print(f"Avg response time: {stats['average_response_time']:.2f}s")
# Reset statistics
client.reset_stats()
API Reference
Client
The main entry point for using libai.
class Client:
def __init__() -> None: ...
def __enter__() -> Client: ...
def __exit__(...) -> None: ...
@staticmethod
def check_availability() -> Availability: ...
@staticmethod
def get_availability_reason() -> str: ...
@staticmethod
def is_ready() -> bool: ...
@staticmethod
def get_version() -> str: ...
@staticmethod
def get_supported_languages() -> List[str]: ...
def create_session(...) -> Session: ...
def get_stats() -> Stats: ...
def reset_stats() -> None: ...
def close() -> None: ...
Session
Manages conversation state and text generation.
class Session:
def __enter__() -> Session: ...
def __exit__(...) -> None: ...
def generate(prompt: str, **params) -> str: ...
def generate_structured(prompt: str, schema: dict, **params) -> dict: ...
async def generate_stream(prompt: str, **params) -> AsyncIterator[str]: ...
def get_history() -> List[dict]: ...
def clear_history() -> None: ...
def add_message(role: str, content: str) -> None: ...
def close() -> None: ...
Types
class Availability(IntEnum):
AVAILABLE = 1
DEVICE_NOT_ELIGIBLE = -1
NOT_ENABLED = -2
MODEL_NOT_READY = -3
class SessionConfig(TypedDict):
instructions: Optional[str]
tools_json: Optional[str]
enable_guardrails: bool
prewarm: bool
class GenerationParams(TypedDict):
temperature: float
max_tokens: int
seed: int
class Stats(TypedDict):
total_requests: int
successful_requests: int
failed_requests: int
total_tokens_generated: int
average_response_time: float
total_processing_time: float
Exceptions
All exceptions inherit from FoundationModelsError:
InitializationError- Library initialization failedNotAvailableError- Apple Intelligence not availableInvalidParametersError- Invalid parametersMemoryError- Memory allocation failedJSONParseError- JSON parsing errorGenerationError- Text generation failedTimeoutError- Operation timeoutSessionNotFoundError- Session not foundStreamNotFoundError- Stream not foundGuardrailViolationError- Content blocked by safety filtersToolNotFoundError- Tool not registeredToolExecutionError- Tool execution failedUnknownError- Unknown error
Examples
See the examples/ directory for complete working examples:
basic_chat.py- Simple conversationstreaming_chat.py- Async streamingtool_calling.py- Tool registration (coming soon)structured_output.py- JSON schema validation
Development
Building from Source
# Install development dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Type checking
mypy applefoundationmodels
# Format code
black applefoundationmodels examples
Project Structure
apple-foundation-models-py/
├── applefoundationmodels/ # Python package
│ ├── __init__.py # Public API
│ ├── _foundationmodels.pyx # Cython bindings
│ ├── _foundationmodels.pxd # C declarations
│ ├── client.py # High-level Client
│ ├── session.py # Session management
│ ├── types.py # Type definitions
│ ├── exceptions.py # Exception classes
│ └── swift/ # Swift FoundationModels bindings
│ ├── foundation_models.swift # Swift implementation
│ └── foundation_models.h # C FFI header
├── lib/ # Swift dylib and modules (auto-generated)
│ └── libfoundation_models.dylib # Compiled Swift library
├── examples/ # Example scripts
└── tests/ # Unit tests
Architecture
apple-foundation-models-py uses a layered architecture for optimal performance:
Python API (client.py, session.py)
↓
Cython FFI (_foundationmodels.pyx)
↓
C FFI Layer (foundation_models.h)
↓
Swift Implementation (foundation_models.swift)
↓
FoundationModels Framework (Apple Intelligence)
Key Design Decisions:
- Direct FoundationModels Integration: No intermediate C library - Swift calls FoundationModels directly
- Minimal Overhead: C FFI layer provides thin wrapper for Python/Swift communication
- Async Coordination: Uses semaphores to bridge Swift's async/await with synchronous C calls
- Streaming: Real-time delta calculation from FoundationModels snapshot-based streaming
Performance
- Cython-compiled for near-C performance
- Direct Swift → FoundationModels calls (no intermediate libraries)
- Async streaming with delta-based chunk delivery
- No GIL during Swift library calls (when possible)
Troubleshooting
Apple Intelligence not available
If you get NotAvailableError:
- Ensure you're running macOS 26.0 (Sequoia) or later
- Check System Settings → Apple Intelligence → Enable
- Wait for models to download (check with
client.get_availability_reason())
Import errors
If you get import errors after installation:
# Rebuild everything (Swift dylib + Cython extension)
pip install --force-reinstall --no-cache-dir -e .
Compilation errors
Ensure you have Xcode command line tools:
xcode-select --install
If the Swift build fails during installation:
- Verify macOS version:
sw_vers -productVersion(should be 26.0+) - Check Swift compiler:
swiftc --version - Clean and reinstall:
pip install --force-reinstall --no-cache-dir -e .
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Links
Acknowledgments
This project was inspired by and learned from several excellent works:
-
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.
-
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file apple_foundation_models-0.1.0.tar.gz.
File metadata
- Download URL: apple_foundation_models-0.1.0.tar.gz
- Upload date:
- Size: 108.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0465e7348246f57b71a40701b712654fdaf1f1b5f4676a49e8405974d101ad5e
|
|
| MD5 |
b6d9c91bfad5f7bae70d3da0cb7360aa
|
|
| BLAKE2b-256 |
21a36ac9f1def0191213edff879129d9382ca6da65ed8266a261ee16584722aa
|
Provenance
The following attestation bundles were made for apple_foundation_models-0.1.0.tar.gz:
Publisher:
publish-to-pypi.yml on btucker/apple-foundation-models-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apple_foundation_models-0.1.0.tar.gz -
Subject digest:
0465e7348246f57b71a40701b712654fdaf1f1b5f4676a49e8405974d101ad5e - Sigstore transparency entry: 677050373
- Sigstore integration time:
-
Permalink:
btucker/apple-foundation-models-py@8d0d547c61a78ae20a3796095a077b322807dca2 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/btucker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8d0d547c61a78ae20a3796095a077b322807dca2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl.
File metadata
- Download URL: apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl
- Upload date:
- Size: 179.1 kB
- Tags: CPython 3.11, macOS 10.9+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9aacf460e4139dd726e5999e84d9448300b26102cf34f5b580956a34b5ec2751
|
|
| MD5 |
43f211e9f38071c1f3149141e398390d
|
|
| BLAKE2b-256 |
e9dd566cd3b6e4fc69a99b4ecaec4458ff0da6ec07077582f47e5da760558600
|
Provenance
The following attestation bundles were made for apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl:
Publisher:
publish-to-pypi.yml on btucker/apple-foundation-models-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl -
Subject digest:
9aacf460e4139dd726e5999e84d9448300b26102cf34f5b580956a34b5ec2751 - Sigstore transparency entry: 677050396
- Sigstore integration time:
-
Permalink:
btucker/apple-foundation-models-py@8d0d547c61a78ae20a3796095a077b322807dca2 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/btucker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8d0d547c61a78ae20a3796095a077b322807dca2 -
Trigger Event:
release
-
Statement type: