grammargem/scripts/build.sh
BigMarc 8a9ffb55af Add real on-device LLM (MLX) for rewrite / tone / Ask
Replace the MLXEngine stub with real Qwen2.5-3B-4bit inference via
mlx-swift-examples (2.25.9): loads the local model snapshot on disk (no
network), chat-templated generation per AIAction, and tone classification.
Wired behind the existing AIEngine seam; AppState resolves the model dir
through ModelManager.

Build notes:
- arm64-only now (MLX is Metal / Apple-Silicon only; matches the spec).
- swift build (CLI) cannot compile mlx-swift's .metal kernels, so the
  metallib is generated once via scripts/build-metallib.sh (xcodebuild) and
  vendored at AppSupport/mlx.metallib; scripts/build.sh ships it as
  Contents/Resources/mlx-swift_Cmlx.bundle/Contents/Resources/default.metallib
  (the path mlx resolves, and it codesigns cleanly).
- A hidden --mlx-selftest entrypoint runs one headless generation to verify
  the runtime end-to-end.

Verified: the signed app's self-test produces a real rewrite; full test
suite green (the MLX integration test is gated behind GRAMMAGEM_MLX_TEST).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 07:34:02 +02:00

51 lines
2.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build a runnable GrammaGem.app bundle (arm64 / Apple Silicon — the MLX
# on-device LLM runtime is Metal-only, so Intel is not supported).
# Usage: ./scripts/build.sh (run from the mac/ directory)
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
CONFIG="release"
APP="GrammaGem.app"
DIST="dist"
echo "==> Building Harper static lib (universal)"
chmod +x harper-ffi/build.sh
./harper-ffi/build.sh
echo "==> Compiling (arm64 / Apple Silicon, ${CONFIG})"
# arm64-only: the MLX on-device LLM runtime is Apple-Silicon / Metal only.
swift build -c "${CONFIG}" --arch arm64
BIN="$(swift build -c "${CONFIG}" --arch arm64 --show-bin-path)"
echo "==> Assembling ${APP}"
rm -rf "${DIST}/${APP}"
mkdir -p "${DIST}/${APP}/Contents/MacOS" "${DIST}/${APP}/Contents/Resources"
cp "${BIN}/GrammaGem" "${DIST}/${APP}/Contents/MacOS/GrammaGem"
cp "AppSupport/Info.plist" "${DIST}/${APP}/Contents/Info.plist"
cp "AppSupport/AppIcon.icns" "${DIST}/${APP}/Contents/Resources/AppIcon.icns"
# SwiftPM resource bundles (tokenizer data, etc.) go in Contents/Resources so
# Bundle.module resolves via the main bundle and the signature stays valid.
shopt -s nullglob
for b in "${BIN}"/*.bundle; do
cp -R "$b" "${DIST}/${APP}/Contents/Resources/"
done
shopt -u nullglob
# MLX Metal kernels: `swift build` (CLI) does NOT compile mlx-swift's .metal
# sources, so we ship a prebuilt metallib in the exact bundle mlx looks for —
# Contents/Resources/mlx-swift_Cmlx.bundle/Contents/Resources/default.metallib —
# which mlx finds via the main bundle's resources and which codesigns cleanly.
# Regenerate AppSupport/mlx.metallib with: ./scripts/build-metallib.sh
MLX_RES="${DIST}/${APP}/Contents/Resources/mlx-swift_Cmlx.bundle/Contents/Resources"
mkdir -p "${MLX_RES}"
cp "AppSupport/mlx.metallib" "${MLX_RES}/default.metallib"
echo "==> Built ${DIST}/${APP}"
lipo -info "${DIST}/${APP}/Contents/MacOS/GrammaGem"
echo " Run with: open \"${DIST}/${APP}\" (grant Accessibility on first launch)"