violawake 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. violawake-0.1.0/.dockerignore +41 -0
  2. violawake-0.1.0/.github/workflows/ci.yml +204 -0
  3. violawake-0.1.0/.github/workflows/console-ci.yml +124 -0
  4. violawake-0.1.0/.github/workflows/model-verify.yml +89 -0
  5. violawake-0.1.0/.github/workflows/release.yml +179 -0
  6. violawake-0.1.0/.gitignore +111 -0
  7. violawake-0.1.0/.pre-commit-config.yaml +17 -0
  8. violawake-0.1.0/CHANGELOG.md +43 -0
  9. violawake-0.1.0/CONTRIBUTING.md +185 -0
  10. violawake-0.1.0/LICENSE +161 -0
  11. violawake-0.1.0/PKG-INFO +644 -0
  12. violawake-0.1.0/README.md +389 -0
  13. violawake-0.1.0/RELEASE_NOTES.md +36 -0
  14. violawake-0.1.0/SECURITY.md +13 -0
  15. violawake-0.1.0/docs/ARCHITECTURE.md +594 -0
  16. violawake-0.1.0/docs/PRD.md +331 -0
  17. violawake-0.1.0/docs/PRE_LAUNCH_CHECKLIST.md +159 -0
  18. violawake-0.1.0/docs/REGISTRY.md +73 -0
  19. violawake-0.1.0/docs/ROADMAP_10_OF_10.md +539 -0
  20. violawake-0.1.0/docs/S1.3_REQUIREMENTS_SYNTHESIS.md +102 -0
  21. violawake-0.1.0/docs/TEST_STRATEGY.md +341 -0
  22. violawake-0.1.0/docs/adr/ADR-001-onnx-runtime.md +149 -0
  23. violawake-0.1.0/docs/adr/ADR-002-oww-feature-extractor.md +156 -0
  24. violawake-0.1.0/docs/adr/ADR-003-python-first.md +146 -0
  25. violawake-0.1.0/docs/adr/ADR-004-open-core.md +182 -0
  26. violawake-0.1.0/docs/adr/ADR-005-packaging.md +229 -0
  27. violawake-0.1.0/docs/api/README.md +40 -0
  28. violawake-0.1.0/examples/.gitkeep +1 -0
  29. violawake-0.1.0/examples/async_detection.py +38 -0
  30. violawake-0.1.0/examples/basic_detection.py +29 -0
  31. violawake-0.1.0/examples/streaming_eval.py +16 -0
  32. violawake-0.1.0/pyproject.toml +257 -0
  33. violawake-0.1.0/python +0 -0
  34. violawake-0.1.0/src/violawake/__init__.py +4 -0
  35. violawake-0.1.0/src/violawake_sdk/__init__.py +143 -0
  36. violawake-0.1.0/src/violawake_sdk/_constants.py +178 -0
  37. violawake-0.1.0/src/violawake_sdk/_exceptions.py +40 -0
  38. violawake-0.1.0/src/violawake_sdk/async_detector.py +145 -0
  39. violawake-0.1.0/src/violawake_sdk/audio.py +334 -0
  40. violawake-0.1.0/src/violawake_sdk/audio_source.py +452 -0
  41. violawake-0.1.0/src/violawake_sdk/backends/__init__.py +99 -0
  42. violawake-0.1.0/src/violawake_sdk/backends/base.py +129 -0
  43. violawake-0.1.0/src/violawake_sdk/backends/onnx_backend.py +101 -0
  44. violawake-0.1.0/src/violawake_sdk/backends/tflite_backend.py +467 -0
  45. violawake-0.1.0/src/violawake_sdk/cli/__init__.py +7 -0
  46. violawake-0.1.0/src/violawake_sdk/cli/download.py +6 -0
  47. violawake-0.1.0/src/violawake_sdk/cli/evaluate.py +6 -0
  48. violawake-0.1.0/src/violawake_sdk/cli/train.py +63 -0
  49. violawake-0.1.0/src/violawake_sdk/confidence.py +135 -0
  50. violawake-0.1.0/src/violawake_sdk/ensemble.py +230 -0
  51. violawake-0.1.0/src/violawake_sdk/models.py +592 -0
  52. violawake-0.1.0/src/violawake_sdk/noise_profiler.py +202 -0
  53. violawake-0.1.0/src/violawake_sdk/oww_backbone.py +272 -0
  54. violawake-0.1.0/src/violawake_sdk/pipeline.py +410 -0
  55. violawake-0.1.0/src/violawake_sdk/power_manager.py +262 -0
  56. violawake-0.1.0/src/violawake_sdk/py.typed +0 -0
  57. violawake-0.1.0/src/violawake_sdk/security/__init__.py +32 -0
  58. violawake-0.1.0/src/violawake_sdk/security/cert_pinning.py +792 -0
  59. violawake-0.1.0/src/violawake_sdk/speaker.py +399 -0
  60. violawake-0.1.0/src/violawake_sdk/stt.py +307 -0
  61. violawake-0.1.0/src/violawake_sdk/stt_engine.py +132 -0
  62. violawake-0.1.0/src/violawake_sdk/training/__init__.py +1 -0
  63. violawake-0.1.0/src/violawake_sdk/training/augment.py +621 -0
  64. violawake-0.1.0/src/violawake_sdk/training/evaluate.py +686 -0
  65. violawake-0.1.0/src/violawake_sdk/training/losses.py +102 -0
  66. violawake-0.1.0/src/violawake_sdk/training/temporal_model.py +302 -0
  67. violawake-0.1.0/src/violawake_sdk/training/weight_averaging.py +279 -0
  68. violawake-0.1.0/src/violawake_sdk/tts.py +307 -0
  69. violawake-0.1.0/src/violawake_sdk/tts_engine.py +21 -0
  70. violawake-0.1.0/src/violawake_sdk/vad.py +378 -0
  71. violawake-0.1.0/src/violawake_sdk/vad_engine.py +9 -0
  72. violawake-0.1.0/src/violawake_sdk/wake_detector.py +1156 -0
