Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
190 changes: 190 additions & 0 deletions .github/workflows/muse-glimmer-macos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

name: Muse Glimmer macOS

on:
pull_request:
paths:
- "muse_glimmer/macos/**"
- ".github/workflows/muse-glimmer-macos.yml"
push:
branches: [main]
paths:
- "muse_glimmer/macos/**"
- ".github/workflows/muse-glimmer-macos.yml"

permissions:
contents: read

jobs:
python:
runs-on: macos-14
defaults:
run:
working-directory: muse_glimmer/macos
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.13"
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e
with:
enable-cache: true
cache-dependency-glob: muse_glimmer/macos/uv.lock
- name: Sync Python dependencies
run: uv sync --all-packages --all-groups --frozen
- name: Check Python lint
run: uv run --all-packages --all-groups ruff check .
- name: Check Python formatting
run: uv run --all-packages --all-groups ruff format --check .
- name: Run Python tests
run: uv run --all-packages --all-groups pytest
- name: Read release compatibility state
id: compatibility
run: |
uv run --all-packages --all-groups python - <<'PY' >> "$GITHUB_OUTPUT"
import json
import re
from pathlib import Path

compatibility = json.loads(
Path("config/dependencies/compatibility.lock.json").read_text()
)
ready = bool(compatibility["ready_for_release"])
commit = compatibility["executorch"]["commit"] or ""
if ready and re.fullmatch(r"[0-9a-f]{40}", commit) is None:
raise SystemExit("release-ready compatibility requires a full commit SHA")
print(f"ready={str(ready).lower()}")
print(f"commit={commit}")
PY
- name: Check out release ExecuTorch revision
if: steps.compatibility.outputs.ready == 'true'
env:
EXECUTORCH_COMMIT: ${{ steps.compatibility.outputs.commit }}
run: |
test -n "$EXECUTORCH_COMMIT"
git clone --filter=blob:none --no-checkout \
https://github.com/pytorch/executorch.git \
"$RUNNER_TEMP/executorch"
git -C "$RUNNER_TEMP/executorch" checkout --detach "$EXECUTORCH_COMMIT"
echo "GLIMMER_EXECUTORCH_ROOT=$RUNNER_TEMP/executorch" >> "$GITHUB_ENV"
- name: Validate manifests
run: uv run --all-packages --all-groups python -m scripts.validate_manifests
- name: Check publication contents
run: uv run --all-packages --all-groups python -m scripts.publication_check
- name: Build Python packages
run: uv build --all-packages --out-dir "$RUNNER_TEMP/muse-glimmer-dist"
- name: Verify package LICENSE payloads
env:
DIST_DIR: ${{ runner.temp }}/muse-glimmer-dist
run: |
uv run --all-packages --all-groups python - <<'PY'
import os
import tarfile
import zipfile
from pathlib import Path

dist = Path(os.environ["DIST_DIR"])
expected = {
"livekit_plugins_executorch": Path(
"packages/livekit-plugins-executorch/LICENSE"
).read_bytes(),
"muse_glimmer_token_service": Path("apps/token-service/LICENSE").read_bytes(),
"muse_glimmer_worker": Path("apps/worker/LICENSE").read_bytes(),
}
archives = list(dist.glob("*.whl")) + list(dist.glob("*.tar.gz"))
for package, license_payload in expected.items():
package_archives = [archive for archive in archives if archive.name.startswith(package)]
if len(package_archives) != 2:
raise SystemExit(
f"expected wheel and sdist for {package}, found {len(package_archives)} archives"
)
for archive in package_archives:
if archive.suffix == ".whl":
with zipfile.ZipFile(archive) as built:
names = built.namelist()
payloads = [
built.read(name)
for name in names
if Path(name).name == "LICENSE"
]
else:
with tarfile.open(archive, mode="r:gz") as built:
members = built.getmembers()
names = [member.name for member in members]
payloads = [
built.extractfile(member).read()
for member in members
if member.isfile() and Path(member.name).name == "LICENSE"
]
if license_payload not in payloads:
raise SystemExit(f"source LICENSE payload missing from {archive.name}")
if any(Path(name).name == "NOTICE" for name in names):
raise SystemExit(f"obsolete NOTICE payload found in {archive.name}")
print("Verified BSD LICENSE payloads in all Python package archives.")
PY

