From 0fcc8ec21c27045205642f528496f3a2dbbdc18e Mon Sep 17 00:00:00 2001 From: karl Date: Mon, 12 Oct 2026 21:42:31 +0200 Subject: [PATCH] fix: default distributions to automatic GPU support with CPU fallback --- AGENTS.md | 4 ++++ crates/searchi-semantic/native/CMakeLists.txt | 16 +++++++--------- docs/BUILDING.md | 8 ++++++++ docs/ROADMAP.md | 4 ++++ docs/SEMANTIC.md | 9 ++++++++- docs/VALIDATION.md | 11 +++++++++-- tools/embeddinggemma2/build_runtime.py | 2 +- tools/embeddinggemma2/stage_runtime.py | 2 +- tools/embeddinggemma2/test_stage_runtime.py | 2 +- 9 files changed, 43 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1120e87..f55f105 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,10 @@ readiness without completed release gates and recorded evidence. - All tests in the Windows VM must use CPU only. Never enable GPU providers here. - Production uses available GPU/hardware acceleration with reliable CPU fallback; the CPU-only rule is a VM testing constraint, not a product restriction. + Ship one normal hardware-detecting runtime per OS/architecture. Default + builds/staging use auto (Metal on macOS, Vulkan on Windows/Linux plus CPU). + Missing acceleration build SDKs must fail release preparation, never silently + produce CPU-only distributions. Explicit CPU builds remain internal VM tools. - Keep the Rust engine independent of the desktop shell; CLI and UI share its API. - Prefer established Rust crates and proven retrieval algorithms over custom implementations. Keep platform adapters small, bound allocations and queues, diff --git a/crates/searchi-semantic/native/CMakeLists.txt b/crates/searchi-semantic/native/CMakeLists.txt index b3a92fc..949db19 100644 --- a/crates/searchi-semantic/native/CMakeLists.txt +++ b/crates/searchi-semantic/native/CMakeLists.txt @@ -10,12 +10,12 @@ endif() set(BUILD_SHARED_LIBS OFF CACHE BOOL "Static local worker runtime" FORCE) set(GGML_OPENMP OFF CACHE BOOL "Use bounded native llama thread pools without external OpenMP runtime" FORCE) set(GGML_NATIVE OFF CACHE BOOL "Portable CPU kernels") -# GPU backends are explicit production build options. The Windows VM builds -# with all of these OFF; runtime device selection always retains CPU fallback. +# Normal distribution builds include GPU support and CPU fallback. CPU-only +# configuration is explicitly reserved for development/testing in the VM. set(GGML_CUDA OFF CACHE BOOL "Production CUDA backend") set(GGML_VULKAN OFF CACHE BOOL "Production Vulkan backend") set(GGML_METAL OFF CACHE BOOL "Production Metal backend") -set(SEARCHI_ACCELERATION "cpu" CACHE STRING "cpu, auto, metal, vulkan, or cuda") +set(SEARCHI_ACCELERATION "auto" CACHE STRING "auto; explicit developer overrides: cpu, metal, vulkan, cuda") if(SEARCHI_ACCELERATION STREQUAL "cpu") set(GGML_CUDA OFF CACHE BOOL "CPU test build" FORCE) set(GGML_VULKAN OFF CACHE BOOL "CPU test build" FORCE) @@ -27,12 +27,10 @@ elseif(SEARCHI_ACCELERATION STREQUAL "cuda") elseif(SEARCHI_ACCELERATION STREQUAL "vulkan") set(GGML_VULKAN ON CACHE BOOL "Production Vulkan backend" FORCE) elseif(SEARCHI_ACCELERATION STREQUAL "auto") - find_package(Vulkan QUIET) - if(Vulkan_FOUND) - set(GGML_VULKAN ON CACHE BOOL "Available production Vulkan backend" FORCE) - else() - message(STATUS "No production acceleration SDK detected; retaining CPU backend") - endif() + # A missing build SDK must not silently turn a normal release into a + # CPU-only artifact. End users need drivers, not this compilation SDK. + find_package(Vulkan COMPONENTS glslc REQUIRED) + set(GGML_VULKAN ON CACHE BOOL "Production GPU support with runtime CPU fallback" FORCE) else() message(FATAL_ERROR "Unknown SEARCHI_ACCELERATION=${SEARCHI_ACCELERATION}") endif() diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 5e3e767..db213f6 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -86,6 +86,14 @@ search starts a model process; the desktop retains a prewarmed query worker. ## Cross-compilation and native release matrix +Normal distribution worker builds and staging default to `auto`: Metal on +macOS, Vulkan on Windows/Linux, with CPU fallback in the same application. +Users do not choose a CPU/GPU edition or rebuild for their graphics card. +Windows/Linux build machines need the Vulkan SDK including `glslc`; absence +fails release preparation instead of quietly generating a CPU-only runtime. +The compilation SDK is not an end-user requirement. Explicit `--backend cpu` +commands in the VM development sections are internal test configurations. + The shared Rust engine/service has no GUI dependency. Install a destination Rust target with `rustup target add TARGET`, then build using `cargo build -p searchi-cli --target TARGET --locked`. This also requires the diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 6d31d5d..fa423bf 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -49,6 +49,10 @@ Status: implementation started; no production release yet. Windows VM tests use - [ ] Isolated extraction workers with timeouts and resource ceilings. ## 5. Release gates +- [x] Normal worker build/staging defaults to hardware-detecting auto with CPU + fallback; missing Vulkan/glslc fails configuration rather than hiding GPU loss. +- [ ] Provision release-build GPU SDKs and verify one hardware-detecting package + per OS/architecture, including missing-driver fallback and dependency closure. - [x] Windows desktop archive packager includes explicit runtime/helper files and notices, checks architecture and records per-file checksums; excludes model caches and user data. Controlled packaging regression tests pass. diff --git a/docs/SEMANTIC.md b/docs/SEMANTIC.md index 73ceaaf..203f587 100644 --- a/docs/SEMANTIC.md +++ b/docs/SEMANTIC.md @@ -33,6 +33,13 @@ an external OpenMP runtime. Production CMake supports cpu, auto, metal, vulkan, and cuda; CPU builds force GPU flags off. Runtime/provider parity and packaging on other operating systems remain independent release checks. +The normal build/staging default is `auto`, shipping GPU support together with +CPU fallback: Metal on macOS, Vulkan on Windows/Linux. Runtime detection chooses +the usable device; users do not need hardware-specific builds. Release builders +must have the relevant compilation SDK. Missing Vulkan/glslc now fails the +normal build rather than silently omitting acceleration. Actual GPU parity, +loader/dependency packaging and no-driver CPU fallback still require evidence. + | Artifact | Bytes | SHA256 | | --- | ---: | --- | | embeddinggemma-2-Q8_0.gguf | 309,855,456 | 2188ac1deca4b77dffefd603c2776a9d76d9d74ec01841392982ebb840b09135 | @@ -264,4 +271,4 @@ point-localization failures and unsuppressed no-answer results. Strong file recall is insufficient for accurate spoken-topic navigation. A ten-second audio experiment reduces some distances but does not close that gate; it is not a new default. Quality policies must be validated on held-out data rather than -inventing thresholds from the evaluation corpus. \ No newline at end of file +inventing thresholds from the evaluation corpus. diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index 8a64c81..80330fe 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -11,8 +11,15 @@ The Windows archive packager has two controlled Python regressions: complete required file collection with checksum verification and user-settings exclusion, and mismatched worker architecture preserving the previous archive. Both pass. An actual CPU-only Tauri embedded-frontend Release build has been started; -archive creation, native launch outside the source tree and clean-machine -installation remain unverified until its completion and separate checks. +the embedded-frontend Release application build completed in 7m 25s. CPU-only +describes its VM test context, not a supported end-user edition. The normal +worker build/staging defaults are now auto and require GPU compilation support +alongside CPU fallback. The previously staged CPU worker remains an internal +test artifact; archive creation, automatic-runtime dependency packaging, native +launch outside the source tree and clean-machine installation remain unverified. +Actual `auto` configuration correctly rejected this VM's missing Vulkan library, +headers and `glslc`; no GPU-capable worker/package is claimed. Both staging +regressions still pass using explicit internal CPU test configuration. Offline staging now verifies the native license and every bundled Google asset before modifying resources. Two Python regressions verify missing-model rejection diff --git a/tools/embeddinggemma2/build_runtime.py b/tools/embeddinggemma2/build_runtime.py index a0b3feb..1202b7e 100644 --- a/tools/embeddinggemma2/build_runtime.py +++ b/tools/embeddinggemma2/build_runtime.py @@ -17,7 +17,7 @@ def run(arguments, cwd=None): def main(): parser = argparse.ArgumentParser(description="Build Searchi's pinned Google embedding worker") - parser.add_argument("--backend", choices=["cpu", "auto", "metal", "vulkan", "cuda"], default="cpu") + parser.add_argument("--backend", choices=["cpu", "auto", "metal", "vulkan", "cuda"], default="auto") parser.add_argument("--jobs", type=int, default=2) args = parser.parse_args() if not 1 <= args.jobs <= 16: diff --git a/tools/embeddinggemma2/stage_runtime.py b/tools/embeddinggemma2/stage_runtime.py index ca57c66..712da5f 100644 --- a/tools/embeddinggemma2/stage_runtime.py +++ b/tools/embeddinggemma2/stage_runtime.py @@ -25,7 +25,7 @@ def verify_bundle(root, manifest): def main(): parser = argparse.ArgumentParser(description="Stage Searchi runtime resources") - parser.add_argument("--backend", choices=["cpu", "auto", "metal", "vulkan", "cuda"], default="cpu") + parser.add_argument("--backend", choices=["cpu", "auto", "metal", "vulkan", "cuda"], default="auto") parser.add_argument("--bundle-model", action="store_true") args = parser.parse_args() name = "searchi-gemma-worker.exe" if os.name == "nt" else "searchi-gemma-worker" diff --git a/tools/embeddinggemma2/test_stage_runtime.py b/tools/embeddinggemma2/test_stage_runtime.py index 5ffcb17..60a04e1 100644 --- a/tools/embeddinggemma2/test_stage_runtime.py +++ b/tools/embeddinggemma2/test_stage_runtime.py @@ -32,7 +32,7 @@ class StagingTests(unittest.TestCase): (models / filename).write_bytes(content) specs.append({"path": filename, "bytes": len(content), "sha256": hashlib.sha256(content).hexdigest()}) (models / "projector.gguf").write_bytes(b"corrupt!") - with patch.object(stage_runtime, "ROOT", root), patch("sys.argv", ["stage_runtime", "--bundle-model"]), patch.object(stage_runtime.json, "loads", return_value={"files": specs}): + with patch.object(stage_runtime, "ROOT", root), patch("sys.argv", ["stage_runtime", "--backend", "cpu", "--bundle-model"]), patch.object(stage_runtime.json, "loads", return_value={"files": specs}): with self.assertRaisesRegex(RuntimeError, "checksum mismatch"): stage_runtime.main() self.assertEqual((destination / name).read_bytes(), b"previous worker")