libembedding-ng 1.12.0__py3-none-macosx_15_0_universal2.whl

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.
@@ -0,0 +1,212 @@
1
+ """libembedding — Fast ONNX-based text, image, and sparse embeddings for Python."""
2
+
3
+ import logging
4
+ from importlib.metadata import PackageNotFoundError, version
5
+
6
+ try:
7
+ __version__ = version("libembedding-ng")
8
+ except PackageNotFoundError:
9
+ __version__ = "0.0.0"
10
+
11
+ from ._binding import ffi, lib
12
+ from .autotune import (
13
+ auto_select_model,
14
+ autotune,
15
+ autotune_unified,
16
+ cleanup_model_cache,
17
+ cleanup_model_cache_except,
18
+ clear_autotune_cache,
19
+ clear_autotune_cache_unified,
20
+ )
21
+ from .backend import detect_backend
22
+ from .benchmark import (
23
+ Benchmark,
24
+ BenchmarkResult,
25
+ ComparisonResult,
26
+ CorpusType,
27
+ HardwareInfo,
28
+ Metrics,
29
+ Objective,
30
+ cache_path,
31
+ clear_cache,
32
+ detect_hardware,
33
+ )
34
+ from .cache import EmbeddingCache, cache_config_default
35
+ from .exceptions import LembedError, LlamaError
36
+ from .gguf_inspect import (
37
+ SPARSE_FORMULA_SCALAR,
38
+ SPARSE_FORMULA_SPLADE,
39
+ SPARSE_FORMULA_UNKNOWN,
40
+ GgufCapabilities,
41
+ GgufDesc,
42
+ inspect_gguf,
43
+ )
44
+ from .image_embedding import ImageEmbedding, image_autotune
45
+ from .models import (
46
+ list_image_models,
47
+ list_reranker_models,
48
+ list_sparse_models,
49
+ list_text_models,
50
+ )
51
+ from .reranker import (
52
+ Reranker,
53
+ clear_reranker_autotune_cache,
54
+ reranker_auto_config,
55
+ reranker_auto_config_profile,
56
+ reranker_autotune,
57
+ reranker_autotune_constrained,
58
+ reranker_autotune_custom,
59
+ )
60
+ from .similarity import cosine_similarity, dot_product, euclidean_distance
61
+ from .sparse_text_embedding import (
62
+ SparseTextEmbedding,
63
+ sparse_autotune,
64
+ sparse_best_config,
65
+ )
66
+ from .text_embedding import ( # pyright: ignore[reportPrivateImportUsage]
67
+ TextEmbedding,
68
+ TextEmbeddingPool, # pyright: ignore[reportPrivateImportUsage]
69
+ )
70
+ from .types import (
71
+ ImageTuningResult,
72
+ ModelDesc,
73
+ ModelInfo,
74
+ ModelSelectionResult,
75
+ RerankerTuningResult,
76
+ RerankResult,
77
+ SparseEmbedding,
78
+ SparseTuningResult,
79
+ Stats,
80
+ TuningResult,
81
+ UnifiedTuningResult,
82
+ )
83
+
84
+ _logger = logging.getLogger(__name__)
85
+
86
+ # Version from C API (runtime)
87
+ try:
88
+ __version__ = lib.lembed_version().decode("utf-8", errors="replace")
89
+ except (AttributeError, OSError) as e:
90
+ _logger.debug("Could not get version from C API: %s", e)
91
+
92
+
93
+ def ggml_cpu_features() -> list[str]:
94
+ """Instruction sets of the ggml CPU backend that runs GGUF models.
95
+
96
+ The wheel ships one ggml CPU library per instruction-set level and ggml
97
+ loads the best match for this machine; this names the one it picked, e.g.
98
+ ``["SSE3", ..., "AVX512", "AVX512_VNNI", "LLAMAFILE", "REPACK"]``. Record
99
+ it with any GGUF benchmark: AVX2 and AVX-512 VNNI kernels are not
100
+ comparable. Loads the backend on first call. Empty when no CPU backend
101
+ could be loaded (a broken install).
102
+ """
103
+ return ffi.string(lib.lembed_ggml_cpu_features()).decode("ascii").split()
104
+
105
+
106
+ # Autotune modes (re-exported for convenience)
107
+ LEMBED_AUTOTUNE_QUICK = lib.LEMBED_AUTOTUNE_QUICK
108
+ LEMBED_AUTOTUNE_FULL = lib.LEMBED_AUTOTUNE_FULL
109
+
110
+ # Optimization objectives
111
+ LEMBED_OBJECTIVE_LATENCY = lib.LEMBED_OBJECTIVE_LATENCY
112
+ LEMBED_OBJECTIVE_THROUGHPUT = lib.LEMBED_OBJECTIVE_THROUGHPUT
113
+ LEMBED_OBJECTIVE_BALANCED = lib.LEMBED_OBJECTIVE_BALANCED
114
+ LEMBED_OBJECTIVE_MEMORY = lib.LEMBED_OBJECTIVE_MEMORY
115
+
116
+ # Task types for unified auto-tuner
117
+ LEMBED_TASK_EMBEDDING = lib.LEMBED_TASK_EMBEDDING
118
+ LEMBED_TASK_RERANKING = lib.LEMBED_TASK_RERANKING
119
+ LEMBED_TASK_IMAGE = lib.LEMBED_TASK_IMAGE
120
+ LEMBED_TASK_SPARSE = lib.LEMBED_TASK_SPARSE
121
+
122
+ # Reranker profiles
123
+ LEMBED_PROFILE_INTERACTIVE = lib.LEMBED_PROFILE_INTERACTIVE
124
+ LEMBED_PROFILE_BALANCED = lib.LEMBED_PROFILE_BALANCED
125
+ LEMBED_PROFILE_QUALITY = lib.LEMBED_PROFILE_QUALITY
126
+
127
+ # Sparse embedding storage formats
128
+ LEMBED_SPARSE_FORMAT_DICT = lib.LEMBED_SPARSE_FORMAT_DICT
129
+ LEMBED_SPARSE_FORMAT_INDEX_ORDER = lib.LEMBED_SPARSE_FORMAT_INDEX_ORDER
130
+ LEMBED_SPARSE_FORMAT_CSR = lib.LEMBED_SPARSE_FORMAT_CSR
131
+ LEMBED_SPARSE_FORMAT_NUMPY = lib.LEMBED_SPARSE_FORMAT_NUMPY
132
+
133
+ __all__ = [
134
+ "LEMBED_AUTOTUNE_FULL",
135
+ "LEMBED_AUTOTUNE_QUICK",
136
+ "LEMBED_OBJECTIVE_BALANCED",
137
+ "LEMBED_OBJECTIVE_LATENCY",
138
+ "LEMBED_OBJECTIVE_MEMORY",
139
+ "LEMBED_OBJECTIVE_THROUGHPUT",
140
+ "LEMBED_PROFILE_BALANCED",
141
+ "LEMBED_PROFILE_INTERACTIVE",
142
+ "LEMBED_PROFILE_QUALITY",
143
+ "LEMBED_SPARSE_FORMAT_CSR",
144
+ "LEMBED_SPARSE_FORMAT_DICT",
145
+ "LEMBED_SPARSE_FORMAT_INDEX_ORDER",
146
+ "LEMBED_SPARSE_FORMAT_NUMPY",
147
+ "LEMBED_TASK_EMBEDDING",
148
+ "LEMBED_TASK_IMAGE",
149
+ "LEMBED_TASK_RERANKING",
150
+ "LEMBED_TASK_SPARSE",
151
+ "SPARSE_FORMULA_SCALAR",
152
+ "SPARSE_FORMULA_SPLADE",
153
+ "SPARSE_FORMULA_UNKNOWN",
154
+ "Benchmark",
155
+ "BenchmarkResult",
156
+ "ComparisonResult",
157
+ "CorpusType",
158
+ "EmbeddingCache",
159
+ "GgufCapabilities",
160
+ "GgufDesc",
161
+ "HardwareInfo",
162
+ "ImageEmbedding",
163
+ "ImageTuningResult",
164
+ "LembedError",
165
+ "LlamaError",
166
+ "Metrics",
167
+ "ModelDesc",
168
+ "ModelInfo",
169
+ "ModelSelectionResult",
170
+ "Objective",
171
+ "RerankResult",
172
+ "Reranker",
173
+ "RerankerTuningResult",
174
+ "SparseEmbedding",
175
+ "SparseTextEmbedding",
176
+ "SparseTuningResult",
177
+ "Stats",
178
+ "TextEmbedding",
179
+ "TextEmbeddingPool",
180
+ "TuningResult",
181
+ "UnifiedTuningResult",
182
+ "auto_select_model",
183
+ "autotune",
184
+ "autotune_unified",
185
+ "cache_config_default",
186
+ "cache_path",
187
+ "cleanup_model_cache",
188
+ "cleanup_model_cache_except",
189
+ "clear_autotune_cache",
190
+ "clear_autotune_cache_unified",
191
+ "clear_cache",
192
+ "clear_reranker_autotune_cache",
193
+ "cosine_similarity",
194
+ "detect_backend",
195
+ "detect_hardware",
196
+ "dot_product",
197
+ "euclidean_distance",
198
+ "ggml_cpu_features",
199
+ "image_autotune",
200
+ "inspect_gguf",
201
+ "list_image_models",
202
+ "list_reranker_models",
203
+ "list_sparse_models",
204
+ "list_text_models",
205
+ "reranker_auto_config",
206
+ "reranker_auto_config_profile",
207
+ "reranker_autotune",
208
+ "reranker_autotune_constrained",
209
+ "reranker_autotune_custom",
210
+ "sparse_autotune",
211
+ "sparse_best_config",
212
+ ]
@@ -0,0 +1,179 @@
1
+ """Low-level cffi binding layer. Loads the shared library and exposes ffi/lib.
2
+
3
+ Auteur: David Orel
4
+ Version: 1.12.0
5
+
6
+ SPDX-License-Identifier: MIT
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import platform
13
+ import warnings
14
+ from pathlib import Path
15
+
16
+ import cffi
17
+
18
+ ffi = cffi.FFI()
19
+
20
+ _cdefs_path = Path(__file__).with_name("_cdefs.h")
21
+ ffi.cdef(_cdefs_path.read_text(encoding="utf-8"))
22
+
23
+
24
+ def _library_candidates(pkg_dir: Path, root: Path, ext: str) -> tuple[list[Path], int]:
25
+ """Candidate library paths in lookup order, and how many are package copies.
26
+
27
+ The package copies come first: they are what a wheel install ships. The
28
+ build-tree paths only exist in a source checkout.
29
+ """
30
+ packaged = [pkg_dir / f"libembedding{ext}", pkg_dir / "lib" / f"libembedding{ext}"]
31
+ build_tree = [
32
+ root / "build" / "Debug" / f"libembedding{ext}",
33
+ root / "build" / "Release" / f"libembedding{ext}",
34
+ root / "build" / "python" / f"libembedding{ext}",
35
+ root / "build" / f"libembedding{ext}",
36
+ root / "build3" / "Release" / f"libembedding{ext}",
37
+ root / "build3" / f"libembedding{ext}",
38
+ root / "build_test" / "Debug" / f"libembedding{ext}",
39
+ root / "build_test" / "Release" / f"libembedding{ext}",
40
+ ]
41
+ return packaged + build_tree, len(packaged)
42
+
43
+
44
+ def _pick_library(candidates: list[Path], packaged_count: int) -> Path | None:
45
+ """First existing candidate; warns when it is a package copy older than a build.
46
+
47
+ In a source checkout the package directory is the wheel staging area: a copy
48
+ left there goes stale as soon as the tree is rebuilt, and every test or
49
+ benchmark run without LIBEMBEDDING_LIBRARY then silently exercises old code.
50
+ """
51
+ existing = [c for c in candidates if c.is_file()]
52
+ if not existing:
53
+ return None
54
+ chosen = existing[0]
55
+ if chosen in candidates[:packaged_count]:
56
+ newer = [
57
+ c for c in existing
58
+ if c in candidates[packaged_count:]
59
+ and c.stat().st_mtime > chosen.stat().st_mtime
60
+ ]
61
+ if newer:
62
+ warnings.warn(
63
+ f"Loading {chosen}, which is older than the build output {newer[0]}. "
64
+ "Set LIBEMBEDDING_LIBRARY to the library to test, or remove the "
65
+ "stale copy.",
66
+ stacklevel=3,
67
+ )
68
+ return chosen
69
+
70
+
71
+ def _find_library() -> str:
72
+ # An explicit path wins: it is the only unambiguous choice in a source
73
+ # checkout that holds several builds.
74
+ override = os.environ.get("LIBEMBEDDING_LIBRARY")
75
+ if override:
76
+ if not Path(override).is_file():
77
+ raise OSError(f"LIBEMBEDDING_LIBRARY points to a missing file: {override}")
78
+ return str(Path(override).resolve())
79
+
80
+ ext = {"Darwin": ".dylib", "Linux": ".so", "Windows": ".dll"}.get(
81
+ platform.system(), ".so"
82
+ )
83
+ pkg_dir = Path(__file__).parent
84
+ root = pkg_dir.parent.parent.parent # project root (libembedding/)
85
+ chosen = _pick_library(*_library_candidates(pkg_dir, root, ext))
86
+ return str(chosen.resolve()) if chosen else f"libembedding{ext}"
87
+
88
+
89
+ # Runtime dependencies that must be discoverable before libembedding is loaded.
90
+ # On Windows the loader resolves a DLL's imports from the executable directory,
91
+ # the directories registered with SetDllDirectoryW, and PATH. The libcurl build
92
+ # that ships inside the package lives in the package directory, which is not the
93
+ # same directory as libembedding.dll in a source checkout, so both are registered
94
+ # (see _runtime_dirs for the bundled third_party/curl/bin location).
95
+ # ggml's CPU backend is not listed: with CPU dispatch it is one of several
96
+ # ggml-cpu-<variant> modules that ggml scores and loads itself, from the
97
+ # library's directory; preloading one here would pick the variant for it.
98
+ _DEPENDENT_DLLS = (
99
+ "onnxruntime.dll",
100
+ "onnxruntime_providers_shared.dll",
101
+ "ggml.dll",
102
+ "ggml-base.dll",
103
+ "llama.dll",
104
+ "libcurl-x64.dll",
105
+ "libcurl.dll",
106
+ )
107
+
108
+
109
+ def _runtime_dirs(lib_path: Path) -> list[Path]:
110
+ """Directories that may contain dependent shared libraries, in priority order.
111
+
112
+ The library's own directory comes first: a build tree places the matching
113
+ ggml, llama and ONNX Runtime copies next to libembedding. Other build
114
+ directories (build/bin/Debug, build/bin/Release) are deliberately absent:
115
+ taking a dependency from there mixes Debug with Release, or a stale build
116
+ with a fresh one, in the same process.
117
+ """
118
+ root = Path(__file__).parent.parent.parent.parent
119
+ dirs = [
120
+ lib_path.parent,
121
+ # Wheel layout, and the MSVC runtime a wheel bundles.
122
+ Path(__file__).parent,
123
+ # Source-checkout libcurl installed by scripts/fetch_windows_libcurl.ps1;
124
+ # absent from a wheel install, hence the is_dir() filter below.
125
+ root / "third_party" / "curl" / "bin",
126
+ ]
127
+ seen: set[Path] = set()
128
+ ordered: list[Path] = []
129
+ for d in dirs:
130
+ if d.is_dir() and d not in seen:
131
+ seen.add(d)
132
+ ordered.append(d)
133
+ return ordered
134
+
135
+
136
+ def _select_dependencies(dirs: list[Path]) -> list[Path]:
137
+ """One path per known dependency: the copy in the first directory holding it.
138
+
139
+ Windows resolves an import by module name to an already loaded DLL, so
140
+ whichever copy is preloaded first is the one libembedding binds to. Loading
141
+ a single copy per name makes that choice explicit and keeps every other copy
142
+ out of the process.
143
+ """
144
+ selected: list[Path] = []
145
+ for name in _DEPENDENT_DLLS:
146
+ for d in dirs:
147
+ if (d / name).is_file():
148
+ selected.append(d / name)
149
+ break
150
+ return selected
151
+
152
+
153
+ if platform.system() == "Windows":
154
+ import ctypes
155
+
156
+ _lib_path_str = _find_library()
157
+ _lib_path = Path(_lib_path_str)
158
+ if _lib_path.exists():
159
+ _dirs = _runtime_dirs(_lib_path)
160
+ for _dep_path in _select_dependencies(_dirs):
161
+ try:
162
+ ctypes.CDLL(str(_dep_path))
163
+ except OSError:
164
+ pass
165
+ for _dir in reversed(_dirs):
166
+ ctypes.windll.kernel32.AddDllDirectory(str(_dir))
167
+ os.environ["PATH"] = str(_dir) + os.pathsep + os.environ.get("PATH", "")
168
+ # SetDllDirectoryW keeps a single directory: the library's own.
169
+ ctypes.windll.kernel32.SetDllDirectoryW(str(_dirs[0]))
170
+ lib = ffi.dlopen(_lib_path_str)
171
+ else:
172
+ _lib_path_str = _find_library()
173
+ _lib_path = Path(_lib_path_str)
174
+ if _lib_path.exists():
175
+ _dirs = [str(d) for d in _runtime_dirs(_lib_path)]
176
+ _env_var = "DYLD_LIBRARY_PATH" if platform.system() == "Darwin" else "LD_LIBRARY_PATH"
177
+ _existing = os.environ.get(_env_var, "")
178
+ os.environ[_env_var] = os.pathsep.join([*_dirs, _existing]) if _existing else os.pathsep.join(_dirs)
179
+ lib = ffi.dlopen(_lib_path_str)