Ship portable CPU dispatch and verified shared runtime dependencies

This commit is contained in:
karl committed 2026-10-12 22:35:34 +02:00
1 parent 278b09f82c
commit 0bba1a0aa7
19 files changed
+675 -17

No files matched your search

+66
View File
@@ -1,6 +1,9 @@
"""Build a pinned isolated native worker; development VM builds use --backend cpu."""
import argparse
import hashlib
import json
import os
import re
from pathlib import Path
import shutil
import subprocess
@@ -9,6 +12,49 @@ import sys
ROOT = Path(__file__).resolve().parents[2]
TAG = "b11461"
COMMIT = "4d756bc72bf00a4aacf410ae15a2d315f3db400d"
WINDOWS_CRT_FILES = (
"concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll",
"msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll",
"vcruntime140.dll", "vcruntime140_1.dll", "vcruntime140_threads.dll",
)
def bundle_windows_crt(build):
"""Copy unchanged release CRT from the selected Visual Studio installation."""
cache = (build / "CMakeCache.txt").read_text(encoding="utf-8")
match = re.search(r"^CMAKE_GENERATOR_INSTANCE:[^=]+=([^\r\n]+)$", cache, re.MULTILINE)
if not match:
raise RuntimeError("Selected Visual Studio installation is missing from CMake cache")
installation = Path(match.group(1))
auxiliary = installation / "VC/Auxiliary/Build"
version = (auxiliary / "Microsoft.VCRedistVersion.default.txt").read_text().strip()
tools_version = (auxiliary / "Microsoft.VCToolsVersion.default.txt").read_text().strip()
if not re.fullmatch(r"\d+\.\d+\.\d+", version) or version.split(".")[:2] != tools_version.split(".")[:2]:
raise RuntimeError("Visual Studio CRT redistributable does not match selected build tools")
cmake_version = [re.search(rf"^CMAKE_CACHE_{part}_VERSION:[^=]+=(\d+)$", cache, re.MULTILINE)
for part in ("MAJOR", "MINOR", "PATCH")]
if all(cmake_version):
compiler_info = build / "CMakeFiles" / ".".join(match.group(1) for match in cmake_version) / "CMakeCXXCompiler.cmake"
if compiler_info.is_file():
compiler = compiler_info.read_text(encoding="utf-8")
actual = re.search(r'set\(CMAKE_CXX_COMPILER_VERSION "(\d+)\.(\d+)\.[^"]+"\)', compiler)
if not actual or (int(actual.group(1)), int(actual.group(2))) != (19, int(version.split(".")[1])):
raise RuntimeError("Visual Studio CRT redistributable does not match actual selected compiler")
source = installation / "VC/Redist/MSVC" / version / "x64/Microsoft.VC143.CRT"
files = [source / name for name in WINDOWS_CRT_FILES]
if any(not path.is_file() or path.is_symlink() for path in files):
raise RuntimeError("Required release Visual Studio CRT redistributable is missing")
destination = build / "Release"
for path in files:
shutil.copy2(path, destination / path.name)
(destination / "MSVC-REDISTRIBUTION.txt").write_text(
"Microsoft Visual C++ release runtime — copyright Microsoft Corporation.\n"
f"Unmodified x64 redistributable files from Visual Studio VC/Redist/MSVC/{version}/x64/Microsoft.VC143.CRT.\n"
"These proprietary files are distributed under Microsoft's applicable Visual Studio license terms, not searchi's license.\n"
"Terms: https://visualstudio.microsoft.com/license-terms/vs2022-ga-diagnosticbuildtools/\n"
"Redistributable list: https://learn.microsoft.com/en-us/visualstudio/releases/2022/redistribution\n"
"Deployment guidance: https://learn.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files\n",
encoding="utf-8")
def run(arguments, cwd=None):
@@ -44,6 +90,14 @@ def main():
reverse = subprocess.run(["git", "apply", "--reverse", "--check", str(patch)], cwd=source, capture_output=True)
if reverse.returncode:
raise RuntimeError("Runtime memory patch cannot be applied or verified")
if os.name == "nt" and args.backend != "cpu":
for patch_name in ["cpu-dispatch.patch", "cpu-only-loader.patch"]:
dispatch_patch = Path(__file__).with_name(patch_name)
check = subprocess.run(["git", "apply", "--check", str(dispatch_patch)], cwd=source, capture_output=True)
if check.returncode == 0:
run(["git", "apply", dispatch_patch], cwd=source)
elif subprocess.run(["git", "apply", "--reverse", "--check", str(dispatch_patch)], cwd=source, capture_output=True).returncode:
raise RuntimeError(f"Runtime patch cannot be applied or verified: {patch_name}")
cmake_exe = shutil.which("cmake")
if not cmake_exe:
try:
@@ -59,6 +113,18 @@ def main():
if vulkan_loader:
for name in ["vulkan-1.dll", "VULKAN-LICENSE.txt"]:
shutil.copy2(vulkan_loader / name, build / "Release" / name)
if os.name == "nt" and args.backend != "cpu":
directory = build / "Release"
bundle_windows_crt(build)
def checksum(path):
with path.open("rb") as stream:
return hashlib.file_digest(stream, "sha256").hexdigest()
inventory = {"worker_sha256": checksum(directory / "searchi-gemma-worker.exe"),
"libraries": [{"path": path.name, "bytes": path.stat().st_size, "sha256": checksum(path)}
for path in sorted(directory.glob("*.dll"))]}
temporary = directory / "runtime-libraries.json.partial"
temporary.write_text(json.dumps(inventory, indent=2), encoding="utf-8")
os.replace(temporary, directory / "runtime-libraries.json")
print(f"Runtime build complete: {build}")
+98
View File
@@ -0,0 +1,98 @@
--- a/ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp
+++ b/ggml/src/ggml-cpu/arch/x86/cpu-feats.cpp
@@ -261,10 +261,30 @@
#endif
static int ggml_backend_cpu_x86_score() {
- // FIXME: this does not check for OS support
-
int score = 1;
cpuid_x86 is;
+ // CPUID describes hardware; AVX also requires OS-managed XMM/YMM state.
+ // Keep this selector compiled without architecture flags (upstream design).
+ unsigned long long xcr0 = 0;
+ if (is.XSAVE() && is.OSXSAVE()) {
+#ifdef _MSC_VER
+ xcr0 = _xgetbv(0);
+#else
+ unsigned int eax, edx;
+ __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
+ xcr0 = (static_cast<unsigned long long>(edx) << 32) | eax;
+#endif
+ }
+#if defined(GGML_AVX) || defined(GGML_AVX2) || defined(GGML_FMA) || defined(GGML_F16C) || defined(GGML_AVX_VNNI) || defined(GGML_AVX512) || defined(GGML_AVX512_VBMI) || defined(GGML_AVX512_BF16) || defined(GGML_AVX512_VNNI)
+ if (!is.AVX() || (xcr0 & 0x6) != 0x6) { return 0; }
+#endif
+#if defined(GGML_AVX512) || defined(GGML_AVX512_VBMI) || defined(GGML_AVX512_BF16) || defined(GGML_AVX512_VNNI)
+ if ((xcr0 & 0xe6) != 0xe6) { return 0; }
+#endif
+#ifdef GGML_AMX_INT8
+ // Tile state also needs OS permission. Searchi does not request it.
+ return 0;
+#endif
#ifdef GGML_FMA
if (!is.FMA()) { return 0; }
--- a/ggml/src/ggml-backend-reg.cpp
+++ b/ggml/src/ggml-backend-reg.cpp
@@ -582,6 +582,11 @@
bool silent = false;
#endif
+ const char * cpu_only = std::getenv("GGML_CPU_ONLY");
+ if (cpu_only && std::strcmp(cpu_only, "1") == 0) {
+ ggml_backend_load_best("cpu", silent, dir_path);
+ return;
+ }
ggml_backend_load_best("blas", silent, dir_path);
ggml_backend_load_best("zendnn", silent, dir_path);
ggml_backend_load_best("cann", silent, dir_path);
@@ -590,7 +595,10 @@
ggml_backend_load_best("metal", silent, dir_path);
ggml_backend_load_best("rpc", silent, dir_path);
ggml_backend_load_best("sycl", silent, dir_path);
- ggml_backend_load_best("vulkan", silent, dir_path);
+ const char * disable_vulkan = std::getenv("GGML_DISABLE_VULKAN");
+ if (!disable_vulkan || std::strcmp(disable_vulkan, "1") != 0) {
+ ggml_backend_load_best("vulkan", silent, dir_path);
+ }
ggml_backend_load_best("virtgpu", silent, dir_path);
ggml_backend_load_best("opencl", silent, dir_path);
ggml_backend_load_best("hexagon", silent, dir_path);
@@ -599,7 +607,7 @@
ggml_backend_load_best("cpu", silent, dir_path);
// check the environment variable GGML_BACKEND_PATH to load an out-of-tree backend
const char * backend_path = std::getenv("GGML_BACKEND_PATH");
- if (backend_path) {
+ if (backend_path && dir_path == nullptr) {
ggml_backend_load(backend_path);
}
}
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,7 +37,9 @@
# Add path to modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
--- a/ggml/src/ggml-cpu/CMakeLists.txt
+++ b/ggml/src/ggml-cpu/CMakeLists.txt
@@ -13,7 +13,11 @@
# from inlining architecture-specific instructions into the score function.
# Without this, LTO can cause SIGILL when loading backends on older CPUs
# (e.g., loading power10 backend on power9 crashes before feature check runs).
- target_compile_options(${GGML_CPU_FEATS_NAME} PRIVATE -fno-lto)
+ if (MSVC)
+ target_compile_options(${GGML_CPU_FEATS_NAME} PRIVATE /GL-)
+ else()
+ target_compile_options(${GGML_CPU_FEATS_NAME} PRIVATE -fno-lto)
+ endif()
target_link_libraries(${cpu_name} PRIVATE ${GGML_CPU_FEATS_NAME})
endfunction()
@@ -0,0 +1,31 @@
--- a/ggml/include/ggml-backend.h
+++ b/ggml/include/ggml-backend.h
@@ -260,6 +260,8 @@
// Load all known backends from dynamic libraries
GGML_API void ggml_backend_load_all(void);
GGML_API void ggml_backend_load_all_from_path(const char * dir_path);
+ // Searchi: explicit CPU-only startup, independent of per-DLL CRT environment state.
+ GGML_API void ggml_backend_load_cpu_only_from_path(const char * dir_path);
//
// Backend scheduler
--- a/ggml/src/ggml-backend-reg.cpp
+++ b/ggml/src/ggml-backend-reg.cpp
@@ -575,6 +575,17 @@
ggml_backend_load_all_from_path(nullptr);
}
+void ggml_backend_load_cpu_only_from_path(const char * dir_path) {
+#ifdef NDEBUG
+ const bool silent = true;
+#else
+ const bool silent = false;
+#endif
+ // Do not inspect GPU backends or GGML_BACKEND_PATH. The caller supplies
+ // the executable directory, so working-directory discovery is excluded.
+ ggml_backend_load_best("cpu", silent, dir_path);
+}
+
void ggml_backend_load_all_from_path(const char * dir_path) {
#ifdef NDEBUG
bool silent = true;
+10 -1
View File
@@ -22,6 +22,7 @@ def main():
parser.add_argument("--archive", type=Path, default=ROOT / ".searchi/packages/searchi-windows.zip")
parser.add_argument("--models", type=Path, default=ROOT / ".searchi/models/embeddinggemma2")
parser.add_argument("--output", type=Path, default=ROOT / ".searchi/packages/smoke.json")
parser.add_argument("--baseline", action="store_true", help="Extract only the baseline CPU module to test the packaged fallback")
args = parser.parse_args()
manifest = json.loads(Path(__file__).with_name("model-manifest.json").read_text())
model = args.models / manifest["files"][0]["path"]
@@ -40,6 +41,8 @@ def main():
data = archive.read(record["path"])
assert len(data) == record["bytes"]
assert hashlib.sha256(data).hexdigest() == record["sha256"]
if args.baseline and record["path"].startswith("ggml-cpu-") and record["path"] != "ggml-cpu-x64.dll":
continue
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(data)
environment = dict(os.environ, GGML_DISABLE_VULKAN="1")
@@ -56,10 +59,13 @@ def main():
worker.kill()
worker.wait(timeout=10)
raise
assert worker.returncode == 0, stderr
assert worker.returncode == 0, f"Worker exit {worker.returncode:#x}: {stderr}"
worker_duration_ms = (time.perf_counter()-started)*1000
hello, result = [json.loads(line) for line in stdout.splitlines()]
assert hello["ready"] and hello["device"] == "cpu" and result["device"] == "cpu" and result["ok"]
assert hello.get("vulkan_module_loaded") is False
if args.baseline:
assert hello.get("cpu_backend_module") == "ggml-cpu-x64.dll"
vector = result["vectors"][0]
assert len(vector) == 768 and all(math.isfinite(x) for x in vector)
assert abs(sum(x*x for x in vector) - 1) < 0.0001
@@ -69,6 +75,9 @@ def main():
with args.archive.open("rb") as stream:
archive_checksum = hashlib.file_digest(stream, "sha256").hexdigest()
report = {"architecture": package["architecture"], "verified_files": len(package["files"]),
"cpu_backend_module": hello.get("cpu_backend_module"),
"vulkan_module_loaded": hello.get("vulkan_module_loaded"),
"baseline_only": args.baseline,
"archive_sha256": archive_checksum,
"worker_requested_device": "auto", "worker_actual_device": hello["device"],
"no_gpu_condition": "GGML_DISABLE_VULKAN=1 only in child; SEARCHI_FORCE_CPU absent",
+56 -1
View File
@@ -3,10 +3,56 @@ import argparse
import hashlib
import json
import os
import re
from pathlib import Path
import shutil
ROOT = Path(__file__).resolve().parents[2]
CRT_LIBRARIES = {"concrt140.dll", "msvcp140.dll", "msvcp140_1.dll", "msvcp140_2.dll", "msvcp140_atomic_wait.dll", "msvcp140_codecvt_ids.dll", "vccorlib140.dll", "vcruntime140.dll", "vcruntime140_1.dll", "vcruntime140_threads.dll"}
def verify_runtime(source, required=False):
"""Preflight an explicit, bounded dependency inventory, never a DLL glob."""
inventory = source.parent / "runtime-libraries.json"
if not inventory.is_file():
if required:
raise RuntimeError("Runtime dependency inventory is missing")
return []
if inventory.stat().st_size > 65536:
raise RuntimeError("Runtime dependency inventory is oversized")
manifest = json.loads(inventory.read_text(encoding="utf-8"))
if not isinstance(manifest, dict):
raise RuntimeError("Invalid runtime dependency inventory")
with source.open("rb") as stream:
if hashlib.file_digest(stream, "sha256").hexdigest() != manifest.get("worker_sha256"):
raise RuntimeError("Runtime worker checksum mismatch")
records = manifest.get("libraries")
if not isinstance(records, list) or not 1 <= len(records) <= 64:
raise RuntimeError("Invalid runtime dependency inventory")
paths, names = [], set()
for record in records:
if not isinstance(record, dict):
raise RuntimeError("Invalid runtime library metadata")
name = record.get("path", "")
if not isinstance(name, str) or not re.fullmatch(r"[A-Za-z0-9_-]+\.dll", name) or name.lower() in names:
raise RuntimeError("Invalid or duplicate runtime library path")
names.add(name.lower())
artifact = source.parent / name
if artifact.is_symlink() or not artifact.resolve().is_relative_to(source.parent.resolve()):
raise RuntimeError("Runtime library resolves outside build directory")
size = record.get("bytes")
checksum = record.get("sha256", "")
if type(size) is not int or size <= 0 or not isinstance(checksum, str) or not re.fullmatch(r"[0-9a-f]{64}", checksum):
raise RuntimeError("Invalid runtime library metadata")
if not artifact.is_file() or artifact.stat().st_size != size:
raise RuntimeError(f"Runtime library missing or size mismatch: {name}")
with artifact.open("rb") as stream:
if hashlib.file_digest(stream, "sha256").hexdigest() != checksum:
raise RuntimeError(f"Runtime library checksum mismatch: {name}")
paths.append(artifact)
if required and not ({"ggml-cpu-x64.dll", "ggml-vulkan.dll", "vulkan-1.dll", "ggml.dll", "ggml-base.dll", "llama.dll", "mtmd.dll"} | CRT_LIBRARIES).issubset(names):
raise RuntimeError("Runtime inventory lacks required CPU/GPU libraries")
return [inventory, *paths]
def verify_bundle(root, manifest):
@@ -38,6 +84,12 @@ def main():
license_path = ROOT / ".searchi/references/llama.cpp/LICENSE"
if not license_path.is_file():
raise RuntimeError("Native worker license is missing")
dependencies = verify_runtime(source, os.name == "nt" and args.backend in {"auto", "vulkan"})
crt_notice = source.parent / "MSVC-REDISTRIBUTION.txt"
if os.name == "nt" and args.backend != "cpu":
if not crt_notice.is_file():
raise RuntimeError("Microsoft CRT redistribution notice is missing")
dependencies.append(crt_notice)
artifacts = []
if args.bundle_model:
manifest = json.loads(Path(__file__).with_name("model-manifest.json").read_text())
@@ -49,11 +101,14 @@ def main():
destination = ROOT / "apps/desktop/src-tauri/resources"
destination.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination / name)
for dependency in dependencies:
shutil.copy2(dependency, destination / dependency.name)
# Include the upstream runtime license with the statically linked helper.
shutil.copy2(license_path, destination / "LLAMA-LICENSE.txt")
if loader:
for name in ["vulkan-1.dll", "VULKAN-LICENSE.txt"]:
shutil.copy2(loader / name, destination / name)
if name not in {path.name for path in dependencies}:
shutil.copy2(loader / name, destination / name)
if args.bundle_model:
model_destination = destination / "embeddinggemma2"
model_destination.mkdir(exist_ok=True)
@@ -0,0 +1,67 @@
"""Redistributable preparation tests; no native builds or GPU execution."""
from pathlib import Path
import tempfile
import unittest
from build_runtime import bundle_windows_crt, WINDOWS_CRT_FILES
class CrtTests(unittest.TestCase):
def fixture(self, directory):
root = Path(directory)
installation = root / "Visual Studio"
auxiliary = installation / "VC/Auxiliary/Build"
auxiliary.mkdir(parents=True)
(auxiliary / "Microsoft.VCRedistVersion.default.txt").write_text("14.44.35112")
(auxiliary / "Microsoft.VCToolsVersion.default.txt").write_text("14.44.35207")
source = installation / "VC/Redist/MSVC/14.44.35112/x64/Microsoft.VC143.CRT"
source.mkdir(parents=True)
for name in WINDOWS_CRT_FILES:
(source / name).write_bytes(name.encode())
(source / "unlisted-debug.dll").write_bytes(b"do not distribute")
build = root / "build"
(build / "Release").mkdir(parents=True)
(build / "CMakeCache.txt").write_text(f"CMAKE_GENERATOR_INSTANCE:INTERNAL={installation}\n")
return build, source, auxiliary
def test_copies_only_fixed_unmodified_release_files_and_public_provenance(self):
with tempfile.TemporaryDirectory() as directory:
build, source, _ = self.fixture(directory)
bundle_windows_crt(build)
for name in WINDOWS_CRT_FILES:
self.assertEqual((build / "Release" / name).read_bytes(), (source / name).read_bytes())
self.assertFalse((build / "Release/unlisted-debug.dll").exists())
notice = (build / "Release/MSVC-REDISTRIBUTION.txt").read_text(encoding="utf-8")
self.assertIn("14.44.35112", notice)
self.assertIn("https://learn.microsoft.com/", notice)
self.assertNotIn(directory, notice)
def test_missing_or_mismatched_crt_preserves_existing_files(self):
with tempfile.TemporaryDirectory() as directory:
build, source, auxiliary = self.fixture(directory)
existing = build / "Release/msvcp140.dll"
existing.write_bytes(b"previous CRT")
(source / "vcruntime140.dll").unlink()
with self.assertRaisesRegex(RuntimeError, "redistributable is missing"):
bundle_windows_crt(build)
self.assertEqual(existing.read_bytes(), b"previous CRT")
(auxiliary / "Microsoft.VCRedistVersion.default.txt").write_text("14.43.12345")
with self.assertRaisesRegex(RuntimeError, "does not match"):
bundle_windows_crt(build)
self.assertEqual(existing.read_bytes(), b"previous CRT")
def test_custom_toolset_compiler_mismatch_rejects_before_copy(self):
with tempfile.TemporaryDirectory() as directory:
build, _, _ = self.fixture(directory)
with (build / "CMakeCache.txt").open("a") as stream:
stream.write("CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4\nCMAKE_CACHE_MINOR_VERSION:INTERNAL=2\nCMAKE_CACHE_PATCH_VERSION:INTERNAL=0\n")
compiler = build / "CMakeFiles/4.2.0/CMakeCXXCompiler.cmake"
compiler.parent.mkdir(parents=True)
compiler.write_text('set(CMAKE_CXX_COMPILER_VERSION "19.45.12345")\n')
with self.assertRaisesRegex(RuntimeError, "actual selected compiler"):
bundle_windows_crt(build)
self.assertFalse((build / "Release/msvcp140.dll").exists())
if __name__ == "__main__":
unittest.main()
@@ -1,5 +1,6 @@
"""Offline installation regressions; no model downloads or inference."""
import hashlib
import json
import os
from pathlib import Path
import tempfile
@@ -10,6 +11,70 @@ import stage_runtime
class StagingTests(unittest.TestCase):
@unittest.skipUnless(os.name == "nt", "Windows app-local CRT staging")
def test_missing_crt_notice_preserves_existing_staged_worker(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
runtime = root / ".searchi/runtime/auto/Release"
runtime.mkdir(parents=True)
worker = runtime / "searchi-gemma-worker.exe"
worker.write_bytes(b"new worker")
names = {"ggml-cpu-x64.dll", "ggml-vulkan.dll", "vulkan-1.dll", "ggml.dll", "ggml-base.dll", "llama.dll", "mtmd.dll"} | stage_runtime.CRT_LIBRARIES
records = []
for name in sorted(names):
data = name.encode()
(runtime / name).write_bytes(data)
records.append({"path": name, "bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()})
(runtime / "runtime-libraries.json").write_text(json.dumps({"worker_sha256": hashlib.sha256(worker.read_bytes()).hexdigest(), "libraries": records}))
license_path = root / ".searchi/references/llama.cpp/LICENSE"
license_path.parent.mkdir(parents=True)
license_path.write_text("license")
destination = root / "apps/desktop/src-tauri/resources"
destination.mkdir(parents=True)
(destination / worker.name).write_bytes(b"previous worker")
with patch.object(stage_runtime, "ROOT", root), patch("sys.argv", ["stage_runtime"]):
with self.assertRaisesRegex(RuntimeError, "redistribution notice"):
stage_runtime.main()
self.assertEqual((destination / worker.name).read_bytes(), b"previous worker")
def test_missing_dependency_preserves_staged_worker(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
runtime = root / ".searchi/runtime/cpu"
if os.name == "nt":
runtime /= "Release"
runtime.mkdir(parents=True)
name = "searchi-gemma-worker.exe" if os.name == "nt" else "searchi-gemma-worker"
(runtime / name).write_bytes(b"new worker")
(runtime / "runtime-libraries.json").write_text(json.dumps({"worker_sha256": hashlib.sha256(b"new worker").hexdigest(), "libraries": [{"path": "ggml-cpu-x64.dll", "bytes": 4, "sha256": hashlib.sha256(b"test").hexdigest()}]}))
license_path = root / ".searchi/references/llama.cpp/LICENSE"
license_path.parent.mkdir(parents=True)
license_path.write_text("license")
destination = root / "apps/desktop/src-tauri/resources"
destination.mkdir(parents=True)
(destination / name).write_bytes(b"previous worker")
with patch.object(stage_runtime, "ROOT", root), patch("sys.argv", ["stage_runtime", "--backend", "cpu"]):
with self.assertRaisesRegex(RuntimeError, "missing or size mismatch"):
stage_runtime.main()
self.assertEqual((destination / name).read_bytes(), b"previous worker")
def test_inventory_rejects_escape_and_omits_unlisted_libraries(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
worker = root / "worker.exe"
worker.write_bytes(b"worker")
(root / "good.dll").write_bytes(b"good")
(root / "private.dll").write_bytes(b"private")
record = {"path": "good.dll", "bytes": 4, "sha256": hashlib.sha256(b"good").hexdigest()}
manifest = {"worker_sha256": hashlib.sha256(b"worker").hexdigest(), "libraries": [record]}
path = root / "runtime-libraries.json"
path.write_text(json.dumps(manifest))
self.assertEqual([p.name for p in stage_runtime.verify_runtime(worker)], ["runtime-libraries.json", "good.dll"])
record["path"] = "../escape.dll"
path.write_text(json.dumps(manifest))
with self.assertRaisesRegex(RuntimeError, "library path"):
stage_runtime.verify_runtime(worker)
def test_corrupt_second_model_preserves_existing_staged_worker(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)