Skip to main content

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 for support 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 failed
  • NotAvailableError - Apple Intelligence not available
  • InvalidParametersError - Invalid parameters
  • MemoryError - Memory allocation failed
  • JSONParseError - JSON parsing error
  • GenerationError - Text generation failed
  • TimeoutError - Operation timeout
  • SessionNotFoundError - Session not found
  • StreamNotFoundError - Stream not found
  • GuardrailViolationError - Content blocked by safety filters
  • ToolNotFoundError - Tool not registered
  • ToolExecutionError - Tool execution failed
  • UnknownError - Unknown error

Examples

See the examples/ directory for complete working examples:

  • basic_chat.py - Simple conversation
  • streaming_chat.py - Async streaming
  • tool_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:

  1. Ensure you're running macOS 26.0 (Sequoia) or later
  2. Check System Settings → Apple Intelligence → Enable
  3. 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:

  1. Verify macOS version: sw_vers -productVersion (should be 26.0+)
  2. Check Swift compiler: swiftc --version
  3. Clean and reinstall: pip install --force-reinstall --no-cache-dir -e .

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apple_foundation_models-0.1.0.tar.gz (108.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl (179.1 kB view details)

Uploaded CPython 3.11macOS 10.9+ universal2 (ARM64, x86-64)

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

Hashes for apple_foundation_models-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0465e7348246f57b71a40701b712654fdaf1f1b5f4676a49e8405974d101ad5e
MD5 b6d9c91bfad5f7bae70d3da0cb7360aa
BLAKE2b-256 21a36ac9f1def0191213edff879129d9382ca6da65ed8266a261ee16584722aa

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for apple_foundation_models-0.1.0-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 9aacf460e4139dd726e5999e84d9448300b26102cf34f5b580956a34b5ec2751
MD5 43f211e9f38071c1f3149141e398390d
BLAKE2b-256 e9dd566cd3b6e4fc69a99b4ecaec4458ff0da6ec07077582f47e5da760558600

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page