Use embedded SABLE for local development and tests
Embedded SABLE runs VectorAmp's native vector index inside your Python or Node.js process for development and test workflows. It needs no API key, network request, external database service, or compiler on the target machine. Give the SDK a local directory, create a dataset with a fixed vector dimension, insert precomputed vectors, and use the normal VectorAmp dataset API for vector search, metadata filters, index status, optimization, persistence, and reopen.
Use embedded SABLE for local development, evaluation, CI, automated tests, benchmarks, demos, and non-production staging. Production applications must use a separately licensed hosted or customer-operated VectorAmp SABLE system.
Development and test license
The Python and Node.js SDKs remain available under Apache-2.0. Their optional native embedded runtime is distributed under the separate proprietary VectorAmp Embedded SABLE Development License.
The runtime permits internal non-production development and test use. It does not permit production traffic, customer-facing deployment, hosted or managed-service use, resale, or external redistribution. It performs no license-network request or telemetry call. Contact VectorAmp for hosted, customer-operated, OEM, edge, or other production rights.
The technical limits below are engineering safety boundaries, not commercial entitlements. In particular, the five-million-vector ceiling does not permit production use below that size.
Supported platforms
The package installs a precompiled native runtime. It does not compile Rust or download an executable the first time your application starts.
| Operating system | Architecture | Python | Node.js |
|---|---|---|---|
| Linux with glibc 2.17+ | x86-64 | CPython 3.9–3.15 | Node.js 18, 20, 22, 24, or 26 |
| Linux with glibc 2.17+ | arm64 | CPython 3.9–3.15 | Node.js 18, 20, 22, 24, or 26 |
| macOS | Apple Silicon arm64 | CPython 3.9–3.15 | Node.js 18, 20, 22, 24, or 26 |
Windows, Linux musl distributions such as Alpine, and Intel macOS are not currently supported.
Install
Python
Install the SDK with its embedded runtime extra:
python -m pip install "vectoramp[embedded]"
Node.js
Install the VectorAmp SDK. npm selects the native package for the current supported platform:
npm install @vectoramp/vectoramp
Python quickstart
This example creates a durable local development database, inserts vectors, waits for automatic index training, performs a filtered search, checkpoints the optimized index, and reopens the same dataset.
from __future__ import annotations
import math
import time
from pathlib import Path
from vectoramp import VectorAmp
root = Path.home() / ".local" / "share" / "my-app" / "sable"
client = VectorAmp.embedded(root)
dataset = client.datasets.create(
name="products",
dim=4,
metric="cosine",
metadata_schema={
"tenant": "string",
"price": "f64",
},
tuning={"training_threshold": 256},
)
vectors = [
{
"id": vector_id,
"values": [
math.cos((vector_id - 1) * 0.01),
math.sin((vector_id - 1) * 0.01),
0.0,
0.0,
],
"metadata": {
"tenant": "acme" if vector_id % 2 == 0 else "other",
"price": str(vector_id / 10),
},
}
for vector_id in range(1, 257)
]
print(dataset.insert(vectors)) # {"inserted": 256}
results = dataset.search(
[1.0, 0.0, 0.0, 0.0],
top_k=10,
filters={"tenant": "acme"},
advanced_filters=[
{"field": "price", "op": "gte", "value": 1.0},
{"field": "price", "op": "lte", "value": 20.0},
],
include_metadata=True,
)
print(results["results"])
# Search works immediately in exact mode. At the configured threshold, SABLE
# trains the compressed index in the background and then reports "indexed".
deadline = time.monotonic() + 120
status = dataset.index_status()
while status["state"] not in {"indexed", "failed"} and time.monotonic() < deadline:
time.sleep(0.25)
status = dataset.index_status()
if status["state"] != "indexed":
raise RuntimeError(f"SABLE training failed or timed out: {status}")
# Explicit optimization builds or checkpoints the durable index.
print(dataset.optimize())
dataset_id = dataset.id
client.close()
# A new client can reopen the catalog and dataset from the same root directory.
reopened = VectorAmp.embedded(root)
restored = reopened.datasets.get(dataset_id)
print(reopened.datasets.stats(dataset_id))
print(restored.search([1.0, 0.0, 0.0, 0.0], top_k=5))
reopened.close()
Use a persistent local directory when a development fixture must survive process restarts. A temporary directory is useful for isolated tests, but its database disappears when the directory is deleted.
Node.js quickstart
The Node SDK exposes the same dataset workflow. Native SABLE operations run through a worker so training, search, and checkpoint work do not block the main event loop.
import { mkdir } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { VectorAmp } from '@vectoramp/vectoramp';
const root = join(homedir(), '.local', 'share', 'my-app', 'sable');
await mkdir(root, { recursive: true });
const client = await VectorAmp.embedded(root);
const dataset = await client.datasets.create({
name: 'products',
dim: 4,
metric: 'cosine',
metadataSchema: {
tenant: 'string',
price: 'f64'
},
tuning: { trainingThreshold: 256 }
});
const vectors = Array.from({ length: 256 }, (_, index) => ({
id: index + 1,
values: [Math.cos(index * 0.01), Math.sin(index * 0.01), 0, 0],
metadata: {
tenant: (index + 1) % 2 === 0 ? 'acme' : 'other',
price: String((index + 1) / 10)
}
}));
console.log(await dataset.insert(vectors)); // { inserted: 256 }
const results = await dataset.search({
vector: [1, 0, 0, 0],
topK: 10,
filters: { tenant: 'acme' },
advancedFilters: [
{ field: 'price', op: 'gte', value: 1 },
{ field: 'price', op: 'lte', value: 20 }
],
includeMetadata: true
});
console.log(results.results);
const deadline = Date.now() + 120_000;
let status = await dataset.indexStatus();
while (!['indexed', 'failed'].includes(String(status.state)) && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 250));
status = await dataset.indexStatus();
}
if (status.state !== 'indexed') {
throw new Error(`SABLE training failed or timed out: ${JSON.stringify(status)}`);
}
console.log(await dataset.optimize());
const datasetId = dataset.id;
await client.close();
const reopened = await VectorAmp.embedded(root);
const restored = await reopened.datasets.get(datasetId);
console.log(await restored.indexStatus());
console.log(await restored.search({ vector: [1, 0, 0, 0], topK: 5 }));
await reopened.close();
Index lifecycle
indexStatus() in Node.js or index_status() in Python reports one of four states:
| State | Meaning |
|---|---|
exact | Vectors are durable and searchable before the compressed index has been trained. |
training | SABLE is building its trained index in the background. Searches continue while training runs. |
indexed | The trained index is active. |
failed | Background training failed. Inspect trainingError in Node.js or training_error in Python. |
The default training threshold is 10,000 vectors. You can lower it for small local datasets or tests, but it cannot be lower than 256. Calling optimize() explicitly trains when enough vectors exist and checkpoints the durable index.
status = dataset.index_status()
print(status["state"], status["vector_count"], status["training_threshold"])
```ts
const status = await dataset.indexStatus();
console.log(status.state, status.vectorCount, status.trainingThreshold);
Metadata filters
Declare metadata fields when you create the dataset. Embedded SABLE supports string, u32, i32, i64, f32, and f64 fields.
- Use
filtersfor exact equality constraints. - Use
advanced_filtersin Python oradvancedFiltersin Node.js for typed numeric comparisons:eq,gt,gte,lt, andlte. - Each advanced filter uses
field,op, andvalue. The SDK infers the numeric kind from the dataset's declared metadata schema; an explicitkind, when supplied, must match it. - Multiple equality and advanced filters are combined with AND semantics.
- Invalid operators, missing values, undeclared fields, and type mismatches fail the search instead of being silently ignored.
- Metadata values are stored with each vector; use a consistent representation when inserting and filtering.
- Result metadata is included by default. Set
include_metadata=Falsein Python orincludeMetadata: falsein Node.js to omit it.
Each result contains id, distance, score, and—when requested—metadata. score follows the hosted VectorAmp contract and equals distance; smaller values rank first. For cosine datasets, distance is 1 - cosine_similarity, so identical or positively collinear vectors have distance 0.0.
Python:
results = dataset.search(
query=[0.2, 0.4, 0.6, 0.8],
top_k=20,
filters={"tenant": "acme"},
advanced_filters=[
{"field": "price", "op": "gte", "value": 10.0},
{"field": "price", "op": "lt", "value": 50.0},
],
)
Node.js:
const results = await dataset.search({
vector: [0.2, 0.4, 0.6, 0.8],
topK: 20,
filters: { tenant: 'acme' },
advancedFilters: [
{ field: 'price', op: 'gte', value: 10 },
{ field: 'price', op: 'lt', value: 50 }
]
});
Persistence and shutdown
The root directory passed to embedded(...) owns the local catalog and every dataset created by that client.
- Reuse the same root path to reopen existing datasets.
- Store the entire root directory together; do not edit
catalog.jsonor individual dataset files. - Call
close()during an orderly shutdown and before copying or backing up the directory. - Keep the vector dimension, metric, and metadata schema stable for an existing dataset.
- Pin compatible SDK/runtime versions in development and CI environments, and test fixture reopen before upgrading.
- Deleting a dataset removes its local dataset directory and catalog entry.
Embedded SABLE uses write-ahead durability for acknowledged inserts and versioned index checkpoints. A reopened database recovers durable vectors even if the trained checkpoint is older than the latest inserts.
Embedded and hosted capabilities
Embedded mode deliberately implements the portable local dataset/vector subset of the VectorAmp SDK.
| Available locally | Hosted VectorAmp only |
|---|---|
| Create, list, get, and delete datasets | Managed embedding models and text embedding |
| Insert precomputed vectors | add_texts / addTexts |
| Vector search | Ingestion sources, pipelines, and schedules |
| Equality and typed numeric comparison filters | Intelligence ask, agents, and cited answers |
| Dataset statistics and index status | Organization secrets and OAuth connections |
| Automatic training and explicit optimization | Retained source-document APIs |
| Durable close and reopen | Multi-node scaling and managed operations |
Embedded mode therefore requires numeric vectors for both inserts and queries. Generate embeddings in your application or another model service, then pass those vectors to the dataset.
Use hosted or customer-operated VectorAmp SABLE for every production workload, including workloads that need managed ingestion, embedding, Intelligence, multi-user API access, or distributed scale. Because both modes use the normal SDK dataset objects, application code that stays inside the shared vector subset can switch transports with a small initialization change.
Runtime limits
The embedded edition enforces these per-dataset limits:
- Vector dimension: 1–4,096.
- Vectors: up to 5,000,000.
- Search results:
top_kup to 10,000. - Declared metadata fields: up to 64.
- Metadata: up to 64 KiB per vector; every declared field is required.
- Training threshold: 256–5,000,000 vectors.
- Metrics:
cosineandl2. - Vector IDs: non-negative integers in Python; non-negative safe integers or
bigintvalues in Node.js. - Ownership: one process per dataset.
- Mutation: insert-only vector IDs; duplicate IDs, per-vector delete, and upsert are unsupported.
- Schema: vector dimension, metric, and declared metadata schema are fixed at dataset creation.
Local development and CI checklist
- Choose a durable application-data directory with enough space for vectors, metadata, write-ahead data, and checkpoints.
- Ensure only the intended application instance owns the directory.
- Validate vector dimensions before insertion.
- Declare every field used by typed advanced filters.
- Monitor
indexStatus()/index_status()and surface thefailedstate. - Call
close()on normal shutdown. - Copy the complete root directory only while the client is closed.
- Test database reopen whenever you change the SDK/runtime version.