"Operate hnswlib 0.9.0 Python Index and BFIndex for typed vector
Scanned 9/8/2026
Install to Claude Code
npx -y skills add VectorSpaceLab/AREX-Skill --skill python-bindings --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Python Bindings?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/vectorspacelab-python-bindings)More formats (shields.io, HTML) on the badges page.
---
name: python-bindings
description: "Operate hnswlib 0.9.0 Python Index and BFIndex for typed vector
search, filtering, mutation, persistence, pickle, and recall checks."
metadata:
disco-role: operating
disable-model-invocation: true
license: Apache 2.0
---
# Python bindings
Use this sub-skill when a Python task mentions `hnswlib.Index`, `hnswlib.BFIndex`,
NumPy vectors, `knn_query`, `add_items`, `save_index`, `load_index`, deletion,
replacement, pickle, filters, or recall. The contract covers the CPU-native
hnswlib 0.9.0 extension and its three exposed spaces: `l2`, `ip`, and
`cosine`. It does not provide a CUDA API.
## Operating contract
- Work with an installed `hnswlib` extension and NumPy arrays. The package's
normal runtime dependency is NumPy; source builds additionally need the
C++/pybind11 toolchain described in [troubleshooting.md](references/troubleshooting.md).
- Treat labels as integer, non-negative external identifiers. Use explicit,
unique `np.int64` labels when persistence, updates, or external metadata must
remain stable.
- Keep vectors as finite numeric data with shape `(rows, dim)` or a single
vector of shape `(dim,)`. The binding force-casts input to contiguous
`float32`, but applications should validate dimensionality before querying.
- Keep mutation, querying, resizing, and serialization phases coordinated. The
extension releases the GIL around native work; Python-level safety is not a
substitute for the operation compatibility rules in [workflows.md](references/workflows.md).
## Standard workflow
1. **Normalize the request.** Record `space`, `dim`, capacity, label policy,
query `k`, filter cardinality, deletion/replacement needs, persistence path,
and the target recall or exactness requirement.
2. **Check import/build state.** Import `hnswlib` and NumPy in the intended
environment. If importing a source-built extension, use the compiler and
native-linkage diagnosis in [troubleshooting.md](references/troubleshooting.md).
3. **Construct and initialize.** Create `Index(space, dim)`, then call
`init_index(max_elements, M=16, ef_construction=200, random_seed=100,
allow_replace_deleted=False)`. `Index` is not ready for insertion until
`init_index` succeeds.
4. **Choose a stable data contract.** Convert vectors with
`np.asarray(data, dtype=np.float32)` and assert `data.ndim in (1, 2)` and
the trailing dimension equals `dim`. Use one scalar integer label only for
one vector; otherwise pass one label per row. Omitted labels are assigned by
the binding and are not a durable metadata scheme.
5. **Populate and tune.** Call `add_items(data, ids=None, num_threads=-1,
replace_deleted=False)`. Reusing an existing label updates its vector.
Set `index.ef` or call `set_ef`; choose `ef >= k` and increase it for recall
at the cost of search time. Set `num_threads`/`set_num_threads` deliberately.
6. **Query and validate.** Call `knn_query(data, k=1, num_threads=-1,
filter=None)`. The return is `(labels, distances)`, both shaped `(rows, k)`
even for a single-vector query. A filter is a Python callable over external
labels; use `num_threads=1` for filtered search and ensure every query has
at least `k` eligible live items.
7. **Inspect data and metadata.** Use explicit label arrays with
`get_items(ids, return_type="numpy"|"list")`; sort `get_ids_list()` before
comparing it. Check `space`, `dim`, `M`, `ef_construction`, `ef`,
`max_elements`, `element_count`, and their method equivalents as needed.
In this binding, `get_items(None)` does not mean “all labels”; retrieve all
vectors by passing `get_ids_list()` explicitly.
8. **Apply lifecycle mutations.** `mark_deleted(label)` hides a live label;
`unmark_deleted(label)` restores it. For slot reuse, enable
`allow_replace_deleted=True` at initialization or load, mark deletions, and
pass `replace_deleted=True` with new labels. Without that policy, replacement
raises; deletion alone does not create unbounded capacity.
9. **Persist or pickle safely.** Use a temporary/application-owned path with
`save_index`, then create a fresh `Index` and `load_index(path,
max_elements=0, allow_replace_deleted=...)`. Set `ef` again after
`load_index`: index-file persistence does not preserve it. `pickle.dumps`
and `pickle.loads` preserve the Python index state, including `ef`, but
pickle/get-index-state operations must not overlap `add_items`.
10. **Measure recall when approximate results matter.** Build a same-space,
same-dimension `BFIndex`, add the same labeled vectors, query both with a
valid `k`, and compare label overlap. BFIndex is an exact linear-scan
oracle, not a performance substitute. Use the bundled tiny checks first:
`python scripts/python_lifecycle_smoke.py --help` and then run the selected
smoke script from outside the checkout.
## API routing map
- Exact signatures, result shapes, properties, and BFIndex: [api-reference.md](references/api-reference.md).
- Build/query/persistence/mutation/recall recipes: [workflows.md](references/workflows.md).
- Shape, labels, dtype, metrics, normalization, and distances:
[data-and-distance-semantics.md](references/data-and-distance-semantics.md).
- Installation, import, native flags, errors, concurrency, and recovery:
[troubleshooting.md](references/troubleshooting.md).
- Deterministic executable checks:
`scripts/python_lifecycle_smoke.py`, `scripts/python_filter_smoke.py`,
`scripts/python_mutation_smoke.py`, and `scripts/python_pickle_smoke.py`.
## Hard guardrails
- Do not claim GPU or CUDA support. The exposed extension is CPU-native.
- Do not use `k` greater than the live/filtered population, or interpret an
exception about a contiguous result array as a partial result.
- Do not assume `ef` is automatically adequate; set `ef >= k` after every
index-file load and tune it against BFIndex when recall is material.
- Do not use negative labels, mismatched label counts, wrong vector dimensions,
or a scalar label for multiple rows. Validate query dimensions too: the
observed binding checks dimensions on insertion but does not provide the same
explicit query-dimension guard.
- Do not call `mark_deleted` twice, unmark a label that is not marked, replace
without the initialization/load policy, or insert beyond capacity without
resizing/loading with a larger capacity.
- Do not compare cosine `get_items` output directly to the original unnormalized
vectors; returned stored vectors are normalized.
- Do not overlap `add_items` with `knn_query`; concurrent additions are allowed
with other additions, and concurrent queries with other queries. Python
filters should run single-threaded. Resizing is not concurrent-safe with
adding/querying, and pickle/getIndexParams is not concurrent-safe with
adding.
## Completion checklist
Before handing off a Python integration, confirm:
- import and package/runtime facts were checked in the target environment;
- the metric, dimension, capacity, dtype, shapes, and labels are explicit;
- initialization precedes insertion and `ef >= k` is set for each query phase;
- result shapes and filter eligibility are asserted;
- explicit IDs are used for `get_items`, metadata, updates, and persistence;
- deletion/replacement flags match the intended lifecycle;
- file paths are temporary or application-owned and reload uses a compatible
metric/dimension;
- `ef` is restored after file loading, while pickle behavior is tested separately;
- BFIndex is used for exact comparison when a recall claim is required; and
- no unsupported CUDA, malformed-query, unsafe-concurrency, or dependency claim
has been smuggled into the integration.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!