Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b396e8e
fix: include libfoundation_models.dylib in wheels and build for multi…
btucker Nov 7, 2025
e74a24a
chore: uv.lock update
btucker Nov 7, 2025
67801b6
Merge branch 'main' of github.com:btucker/apple-foundation-models-py …
btucker Nov 7, 2025
67ad97d
chore: bump version
btucker Nov 7, 2025
055e1be
ci: remove build-sdist
btucker Nov 7, 2025
d6b4981
fix: ensure dylib is included in wheel by building during build_py phase
btucker Nov 7, 2025
65de3c1
ci: set MACOSX_DEPLOYMENT_TARGET and ARCHFLAGS for wheel builds
btucker Nov 7, 2025
80bfb95
ci: force arm64 platform tag by setting _PYTHON_HOST_PLATFORM
btucker Nov 7, 2025
498d729
ci: use macOS 11.0 platform tag for PyPI compatibility
btucker Nov 7, 2025
cccb0c0
docs: add macOS 26.0+ requirement to package description
btucker Nov 7, 2025
2c22811
ci: adopt uv build for wheel building
btucker Nov 7, 2025
1ba5208
ci: properly build wheels for macOS 26.0 platform
btucker Nov 7, 2025
bd196ac
refactor: simplify package data configuration
btucker Nov 7, 2025
2bad884
docs: document uv as primary build tool in README
btucker Nov 7, 2025
1d099c3
fix: update Python version requirement to 3.9+
btucker Nov 7, 2025
0eb78dd
feat: add Python 3.14 support
btucker Nov 7, 2025
0502fdc
refactor: exclude Swift source files from wheels
btucker Nov 7, 2025
98b3c66
ci: enhance wheel verification error messages
btucker Nov 7, 2025
ed1b1c2
refactor: simplify setup.py
btucker Nov 7, 2025
c8c8f7f
ci: add comprehensive wheel testing
btucker Nov 7, 2025
5b41a69
refactor: move wheel testing to test workflow
btucker Nov 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 52 additions & 19 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,65 +10,98 @@ permissions:
id-token: write # Required for trusted publishing to PyPI

jobs:
build:
name: Build package on macOS
build-wheels:
name: Build wheels on macOS for Python ${{ matrix.python-version }}
runs-on: macos-26
strategy:
matrix:
# Build wheels for all supported Python versions
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
fail-fast: false

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: "3.11"
python-version: ${{ matrix.python-version }}

- name: Check macOS version
run: |
echo "macOS version:"
sw_vers
echo "Architecture:"
uname -m
echo "Python version:"
python --version

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build wheel setuptools Cython>=3.0.0
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Build package
- name: Build wheel
env:
MACOSX_DEPLOYMENT_TARGET: "26.0"
ARCHFLAGS: "-arch arm64"
_PYTHON_HOST_PLATFORM: "macosx-26.0-arm64"
run: |
python -m build
uv build --wheel