@@ -0,0 +1,41 @@
1
+ # Git
2
+ .git
3
+ .gitignore
4
+
5
+ # Node (frontend builds have their own Dockerfile)
6
+ console/frontend/node_modules
7
+ console/frontend/dist
8
+
9
+ # Python caches
10
+ __pycache__
11
+ *.pyc
12
+ *.pyo
13
+ .pytest_cache
14
+ .ruff_cache
15
+ .coverage
16
+ .benchmarks
17
+ *.egg-info
18
+
19
+ # IDE / editor
20
+ .vscode
21
+ .idea
22
+ *.swp
23
+
24
+ # Environment and secrets
25
+ .env
26
+ .env.*
27
+ !.env.example
28
+
29
+ # Docs and non-runtime files
30
+ docs/
31
+ *.md
32
+ LICENSE
33
+ CLAUDE.md
34
+
35
+ # Test data (large)
36
+ data/
37
+ models/*.onnx
38
+
39
+ # OS
40
+ Thumbs.db
41
+ .DS_Store
@@ -0,0 +1,204 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main, develop]
6
+ tags:
7
+ - "v*.*.*"
8
+ pull_request:
9
+ branches: [master, main]
10
+ schedule:
11
+ - cron: "0 2 * * *" # 2am UTC nightly
12
+
13
+ concurrency:
14
+ group: ci-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ env:
18
+ PYTHON_DEFAULT: "3.11"
19
+
20
+ jobs:
21
+ # Job 1: Lint (ruff + mypy)
22
+ lint:
23
+ name: Lint
24
+ runs-on: ubuntu-22.04
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Set up Python
29
+ uses: actions/setup-python@v5
30
+ with:
31
+ python-version: ${{ env.PYTHON_DEFAULT }}
32
+ cache: pip
33
+
34
+ - name: Install lint tools
35
+ # Only install lint tools — no pyaudio/portaudio needed for static analysis
36
+ run: |
37
+ pip install ruff mypy types-requests
38
+ pip install -e "." --no-deps || true
39
+ pip install onnxruntime numpy scipy
40
+ pip install openwakeword --no-deps
41
+
42
+ - name: Run ruff (lint)
43
+ run: ruff check src/
44
+
45
+ - name: Run ruff (format check)
46
+ run: ruff format --check src/
47
+
48
+ - name: Run mypy
49
+ # Mypy config in pyproject.toml: --strict for core SDK, training/tools excluded.
50
+ # Non-blocking: numpy/torch types cause noise in strict mode. Track error count down.
51
+ continue-on-error: true
52
+ run: mypy src/violawake_sdk --exclude 'training|tools'
53
+
54
+ # Job 2: Unit tests (all platforms x Python versions)
55
+ test-unit:
56
+ name: Unit tests (Python ${{ matrix.python-version }}, ${{ matrix.os }})
57
+ runs-on: ${{ matrix.os }}
58
+ strategy:
59
+ fail-fast: false
60
+ matrix:
61
+ os: [ubuntu-22.04, windows-2022, macos-14]
62
+ python-version: ["3.10", "3.11", "3.12"]
63
+
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - name: Set up Python ${{ matrix.python-version }}
68
+ uses: actions/setup-python@v5
69
+ with:
70
+ python-version: ${{ matrix.python-version }}
71
+ cache: pip
72
+
73
+ # PyAudio requires PortAudio headers.
74
+ - name: Install PortAudio (Linux)
75
+ if: runner.os == 'Linux'
76
+ run: sudo apt-get update && sudo apt-get install -y portaudio19-dev
77
+
78
+ - name: Install PortAudio (macOS)
79
+ if: runner.os == 'macOS'
80
+ run: brew install portaudio
81
+
82
+ - name: Install dependencies
83
+ # openwakeword is now an optional dep [oww]. Install it --no-deps to avoid
84
+ # tflite-runtime which has no Python 3.12 Linux wheels.
85
+ run: |
86
+ pip install openwakeword --no-deps
87
+ pip install onnxruntime numpy tqdm requests scipy
88
+ pip install -e ".[dev]"
89
+
90
+ - name: Smoke test install
91
+ run: |
92
+ python -c "from violawake_sdk import WakeDetector, __version__; print(f'violawake {__version__} OK')"
93
+
94
+ - name: Run unit tests
95
+ # Coverage floor: 50%. Hardware-dependent code (audio.py, pipeline.py) and
96
+ # CLI wrappers can't be tested without devices. Increase as mock tests are added.
97
+ run: pytest tests/unit/ -v --cov=violawake_sdk --cov-report=xml --cov-fail-under=50
98
+
99
+ - name: Upload coverage to Codecov
100
+ if: matrix.os == 'ubuntu-22.04' && matrix.python-version == env.PYTHON_DEFAULT
101
+ uses: codecov/codecov-action@v4
102
+ with:
103
+ files: coverage.xml
104
+ flags: unit
105
+ token: ${{ secrets.CODECOV_TOKEN }}
106
+
107
+ # Job 3: Integration tests (main branch + PRs with [integration] label)
108
+ test-integration:
109
+ name: Integration tests
110
+ runs-on: ubuntu-22.04
111
+ if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'run-integration')
112
+ needs: [lint, test-unit]
113
+
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+
117
+ - name: Set up Python
118
+ uses: actions/setup-python@v5
119
+ with:
120
+ python-version: ${{ env.PYTHON_DEFAULT }}
121
+ cache: pip
122
+
123
+ - name: Install PortAudio
124
+ run: sudo apt-get install -y portaudio19-dev
125
+
126
+ - name: Install all extras
127
+ run: |
128
+ pip install openwakeword --no-deps
129
+ pip install -e ".[all,dev]" || pip install -e ".[audio,download,tts,stt,vad,training,generate,dev]"
130
+
131
+ - name: Cache models
132
+ uses: actions/cache@v4
133
+ id: model-cache
134
+ with:
135
+ path: ~/.violawake/models
136
+ key: violawake-models-v1-${{ hashFiles('src/violawake_sdk/models.py') }}
137
+
138
+ - name: Download models (if cache miss)
139
+ if: steps.model-cache.outputs.cache-hit != 'true'
140
+ run: |
141
+ violawake-download --model temporal_cnn
142
+ violawake-download --model kokoro_v1_0
143
+
144
+ - name: Run integration tests
145
+ run: pytest tests/integration/ -v -m integration --cov=violawake_sdk --cov-report=xml
146
+
147
+ - name: Upload integration coverage
148
+ uses: codecov/codecov-action@v4
149
+ with:
150
+ files: coverage.xml
151
+ flags: integration
152
+ token: ${{ secrets.CODECOV_TOKEN }}
153
+
154
+ # Job 4: Benchmark regression check (nightly on main)
155
+ benchmark:
156
+ name: Benchmark regression check
157
+ runs-on: ubuntu-22.04
158
+ if: github.event_name == 'schedule' || (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'))
159
+ needs: [test-unit]
160
+
161
+ steps:
162
+ - uses: actions/checkout@v4
163
+
164
+ - name: Set up Python
165
+ uses: actions/setup-python@v5
166
+ with:
167
+ python-version: ${{ env.PYTHON_DEFAULT }}
168
+ cache: pip
169
+
170
+ - name: Install PortAudio
171
+ run: sudo apt-get install -y portaudio19-dev
172
+
173
+ - name: Install all extras
174
+ run: |
175
+ pip install openwakeword --no-deps
176
+ pip install -e ".[all,dev]" || pip install -e ".[audio,download,tts,stt,vad,training,generate,dev]"
177
+
178
+ - name: Cache models
179
+ uses: actions/cache@v4
180
+ with:
181
+ path: ~/.violawake/models
182
+ key: violawake-models-v1-${{ hashFiles('src/violawake_sdk/models.py') }}
183
+
184
+ - name: Download models
185
+ run: |
186
+ violawake-download --model temporal_cnn
187
+
188
+ - name: Run latency benchmarks
189
+ run: |
190
+ mkdir -p benchmark-results
191
+ pytest tests/benchmarks/bench_latency.py \
192
+ --benchmark-json=benchmark-results/latency-${{ github.sha }}.json
193
+
194
+ - name: Check for regressions
195
+ # --threshold 0.10 = fail if any benchmark regresses by more than 10%.
196
+ # This catches meaningful latency regressions while ignoring normal
197
+ # run-to-run variance on CI machines (typically ~5%).
198
+ run: python tools/benchmark_regression_check.py --threshold 0.10
199
+
200
+ - name: Upload benchmark results
201
+ uses: actions/upload-artifact@v4
202
+ with:
203
+ name: benchmark-results-${{ github.sha }}
204
+ path: benchmark-results/
@@ -0,0 +1,124 @@
1
+ name: Console CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ paths:
7
+ - "console/**"
8
+ pull_request:
9
+ branches: [main]
10
+ paths:
11
+ - "console/**"
12
+
13
+ concurrency:
14
+ group: console-ci-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ env:
18
+ PYTHON_DEFAULT: "3.11"
19
+ NODE_DEFAULT: "20"
20
+
21
+ jobs:
22
+ # --------------------------------------------------------------
23
+ # Job 1: Backend lint (ruff)
24
+ # --------------------------------------------------------------
25
+ backend-lint:
26
+ name: Backend lint
27
+ runs-on: ubuntu-22.04
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v5
33
+ with:
34
+ python-version: ${{ env.PYTHON_DEFAULT }}
35
+ cache: pip
36
+ cache-dependency-path: console/backend/requirements.txt
37
+
38
+ - name: Install lint dependencies
39
+ run: pip install ruff
40
+
41
+ - name: Run ruff (lint)
42
+ run: ruff check console/backend/
43
+
44
+ - name: Run ruff (format check)
45
+ run: ruff format --check console/backend/
46
+
47
+ # --------------------------------------------------------------
48
+ # Job 2: Backend tests
49
+ # --------------------------------------------------------------
50
+ backend-test:
51
+ name: Backend tests
52
+ runs-on: ubuntu-22.04
53
+ needs: [backend-lint]
54
+ steps:
55
+ - uses: actions/checkout@v4
56
+
57
+ - name: Set up Python
58
+ uses: actions/setup-python@v5
59
+ with:
60
+ python-version: ${{ env.PYTHON_DEFAULT }}
61
+ cache: pip
62
+ cache-dependency-path: console/backend/requirements.txt
63
+
64
+ - name: Install test dependencies
65
+ run: pip install -r console/backend/requirements.txt pytest httpx
66
+
67
+ - name: Run backend tests
68
+ run: pytest console/tests/test_backend.py -v
69
+
70
+ # --------------------------------------------------------------
71
+ # Job 3: Frontend build
72
+ # --------------------------------------------------------------
73
+ frontend-build:
74
+ name: Frontend build
75
+ runs-on: ubuntu-22.04
76
+ defaults:
77
+ run:
78
+ working-directory: console/frontend
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+
82
+ - name: Set up Node.js
83
+ uses: actions/setup-node@v4
84
+ with:
85
+ node-version: ${{ env.NODE_DEFAULT }}
86
+ cache: npm
87
+ cache-dependency-path: console/frontend/package-lock.json
88
+
89
+ - name: Install dependencies
90
+ run: npm ci
91
+
92
+ - name: Build frontend
93
+ run: npm run build
94
+
95
+ - name: Upload frontend artifact
96
+ uses: actions/upload-artifact@v4
97
+ with:
98
+ name: console-frontend-dist
99
+ path: console/frontend/dist/
100
+
101
+ # --------------------------------------------------------------
102
+ # Job 4: Frontend typecheck
103
+ # --------------------------------------------------------------
104
+ frontend-typecheck:
105
+ name: Frontend typecheck
106
+ runs-on: ubuntu-22.04
107
+ defaults:
108
+ run:
109
+ working-directory: console/frontend
110
+ steps:
111
+ - uses: actions/checkout@v4
112
+
113
+ - name: Set up Node.js
114
+ uses: actions/setup-node@v4
115
+ with:
116
+ node-version: ${{ env.NODE_DEFAULT }}
117
+ cache: npm
118
+ cache-dependency-path: console/frontend/package-lock.json
119
+
120
+ - name: Install dependencies
121
+ run: npm ci
122
+
123
+ - name: Run TypeScript typecheck
124
+ run: npx tsc --noEmit
@@ -0,0 +1,89 @@
1
+ name: Model Verification
2
+
3
+ on:
4
+ # Weekly on Monday at 06:00 UTC
5
+ schedule:
6
+ - cron: "0 6 * * 1"
7
+ # On changes to model registry or this workflow
8
+ push:
9
+ branches: [main, develop]
10
+ paths:
11
+ - "src/violawake_sdk/models.py"
12
+ - ".github/workflows/model-verify.yml"
13
+ pull_request:
14
+ branches: [main]
15
+ paths:
16
+ - "src/violawake_sdk/models.py"
17
+ - ".github/workflows/model-verify.yml"
18
+ # Manual trigger
19
+ workflow_dispatch:
20
+
21
+ concurrency:
22
+ group: model-verify-${{ github.ref }}
23
+ cancel-in-progress: true
24
+
25
+ jobs:
26
+ verify-models:
27
+ name: Verify models (Python ${{ matrix.python-version }})
28
+ runs-on: ubuntu-latest
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ python-version: ["3.10", "3.11", "3.12"]
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Set up Python ${{ matrix.python-version }}
38
+ uses: actions/setup-python@v5
39
+ with:
40
+ python-version: ${{ matrix.python-version }}
41
+ cache: pip
42
+
43
+ - name: Install dependencies
44
+ run: |
45
+ pip install -e ".[download]"
46
+ pip install onnxruntime
47
+
48
+ - name: Warn on placeholder hashes
49
+ run: |
50
+ python - <<'PY'
51
+ from violawake_sdk.models import MODEL_REGISTRY
52
+
53
+ seen = set()
54
+ placeholders = []
55
+ for name, spec in MODEL_REGISTRY.items():
56
+ if spec.name in seen:
57
+ continue
58
+ seen.add(spec.name)
59
+ if spec.sha256.startswith("PLACEHOLDER"):
60
+ placeholders.append(name)
61
+
62
+ if placeholders:
63
+ models = ", ".join(placeholders)
64
+ print(f"::warning::Placeholder SHA-256 values detected. Hash verification will be skipped for: {models}")
65
+ else:
66
+ print("All model hashes are populated.")
67
+ PY
68
+
69
+ - name: Cache downloaded models
70
+ uses: actions/cache@v4
71
+ id: model-cache
72
+ with:
73
+ path: ~/.violawake/models
74
+ key: model-verify-v1-${{ hashFiles('src/violawake_sdk/models.py') }}
75
+ restore-keys: |
76
+ model-verify-v1-
77
+
78
+ - name: Verify all models
79
+ run: python scripts/verify_models.py --ci
80
+ env:
81
+ VIOLAWAKE_MODEL_DIR: ~/.violawake/models
82
+
83
+ - name: Upload verification report
84
+ if: always()
85
+ uses: actions/upload-artifact@v4
86
+ with:
87
+ name: model-verification-py${{ matrix.python-version }}
88
+ path: model-verify-report.json
89
+ retention-days: 30
@@ -0,0 +1,179 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: write # for GitHub Release creation
10
+ id-token: write # for PyPI trusted publishing (OIDC)
11
+
12
+ jobs:
13
+ # ──────────────────────────────────────────────
14
+ # Job 1: Validate the release
15
+ # ──────────────────────────────────────────────
16
+ validate:
17
+ name: Validate release tag
18
+ runs-on: ubuntu-22.04
19
+ outputs:
20
+ version: ${{ steps.extract-version.outputs.version }}
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Extract version from tag
26
+ id: extract-version
27
+ run: |
28
+ TAG="${GITHUB_REF#refs/tags/v}"
29
+ echo "version=${TAG}" >> "$GITHUB_OUTPUT"
30
+ echo "Release version: ${TAG}"
31
+
32
+ - name: Verify version matches pyproject.toml
33
+ run: |
34
+ PYPROJECT_VERSION=$(python -c "
35
+ try:
36
+ import tomllib
37
+ except ImportError:
38
+ import tomli as tomllib
39
+ d=tomllib.load(open('pyproject.toml','rb')); print(d['project']['version'])")
40
+ TAG_VERSION="${{ steps.extract-version.outputs.version }}"
41
+ if [ "$PYPROJECT_VERSION" != "$TAG_VERSION" ]; then
42
+ echo "ERROR: pyproject.toml version ($PYPROJECT_VERSION) != tag ($TAG_VERSION)"
43
+ exit 1
44
+ fi
45
+
46
+ - name: Set up Python
47
+ uses: actions/setup-python@v5
48
+ with:
49
+ python-version: "3.11"
50
+
51
+ - name: Install PortAudio
52
+ run: sudo apt-get install -y portaudio19-dev
53
+
54
+ - name: Install and run full test suite
55
+ run: |
56
+ pip install -e ".[dev]"
57
+ pytest tests/unit/ -v --cov=violawake_sdk --cov-fail-under=65
58
+
59
+ # ──────────────────────────────────────────────
60
+ # Job 2: Build distribution artifacts
61
+ # ──────────────────────────────────────────────
62
+ build:
63
+ name: Build wheel + sdist
64
+ runs-on: ubuntu-22.04
65
+ needs: validate
66
+
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+
70
+ - name: Set up Python
71
+ uses: actions/setup-python@v5
72
+ with:
73
+ python-version: "3.11"
74
+
75
+ - name: Install hatch
76
+ run: pip install hatch
77
+
78
+ - name: Build wheel and sdist
79
+ run: hatch build
80
+
81
+ - name: Verify wheel contents
82
+ run: |
83
+ pip install twine
84
+ twine check dist/*
85
+
86
+ - name: Upload dist artifacts
87
+ uses: actions/upload-artifact@v4
88
+ with:
89
+ name: dist-artifacts
90
+ path: dist/
91
+
92
+ # ──────────────────────────────────────────────
93
+ # Job 3: Create GitHub Release with model assets
94
+ # ──────────────────────────────────────────────
95
+ github-release:
96
+ name: Create GitHub Release
97
+ runs-on: ubuntu-22.04
98
+ needs: [validate, build]
99
+
100
+ steps:
101
+ - uses: actions/checkout@v4
102
+
103
+ - name: Download dist artifacts
104
+ uses: actions/download-artifact@v4
105
+ with:
106
+ name: dist-artifacts
107
+ path: dist/
108
+
109
+ - name: Fetch model files from model artifact store
110
+ # Models are stored in a private artifact store (not in git).
111
+ # This step retrieves the models for the current version and
112
+ # attaches them to the GitHub Release.
113
+ env:
114
+ MODEL_STORE_TOKEN: ${{ secrets.MODEL_STORE_TOKEN }}
115
+ run: |
116
+ python tools/fetch_release_models.py \
117
+ --version ${{ needs.validate.outputs.version }} \
118
+ --output models/
119
+
120
+ - name: Create GitHub Release
121
+ uses: softprops/action-gh-release@v2
122
+ with:
123
+ tag_name: ${{ github.ref_name }}
124
+ name: ViolaWake SDK ${{ needs.validate.outputs.version }}
125
+ body_path: RELEASE_NOTES.md
126
+ draft: false
127
+ prerelease: ${{ contains(needs.validate.outputs.version, 'rc') || contains(needs.validate.outputs.version, 'alpha') || contains(needs.validate.outputs.version, 'beta') }}
128
+ files: |
129
+ dist/*.whl
130
+ dist/*.tar.gz
131
+ models/*.onnx
132
+
133
+ # ──────────────────────────────────────────────
134
+ # Job 4: Publish to PyPI (trusted publishing via OIDC)
135
+ # ──────────────────────────────────────────────
136
+ pypi-publish:
137
+ name: Publish to PyPI
138
+ runs-on: ubuntu-22.04
139
+ needs: [validate, build, github-release]
140
+ environment: pypi # required for OIDC trusted publishing
141
+
142
+ steps:
143
+ - name: Download dist artifacts
144
+ uses: actions/download-artifact@v4
145
+ with:
146
+ name: dist-artifacts
147
+ path: dist/
148
+
149
+ - name: Publish to PyPI
150
+ uses: pypa/gh-action-pypi-publish@release/v1
151
+ # Uses OIDC trusted publishing — no API token needed if configured
152
+ # in PyPI project settings. See:
153
+ # https://docs.pypi.org/trusted-publishers/
154
+
155
+ # ──────────────────────────────────────────────
156
+ # Job 5: Update model registry SHA-256s in docs
157
+ # ──────────────────────────────────────────────
158
+ update-docs:
159
+ name: Update model registry
160
+ runs-on: ubuntu-22.04
161
+ needs: github-release
162
+
163
+ steps:
164
+ - uses: actions/checkout@v4
165
+ with:
166
+ token: ${{ secrets.GITHUB_TOKEN }}
167
+
168
+ - name: Update model SHA-256s in models.py
169
+ run: |
170
+ python tools/update_model_registry.py \
171
+ --version ${{ needs.validate.outputs.version }}
172
+
173
+ - name: Commit updated registry
174
+ run: |
175
+ git config user.name "ViolaWake Release Bot"
176
+ git config user.email "bot@violawake.dev"
177
+ git add src/violawake_sdk/models.py
178
+ git commit -m "chore: update model registry for v${{ needs.validate.outputs.version }}" || echo "No changes to commit"
179
+ git push origin main