web:
runs-on: macos-14
defaults:
run:
working-directory: muse_glimmer/macos/apps/web
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with:
node-version: "22.12.0"
cache: npm
cache-dependency-path: muse_glimmer/macos/apps/web/package-lock.json
- name: Install web dependencies
run: npm ci
- name: Lint web app
run: npm run lint
- name: Type-check web app
run: npm run typecheck
- name: Test web app
run: npm test
- name: Build web app
run: npm run build
- name: Audit web dependencies
run: npm audit --audit-level=high
- name: Scan browser bundle for private runtime data
run: |
node --input-type=module <<'JS'
import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";

const forbidden = new Map([
["LLM port", /127\.0\.0\.1:8000/],
["model variant", /muse-glimmer-k-quant|17G|128K|dflash/i],
["model runtime", /Parakeet|Supertonic|MUSE_GLIMMER_|PARAKEET_|SUPERTONIC_/],
["private path", /\/Users\/|\.local\/artifacts|\.pte\b/],
["credential name", /LIVEKIT_API_SECRET/],
["cloud URL", /wss:\/\/|https:\/\/[^\s"']*livekit/i],
]);

async function* files(directory) {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name);
if (entry.isDirectory()) {
yield* files(path);
} else if (entry.isFile()) {
yield path;
}
}
}

for await (const path of files("dist")) {
const contents = await readFile(path, "utf8");
for (const [label, pattern] of forbidden) {
if (pattern.test(contents)) {
throw new Error(`browser bundle contains forbidden ${label}: ${path}`);
}
}
}
console.log("Browser bundle privacy scan passed.");
JS
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
Example apps and demos using PyTorch's [ExecuTorch](https://github.com/pytorch/executorch) framework.

## Full applications

- [Muse Glimmer Voice Agent for macOS](muse_glimmer/macos/README.md): a fully local
Apple silicon voice app whose native setup and release remain development-gated
pending a compatible pinned ExecuTorch commit.

## License

ExecuTorch is BSD licensed, as found in the LICENSE file.
54 changes: 54 additions & 0 deletions muse_glimmer/macos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Local configuration and credentials
.env
.env.*
!.env.example
*.key
*.keys
*.pem
*.token

# Managed local state
.local/

# Python
.venv/
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
.mypy_cache/
.ruff_cache/
.coverage
htmlcov/
dist/
build/

# Node
node_modules/
*.tsbuildinfo
coverage/

# Native/model artifacts
*.pte
*.pt
*.ptd
*.onnx
*.safetensors
*.gguf
*.bin
*.dylib
*.so
*.a
*.o
*.wav
*.pcm
*.metallib

# Runtime data
*.log
*.pid
*.sock
recordings/
reports/
museglimmer-reports/
.DS_Store
1 change: 1 addition & 0 deletions muse_glimmer/macos/.node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.12.0
1 change: 1 addition & 0 deletions muse_glimmer/macos/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
16 changes: 16 additions & 0 deletions muse_glimmer/macos/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Contributing

This subtree follows the repository contribution requirements in
[`../../CONTRIBUTING.md`](../../CONTRIBUTING.md), including its licensing and
Contributor License Agreement terms. The guidance below is specific to the
local-only, source-only macOS example.

- Do not commit models, native binaries, credentials, recordings, logs, caches,
generated output, or another repository.
- Keep browser-visible data within the policy documented in
`docs/security-model.md`.
- Keep ASR, LLM, and TTS native components on one pinned ExecuTorch revision.
- Preserve MuseGlimmer reasoning configuration under
`chat_template_kwargs.reasoning_strength`; do not use `reasoning_effort`.
- Run `make check`, `make test`, and `make publication-check` before opening a
pull request.
30 changes: 30 additions & 0 deletions muse_glimmer/macos/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
BSD License

For "ExecuTorch" software

Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name Meta nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Loading
Loading