metta-faiss 0.9.1__tar.gz

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,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: metta-faiss
3
+ Version: 0.9.1
4
+ Summary: faiss as a nearest-neighbour backend for a MeTTa embedding store
5
+ License-Expression: MIT
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Intended Audience :: Science/Research
9
+ Requires-Python: >=3.12
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: pymetta==0.9.1
12
+ Requires-Dist: faiss-cpu
13
+ Requires-Dist: numpy
14
+
15
+ # metta-faiss
16
+
17
+ faiss as a nearest-neighbour backend for a MeTTa embedding store.
18
+
19
+ `pymetta` names no third-party library. This package is one row against the `index` point, and the seat
20
+ finds it exactly as it finds a package written by somebody else: through the
21
+ `metta.extensions` entry point, loaded at the first dispatch that has no answer
22
+ without it, or through `import metta_faiss`, whose module body registers the same
23
+ rows.
24
+
25
+ ```sh
26
+ pip install metta-faiss # or: pip install 'pymetta[arrays]'
27
+ ```
28
+
29
+ Its tests are in `tests/`, and they run in the workspace suite:
30
+
31
+ ```sh
32
+ sh extensions/python/test.sh ext/metta-faiss
33
+ ```
@@ -0,0 +1,19 @@
1
+ # metta-faiss
2
+
3
+ faiss as a nearest-neighbour backend for a MeTTa embedding store.
4
+
5
+ `pymetta` names no third-party library. This package is one row against the `index` point, and the seat
6
+ finds it exactly as it finds a package written by somebody else: through the
7
+ `metta.extensions` entry point, loaded at the first dispatch that has no answer
8
+ without it, or through `import metta_faiss`, whose module body registers the same
9
+ rows.
10
+
11
+ ```sh
12
+ pip install metta-faiss # or: pip install 'pymetta[arrays]'
13
+ ```
14
+
15
+ Its tests are in `tests/`, and they run in the workspace suite:
16
+
17
+ ```sh
18
+ sh extensions/python/test.sh ext/metta-faiss
19
+ ```
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: metta-faiss
3
+ Version: 0.9.1
4
+ Summary: faiss as a nearest-neighbour backend for a MeTTa embedding store
5
+ License-Expression: MIT
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Intended Audience :: Science/Research
9
+ Requires-Python: >=3.12
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: pymetta==0.9.1
12
+ Requires-Dist: faiss-cpu
13
+ Requires-Dist: numpy
14
+
15
+ # metta-faiss
16
+
17
+ faiss as a nearest-neighbour backend for a MeTTa embedding store.
18
+
19
+ `pymetta` names no third-party library. This package is one row against the `index` point, and the seat
20
+ finds it exactly as it finds a package written by somebody else: through the
21
+ `metta.extensions` entry point, loaded at the first dispatch that has no answer
22
+ without it, or through `import metta_faiss`, whose module body registers the same
23
+ rows.
24
+
25
+ ```sh
26
+ pip install metta-faiss # or: pip install 'pymetta[arrays]'
27
+ ```
28
+
29
+ Its tests are in `tests/`, and they run in the workspace suite:
30
+
31
+ ```sh
32
+ sh extensions/python/test.sh ext/metta-faiss
33
+ ```
@@ -0,0 +1,10 @@
1
+ README.md
2
+ metta_faiss.py
3
+ pyproject.toml
4
+ metta_faiss.egg-info/PKG-INFO
5
+ metta_faiss.egg-info/SOURCES.txt
6
+ metta_faiss.egg-info/dependency_links.txt
7
+ metta_faiss.egg-info/entry_points.txt
8
+ metta_faiss.egg-info/requires.txt
9
+ metta_faiss.egg-info/top_level.txt
10
+ tests/test_faiss.py
@@ -0,0 +1,2 @@
1
+ [metta.extensions]
2
+ metta-faiss = metta_faiss
@@ -0,0 +1,3 @@
1
+ pymetta==0.9.1
2
+ faiss-cpu
3
+ numpy
@@ -0,0 +1 @@
1
+ metta_faiss
@@ -0,0 +1,86 @@
1
+ """Purpose: faiss as a nearest-neighbour backend of the MeTTa Python seat.
2
+
3
+ One row against the seat's `index` point, so an embedding store searches
4
+ through faiss when this package is installed. The row is NOT a fallback, which
5
+ is what makes `backend="auto"` prefer it over the generic Array API path
6
+ whatever order the two distributions loaded in; entry-point order is not
7
+ something a package can arrange.
8
+
9
+ NumPy is a dependency here and is not an integration: faiss' own interface
10
+ takes contiguous float32 NumPy arrays for `add` and `search`, so staging one is
11
+ part of calling faiss at all, and faiss-cpu depends on NumPy itself.
12
+
13
+ Assumes:
14
+ - a normalized matrix, which is the `index` point's contract: the backend
15
+ searches inner products and the store normalizes before it gets here
16
+ Guarantees:
17
+ - `available()` answers False rather than raising when faiss is absent, so
18
+ an embedding store falls through to the next backend instead of failing
19
+ [tested: tests/test_faiss.py::test_an_absent_faiss_declines_rather_than_raising;
20
+ commit=94057a0f073c0fab0a35c42beff2c324d8a0addd]
21
+ - faiss wins `backend="auto"` over the Array API fallback in either load
22
+ order [tested: tests/test_faiss.py::test_faiss_wins_auto_over_the_array_api_fallback;
23
+ commit=94057a0f073c0fab0a35c42beff2c324d8a0addd]
24
+ Open Obligations:
25
+ To Do: None
26
+ Hacks: None
27
+ Future Enhancements: None
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from typing import Any, Final
33
+
34
+ from metta import seam
35
+
36
+ _MISSING: Final = (
37
+ "the faiss embedding backend needs faiss-cpu; install pymetta[arrays]"
38
+ )
39
+ _NUMPY_MISSING: Final = (
40
+ "the faiss embedding backend stages its matrix as a contiguous float32 "
41
+ "NumPy array, which is faiss' own interface, and NumPy is not installed; "
42
+ "install pymetta[arrays]"
43
+ )
44
+
45
+ require_module = seam.at("module").call()
46
+ optional_module = seam.at("optional-module").call()
47
+
48
+
49
+ def _available() -> bool:
50
+ """Whether faiss can run here."""
51
+ return optional_module("faiss") is not None
52
+
53
+
54
+ def _build(matrix: Any) -> Any:
55
+ """An exact inner-product index over the normalized matrix."""
56
+ faiss = require_module("faiss", _MISSING)
57
+ numpy = require_module("numpy", _NUMPY_MISSING)
58
+ staged = numpy.ascontiguousarray(numpy.asarray(matrix, dtype=numpy.float32))
59
+ index = faiss.IndexFlatIP(staged.shape[1])
60
+ index.add(staged)
61
+ return index
62
+
63
+
64
+ def _search(index: Any, query: Any, count: int) -> list[tuple[int, float]]:
65
+ """(row, score) pairs best first, from the built index."""
66
+ numpy = require_module("numpy", _NUMPY_MISSING)
67
+ probe = numpy.ascontiguousarray(numpy.asarray(query, dtype=numpy.float32).reshape(1, -1))
68
+ scores, indexes = index.search(probe, count)
69
+ return [
70
+ (int(index), float(score))
71
+ for score, index in zip(scores[0], indexes[0], strict=True)
72
+ ]
73
+
74
+
75
+ def register() -> None:
76
+ """This package's one row, against the seat's `index` point."""
77
+ seam.index.register(
78
+ "faiss",
79
+ available=_available,
80
+ build=_build,
81
+ search=_search,
82
+ missing=_MISSING,
83
+ )
84
+
85
+
86
+ register()
@@ -0,0 +1,41 @@
1
+ # Purpose: package metta-faiss, one distribution for one library.
2
+ # Assumes: pymetta of the same version is installed; this is a workspace member
3
+ # released with it and pinned to it exactly.
4
+ # Guarantees: importing metta_faiss registers this package's rows, and so does the
5
+ # `metta.extensions` entry point below when a dispatch discovers it.
6
+ # Decides: the dependency set is this library and nothing else, which is what
7
+ # makes "the core names no library" hold without an allowlist.
8
+ [build-system]
9
+ requires = ["setuptools>=83"]
10
+ build-backend = "setuptools.build_meta"
11
+
12
+ [project]
13
+ name = "metta-faiss"
14
+ version = "0.9.1"
15
+ description = "faiss as a nearest-neighbour backend for a MeTTa embedding store"
16
+ readme = "README.md"
17
+ requires-python = ">=3.12"
18
+ license = "MIT"
19
+ dependencies = [
20
+ #: pymetta of exactly this version. A member is released with the core and
21
+ #: reads its seam, so a floating pin would let a row meet a seam that no
22
+ #: longer declares its point.
23
+ "pymetta==0.9.1",
24
+ "faiss-cpu",
25
+ "numpy",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 3 - Alpha",
29
+ "Programming Language :: Python :: 3",
30
+ "Intended Audience :: Science/Research",
31
+ ]
32
+
33
+ #: How a seat that never heard of this package finds it: one entry in the
34
+ #: group `metta.seam` reads, loaded at the first dispatch that has no answer
35
+ #: without it. The target is the MODULE, whose body registers on import, which
36
+ #: is the same door `import metta_faiss` opens.
37
+ [project.entry-points."metta.extensions"]
38
+ metta-faiss = "metta_faiss"
39
+
40
+ [tool.setuptools]
41
+ py-modules = ["metta_faiss"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,51 @@
1
+ """Purpose: prove the faiss row searches, and wins `backend="auto"`.
2
+
3
+ The second assertion is the load-bearing one. The Array API backend the array
4
+ layer registers is always available, so with both rows present the ORDER
5
+ decides `auto`, and order across distributions is not something a package can
6
+ arrange: the fallback flag is what makes the specific row win either way.
7
+ Open Obligations:
8
+ To Do: None
9
+ Hacks: None
10
+ Future Enhancements: None
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import metta_arrays
16
+ import metta_faiss
17
+ import metta_numpy # noqa: F401 -- the default array library the store needs
18
+ import pytest
19
+
20
+ from metta import S, seam
21
+
22
+ pytest.importorskip("faiss")
23
+
24
+
25
+ def test_the_row_is_registered_against_the_index_point():
26
+ """One row, named for the library, and NOT a fallback."""
27
+ row = seam.index.find("faiss")
28
+ assert row is not None
29
+ assert row.available() is True
30
+ assert not row.fallback
31
+
32
+
33
+ def test_faiss_wins_auto_over_the_array_api_fallback():
34
+ """Every non-fallback row is consulted before every fallback one."""
35
+ names = [row.name for row in seam.index.rows()]
36
+ assert names.index("faiss") < names.index("argsort")
37
+ assert seam.index.find("argsort").fallback is True
38
+
39
+
40
+ def test_an_embedding_store_searches_through_faiss(scratch_space):
41
+ """The store takes the backend by name and answers what faiss ranked."""
42
+ store = metta_arrays.EmbeddingStore(scratch_space, name="faiss_emb", backend="faiss")
43
+ store.add(S.dog, [1.0, 0.0])
44
+ store.add(S.cat, [0.0, 1.0])
45
+ assert [str(key) for key, _score in store.ranked([1.0, 0.0], 1)] == ["dog"]
46
+
47
+
48
+ def test_an_absent_faiss_declines_rather_than_raising(monkeypatch):
49
+ """available() answers False, so the store falls through to the next row."""
50
+ monkeypatch.setattr(metta_faiss, "optional_module", lambda _name: None)
51
+ assert seam.index.find("faiss").available() is False