diff --git a/.gitignore b/.gitignore index 4802e98..cb55e24 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,6 @@ docs/dist plugin/.claude-plugin/plugin.json plugin/.claude-plugin/marketplace.json user-prompt + +# Compressed ONNX runtime libs are produced by scripts/download-onnx-libs.sh +internal/embedding/assets/lib/**/*.gz diff --git a/experiments.md b/experiments.md index 9a36187..3061b70 100644 --- a/experiments.md +++ b/experiments.md @@ -10,3 +10,9 @@ Mutate: Go code and build flags. Frozen: public API, DB schema, plugin manifest. |----|----------|-------------|---------------|-------| | 01 | baseline | 102698416 | - | base | | 02 | pkg/hooks: raw HTTP/1.0 client replaces net/http | 87603952 | -14.7% | KEEP | +| 03 | statusline: shared client, drop net/http | 84531056 | -3.5% vs 02 | KEEP | +| 04 | embed gzipped ONNX runtime libs, gunzip at first use | 57216880 | -32.3% vs 03 | KEEP | + +Notes: +- Trial 04 needs `scripts/download-onnx-libs.sh` to produce `.gz` files; existing raw libs trigger a re-download. +- Extracted library verified byte-identical to the raw dylib (`cmp`). diff --git a/internal/embedding/lib_darwin_arm64.go b/internal/embedding/lib_darwin_arm64.go index 807e07e..15159dd 100644 --- a/internal/embedding/lib_darwin_arm64.go +++ b/internal/embedding/lib_darwin_arm64.go @@ -6,7 +6,7 @@ import ( _ "embed" ) -//go:embed assets/lib/darwin-arm64/libonnxruntime.dylib +//go:embed assets/lib/darwin-arm64/libonnxruntime.dylib.gz var onnxRuntimeLib []byte const onnxRuntimeLibName = "libonnxruntime.dylib" diff --git a/internal/embedding/lib_linux_amd64.go b/internal/embedding/lib_linux_amd64.go index 80b4a30..a9ef22b 100644 --- a/internal/embedding/lib_linux_amd64.go +++ b/internal/embedding/lib_linux_amd64.go @@ -6,10 +6,10 @@ import ( _ "embed" ) -//go:embed assets/lib/linux-amd64/libonnxruntime.so +//go:embed assets/lib/linux-amd64/libonnxruntime.so.gz var onnxRuntimeLib []byte -//go:embed assets/lib/linux-amd64/libonnxruntime_providers_shared.so +//go:embed assets/lib/linux-amd64/libonnxruntime_providers_shared.so.gz var onnxRuntimeProvidersLib []byte const onnxRuntimeLibName = "libonnxruntime.so" diff --git a/internal/embedding/lib_linux_arm64.go b/internal/embedding/lib_linux_arm64.go index 23283c2..090447a 100644 --- a/internal/embedding/lib_linux_arm64.go +++ b/internal/embedding/lib_linux_arm64.go @@ -6,10 +6,10 @@ import ( _ "embed" ) -//go:embed assets/lib/linux-arm64/libonnxruntime.so +//go:embed assets/lib/linux-arm64/libonnxruntime.so.gz var onnxRuntimeLib []byte -//go:embed assets/lib/linux-arm64/libonnxruntime_providers_shared.so +//go:embed assets/lib/linux-arm64/libonnxruntime_providers_shared.so.gz var onnxRuntimeProvidersLib []byte const onnxRuntimeLibName = "libonnxruntime.so" diff --git a/internal/embedding/lib_windows_amd64.go b/internal/embedding/lib_windows_amd64.go index 2d0351a..0e7808b 100644 --- a/internal/embedding/lib_windows_amd64.go +++ b/internal/embedding/lib_windows_amd64.go @@ -6,7 +6,7 @@ import ( _ "embed" ) -//go:embed assets/lib/windows-amd64/onnxruntime.dll +//go:embed assets/lib/windows-amd64/onnxruntime.dll.gz var onnxRuntimeLib []byte const onnxRuntimeLibName = "onnxruntime.dll" diff --git a/internal/embedding/service.go b/internal/embedding/service.go index 7ecb0a2..269a5ae 100644 --- a/internal/embedding/service.go +++ b/internal/embedding/service.go @@ -3,10 +3,12 @@ package embedding import ( "bytes" + "compress/gzip" "context" "crypto/sha256" "encoding/hex" "fmt" + "io" "os" "path/filepath" "strings" @@ -134,7 +136,7 @@ func (m *bgeModel) Dimensions() int { return EmbeddingDim } -// extractONNXLibrary extracts the embedded ONNX runtime library to a temp directory. +// extractONNXLibrary gunzips the embedded ONNX runtime library to a temp directory. // Uses content hash to avoid re-extracting if already present. func extractONNXLibrary() (string, error) { // Create a hash of the library content for cache key @@ -157,16 +159,14 @@ func extractONNXLibrary() (string, error) { } // Write main library - // #nosec G306 -- Shared library needs executable permission (0755) for dynamic linker - if err := os.WriteFile(libPath, onnxRuntimeLib, 0755); err != nil { + if err := writeGunzipped(libPath, onnxRuntimeLib); err != nil { return "", fmt.Errorf("write library: %w", err) } // Write providers library if present (Linux only) if len(onnxRuntimeProvidersLib) > 0 && onnxRuntimeProvidersLibName != "" { providersPath := filepath.Join(cacheDir, onnxRuntimeProvidersLibName) - // #nosec G306 -- Shared library needs executable permission (0755) for dynamic linker - if err := os.WriteFile(providersPath, onnxRuntimeProvidersLib, 0755); err != nil { + if err := writeGunzipped(providersPath, onnxRuntimeProvidersLib); err != nil { return "", fmt.Errorf("write providers library: %w", err) } } @@ -626,3 +626,23 @@ func (s *Service) EmbedBatchWithContext(ctx context.Context, texts []string) ([] func (s *Service) Close() error { return s.model.Close() } + +// writeGunzipped decompresses gz into path with the executable bit the dynamic linker needs. +func writeGunzipped(path string, gz []byte) error { + zr, err := gzip.NewReader(bytes.NewReader(gz)) + if err != nil { + return err + } + defer func() { _ = zr.Close() }() + + // #nosec G302 G304 -- shared library needs 0755; path is under our own cache dir + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) + if err != nil { + return err + } + if _, err := io.Copy(f, zr); err != nil { + _ = f.Close() + return err + } + return f.Close() +} diff --git a/scripts/download-onnx-libs.sh b/scripts/download-onnx-libs.sh index 83bfcc5..39e3bbe 100755 --- a/scripts/download-onnx-libs.sh +++ b/scripts/download-onnx-libs.sh @@ -51,9 +51,20 @@ get_installed_version() { fi } +# Gzip the libraries: Go embeds the .gz and gunzips once at first use, which +# shrinks the worker binary by roughly 25 MB. +compress_libs() { + local plat="$1" + local f + for f in "${ASSETS_DIR}/${plat}"/*.dylib "${ASSETS_DIR}/${plat}"/*.so "${ASSETS_DIR}/${plat}"/*.dll; do + [ -f "$f" ] && gzip -9 -n -f "$f" + done +} + # Write version file after successful download write_version_file() { local plat="$1" + compress_libs "$plat" echo "${ONNX_VERSION}" > "${ASSETS_DIR}/${plat}/.version" } @@ -110,9 +121,9 @@ download_windows_amd64() { lib_exists() { local plat="$1" case "$plat" in - darwin-*) [ -f "${ASSETS_DIR}/${plat}/libonnxruntime.dylib" ] ;; - linux-*) [ -f "${ASSETS_DIR}/${plat}/libonnxruntime.so" ] ;; - windows-*) [ -f "${ASSETS_DIR}/${plat}/onnxruntime.dll" ] ;; + darwin-*) [ -f "${ASSETS_DIR}/${plat}/libonnxruntime.dylib.gz" ] ;; + linux-*) [ -f "${ASSETS_DIR}/${plat}/libonnxruntime.so.gz" ] ;; + windows-*) [ -f "${ASSETS_DIR}/${plat}/onnxruntime.dll.gz" ] ;; *) return 1 ;; esac }