- name: Check build artifacts
- name: Verify wheel contents
run: |
echo "Build artifacts:"
ls -lh dist/
echo ""
echo "Verifying wheel contents:"
unzip -l dist/*.whl || true
echo "Checking for dylib in wheel:"
if unzip -l dist/*.whl | grep -q "libfoundation_models.dylib"; then
echo "✓ libfoundation_models.dylib found in wheel"
unzip -l dist/*.whl | grep -E "(dylib|\.so)"
else
echo "✗ ERROR: libfoundation_models.dylib NOT found in wheel!"
echo "Full wheel contents:"
unzip -l dist/*.whl
exit 1
fi

- name: Upload build artifacts
- name: Upload wheel
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
name: wheel-${{ matrix.python-version }}
path: dist/*.whl

publish:
name: Publish to PyPI
needs: build
needs: [build-wheels]
runs-on: ubuntu-latest

steps:
- name: Download build artifacts
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
merge-multiple: true

- name: Verify artifacts
run: |
echo "Downloaded artifacts:"
ls -lh dist/
echo ""
echo "Verifying each wheel contains dylib:"
for wheel in dist/*.whl; do
echo "Checking $wheel:"
ls -lh "$wheel"
if unzip -l "$wheel" | grep -q "libfoundation_models.dylib"; then
echo " ✓ dylib present"
else
echo " ✗ ERROR: dylib missing in $wheel!"
echo "Wheel contents:"
unzip -l "$wheel"
exit 1
fi
done

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
Expand Down
27 changes: 19 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
fail-fast: false

steps:
Expand All @@ -38,13 +38,20 @@ jobs:
echo "Architecture:"
uname -m

- name: Install dependencies and build extensions
- name: Build wheel
run: |
uv sync --extra dev
uv build --wheel

- name: Install wheel and test dependencies
run: |
uv venv .test-venv
source .test-venv/bin/activate
uv pip install dist/*.whl pytest pytest-asyncio pytest-cov

- name: Check Apple Intelligence availability
run: |
uv run python -c "
source .test-venv/bin/activate
python -c "
import applefoundationmodels as afm
status = afm.Client.check_availability()
reason = afm.Client.get_availability_reason()
Expand All @@ -54,15 +61,19 @@ jobs:
"
continue-on-error: true

- name: Run tests
- name: Run tests from installed wheel
run: |
# Tests will auto-skip if Apple Intelligence is unavailable
uv run pytest tests/ -v --tb=short -ra
source .test-venv/bin/activate
cd /tmp
pytest ${{ github.workspace }}/tests/ -v --tb=short -ra

- name: Run tests with coverage (Python 3.11 only)
if: matrix.python-version == '3.11'
run: |
uv run pytest tests/ --cov=applefoundationmodels --cov-report=xml --cov-report=term
source .test-venv/bin/activate
cd /tmp
pytest ${{ github.workspace }}/tests/ --cov=applefoundationmodels --cov-report=xml --cov-report=term
cp coverage.xml ${{ github.workspace }}/

- name: Upload coverage reports
if: matrix.python-version == '3.11'
Expand Down
23 changes: 6 additions & 17 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
# Include all necessary files in source distribution

# Swift source files
include applefoundationmodels/swift/*.swift
include applefoundationmodels/swift/*.h

# Dynamic library
include lib/*.dylib
include lib/*.swiftmodule

# Cython source files
include applefoundationmodels/*.pyx
include applefoundationmodels/*.pxd

# Type stub marker
include applefoundationmodels/py.typed
# This file is for sdist builds only (not used for wheels)
# Wheels use pyproject.toml [tool.setuptools.package-data] instead

# Documentation
include README.md
include LICENSE

# Exclude unnecessary files
# Exclude build artifacts and source files from wheels
global-exclude *.pyc
global-exclude __pycache__
global-exclude *.so
global-exclude .DS_Store
global-exclude *.a
global-exclude *.swift
global-exclude *.h
global-exclude lib/*
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Python bindings for Apple's FoundationModels framework - Direct access to on-dev
## Requirements

- macOS 26.0+ (macOS Sequoia or later)
- Python 3.8 or higher
- Python 3.9 or higher
- Apple Intelligence enabled on your device

## Installation
Expand Down Expand Up @@ -47,7 +47,7 @@ pip install -e .

- macOS 26.0+ (Sequoia) with Apple Intelligence enabled
- Xcode command line tools (`xcode-select --install`)
- Python 3.8 or higher
- Python 3.9 or higher

**Note:** The Swift dylib is built automatically during installation.

Expand Down Expand Up @@ -320,17 +320,34 @@ See the `examples/` directory for complete working examples:

### Building from Source

This project uses [uv](https://docs.astral.sh/uv/) for fast, reliable builds and dependency management:

```bash
# Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install development dependencies
pip install -e ".[dev]"
uv sync --extra dev

# Run tests
pytest
uv run pytest

# Type checking
mypy applefoundationmodels
uv run mypy applefoundationmodels

# Format code
uv run black applefoundationmodels examples

# Build wheels
uv build --wheel
```

You can also use pip if preferred:

```bash
pip install -e ".[dev]"
pytest
mypy applefoundationmodels
black applefoundationmodels examples
```

Expand Down
17 changes: 11 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"

[project]
name = "apple-foundation-models"
version = "0.1.3"
description = "Python bindings for Apple's FoundationModels framework - on-device AI"
version = "0.1.4"
description = "Python bindings for Apple's FoundationModels framework - on-device AI (requires macOS 26.0+)"
readme = "README.md"
license = { text = "MIT" }
authors = [{ name = "Ben Tucker" }]
Expand All @@ -24,16 +24,17 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Cython",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = ["typing-extensions>=4.0.0"]

[project.optional-dependencies]
Expand All @@ -48,10 +49,14 @@ Issues = "https://github.com/btucker/apple-foundation-models-py/issues"

[tool.setuptools]
packages = ["applefoundationmodels"]
include-package-data = true

[tool.setuptools.package-data]
applefoundationmodels = ["py.typed", "*.pxd"]
applefoundationmodels = [
"py.typed",
"*.pxd",
"*.pyx",
"*.dylib",
]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
Loading
Loading