diff --git a/.github/workflows/neural.yml b/.github/workflows/neural.yml index 9be3c77..934bd3c 100644 --- a/.github/workflows/neural.yml +++ b/.github/workflows/neural.yml @@ -4,6 +4,7 @@ on: branches: [main] pull_request: workflow_dispatch: + workflow_call: permissions: contents: read concurrency: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38bddec..e900216 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,12 +19,17 @@ jobs: import os, pathlib, tomllib version = tomllib.loads(pathlib.Path('Cargo.toml').read_text())['package']['version'] assert os.environ['GITHUB_REF_NAME'] == 'v' + version, 'Tag must match Cargo.toml' + for manifest in ['neural/Cargo.toml', 'neural/worker/Cargo.toml']: + assert tomllib.loads(pathlib.Path(manifest).read_text())['package']['version'] == version, 'Companion versions must match' PY validate: needs: version uses: ./.github/workflows/ci.yml + neural: + needs: version + uses: ./.github/workflows/neural.yml draft: - needs: validate + needs: [validate, neural] runs-on: ubuntu-latest permissions: contents: write @@ -39,4 +44,4 @@ jobs: RELEASE_TAG: ${{ github.ref_name }} shell: bash run: | - gh release create "$RELEASE_TAG" --verify-tag --draft --generate-notes release-assets/*/*.zip release-assets/*/*.sha256 + gh release create "$RELEASE_TAG" --verify-tag --draft --notes-file "docs/releases/$RELEASE_TAG.md" release-assets/*/*.zip release-assets/*/*.sha256 diff --git a/Cargo.lock b/Cargo.lock index 0807af4..651d0b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,7 +481,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "switchify-prediction" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "rusqlite", diff --git a/Cargo.toml b/Cargo.toml index 770dd4a..78f605f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "switchify-prediction" -version = "0.1.0" +version = "0.2.0" edition = "2024" rust-version = "1.97.1" license = "MIT" diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md new file mode 100644 index 0000000..0e2a19a --- /dev/null +++ b/docs/releases/v0.2.0.md @@ -0,0 +1,14 @@ +# Switchify Prediction v0.2.0 + +Adds an optional offline SmolLM2 companion library and CLI. It returns statistical suggestions immediately and can refine them asynchronously in an isolated worker. Existing predictor APIs, personal learning, database formats and the default statistical model are unchanged. + +The neural companion is experimental and **not production-qualified**. Optimized Windows refinement measured 122 ms p95, but two development quality cells regressed and portable performance missed the target. Linux and macOS packages passed build and automated tests; actual model performance on those platforms remains unmeasured. See the repository's `docs/smol-production-results.md` for measurements and limitations. + +## Downloads + +- `switchify-prediction-*` packages contain the statistical CLI. +- `switchify-prediction-neural-*` packages contain the companion CLI and workers for the named platform. These are separate from the statistical CLI packages. +- The model bundle contains the existing English statistical database and attribution notices. +- Each ZIP has a SHA-256 file. Verify it and run the included `verify_bundle.py` after extraction. + +Neural model weights are not included. Follow `neural/README.md` to assemble the pinned local model bundle. The companion never downloads weights at runtime. Packages include license notices and explicit qualification status. Executables are unsigned, and this release does not integrate the companion into Switchify PC or publish crates to crates.io. diff --git a/neural/Cargo.lock b/neural/Cargo.lock index 14aecdd..d27eeaf 100644 --- a/neural/Cargo.lock +++ b/neural/Cargo.lock @@ -1401,7 +1401,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "switchify-prediction" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "rusqlite", @@ -1416,7 +1416,7 @@ dependencies = [ [[package]] name = "switchify-prediction-neural" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "serde", @@ -1429,7 +1429,7 @@ dependencies = [ [[package]] name = "switchify-smol-worker" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "candle-core", diff --git a/neural/Cargo.toml b/neural/Cargo.toml index f9290cd..1d6da3d 100644 --- a/neural/Cargo.toml +++ b/neural/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "switchify-prediction-neural" -version = "0.1.0" +version = "0.2.0" edition = "2024" rust-version = "1.97.1" license = "MIT" diff --git a/neural/worker/Cargo.toml b/neural/worker/Cargo.toml index c5595e6..95790ba 100644 --- a/neural/worker/Cargo.toml +++ b/neural/worker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "switchify-smol-worker" -version = "0.1.0" +version = "0.2.0" edition = "2024" rust-version = "1.97.1" license = "MIT" diff --git a/scripts/package_neural.py b/scripts/package_neural.py index aaeec62..8af4afc 100644 --- a/scripts/package_neural.py +++ b/scripts/package_neural.py @@ -9,6 +9,7 @@ import shutil import subprocess import tempfile +import tomllib import dependency_notices @@ -65,7 +66,8 @@ def main(): rustc = subprocess.check_output(['rustc', '-vV'], text=True) target = next(line.split(': ',1)[1] for line in rustc.splitlines() if line.startswith('host: ')) ext = '.exe' if os.name == 'nt' else '' - name = 'switchify-prediction-neural-0.1.0-' + target + version = tomllib.loads((ROOT / 'neural/Cargo.toml').read_text())['package']['version'] + name = 'switchify-prediction-neural-' + version + '-' + target args.output.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory() as temp: stage = Path(temp) / name @@ -85,7 +87,7 @@ def main(): (stage / 'THIRD_PARTY_NOTICES.md').write_text(notices(target), encoding='utf-8') sysroot = Path(subprocess.check_output(['rustc','--print','sysroot'], text=True).strip()) shutil.copyfile(sysroot / 'share/doc/rust/COPYRIGHT-library.html', stage / 'RUST_LIBRARY_COPYRIGHT.html') - (stage / 'BUILD.json').write_text(json.dumps({'version':'0.1.0','target':target, 'rustc':rustc, + (stage / 'BUILD.json').write_text(json.dumps({'version':version,'target':target, 'rustc':rustc, 'platform':platform.platform(), 'libc':platform.libc_ver(), 'signed':False, 'commit':subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip(), 'model_id':'smollm2-135m-q8-v1', 'accelerated_requires':['avx2','fma','f16c'] if args.accelerated else [],