Overview
Config is a fluent (method-chaining) builder that collects all settings for a database before it is opened. You can pass a Config directly to elips.open_with_config(), or use the convenience keyword arguments on elips.open() which constructs a Config internally.
from elips import Config, GraphParams
cfg = (
Config()
.dimension(768)
.metric("cosine")
.index("graph")
.graph_params(GraphParams(max_connections=32, ef_construction=300))
.durability("standard")
.metadata_acceleration(True)
)
import elips
db = elips.open_with_config("/data/vectors.elips", cfg)Persisted Identity
Certain fields are written to the database's on-disk metadata on first open and cannot be changed for the lifetime of the database file. These are called persisted identity fields:
- dimension
- metric
- index type (
"graph"vs"exact")
If you open an existing database with a Config that conflicts with these stored values, ELIPS raises a ConfigError immediately — before any I/O is done on the data files. This prevents silent data corruption.
Non-identity fields (durability, access_mode, graph_params, metadata_acceleration, embedder) can be changed between opens.
# First open — establishes identity
db = elips.open("/data/v.elips", dimension=768, metric="cosine", index="graph")
db.close()
# Reopen with different durability — fine
db = elips.open("/data/v.elips", durability="relaxed")
# Reopen with different dimension — ConfigError
try:
db = elips.open("/data/v.elips", dimension=512)
except elips.ConfigError as e:
print(e) # Dimension mismatch: expected 768, got 512Config() Constructor
from elips import Config
cfg = Config()Creates a blank Config with all fields at their default values. Every subsequent method call returns self, enabling chaining. You must call at least .dimension() when creating a new database.
Config.dimension(dim) -> Config
Config.dimension(dim: int) -> ConfigSets the vector dimensionality. Required for new databases; ignored (but validated) on reopen. Valid range: 1–65 536.
- Persisted identity — cannot be changed after first open.
- All vaults within a database share this dimension.
- Common values:
384(MiniLM-L6),768(BERT-base),1536(OpenAI text-embedding-3-small),3072(text-embedding-3-large).
Config.metric(metric_str) -> Config
Config.metric(metric_str: str) -> Config
# metric_str ∈ {"cosine", "euclidean", "dot_product"}Chooses the distance function used for all vector comparisons. Must match the metric used to train/produce the embeddings.
"cosine"(default) — Angular distance. Normalises vectors before comparison. Best for sentence embeddings from models like BERT, OpenAI, and Sentence-Transformers."euclidean"— L2 distance. Does not normalise. Use when magnitude matters (e.g., audio embeddings, pixel features)."dot_product"— Inner product. Highest score = most similar (note: results are returned in descending order unlike cosine/euclidean). Appropriate for embeddings already normalised to unit length where speed matters.
Persisted identity — cannot be changed after first open.
Config.index(type_str) -> Config
Config.index(type_str: str) -> Config
# type_str ∈ {"graph", "exact"}"graph"(default) — HNSW approximate nearest-neighbour index. O(log n) search time. Suitable for > 10 k vectors."exact"— Brute-force linear scan. O(n) search time. Perfect recall (100 %). Appropriate only for small datasets (< ~50 k vectors) or ground-truth benchmarking.
Persisted identity — cannot be changed after first open.
Config.graph_params() & GraphParams
Config.graph_params(params: GraphParams) -> ConfigGraphParams
from elips import GraphParams
params = GraphParams(
max_connections: int = 16,
ef_construction: int = 200,
ef_search: int = 50,
compaction_ratio: float = 0.2,
)Parameters in depth
max_connections(M) — Maximum number of bi-directional links each node has at each HNSW layer. Higher values improve recall and graph connectivity but increase memory usage (~8 bytes per connection per record) and slow down insertions.
Typical values:8–64. Default:16.ef_construction— Beam width during graph construction. Controls how many candidate neighbours are explored when inserting a new node. Higher values produce a better-quality graph (higher recall at search time) at the cost of slower insertion throughput.
Rule of thumb:ef_construction≥2 × max_connections. Default:200.ef_search— Beam width during query. Controls the search-time recall/latency trade-off. Can be tuned at runtime by changingGraphParamswithout rebuilding the graph (unlikemax_connectionsandef_construction).
Must satisfyef_search≥top(number of results requested). Default:50.compaction_ratio— Fraction of live records that may be tombstoned (erased but not yet purged) before an automatic graph rebuild is triggered on the next write. Set to0.0to disable automatic compaction. Default:0.2(20 %).
# High-recall configuration (favours quality over speed)
high_recall = GraphParams(
max_connections=32,
ef_construction=400,
ef_search=200,
)
# Low-latency configuration (favours speed, slight recall trade-off)
low_latency = GraphParams(
max_connections=16,
ef_construction=100,
ef_search=25,
)
cfg = Config().dimension(768).metric("cosine").graph_params(high_recall)Config.durability(level_str) -> Config
Config.durability(level_str: str) -> Config
# level_str ∈ {"paranoid", "standard", "relaxed", "ephemeral"}Controls how aggressively the WAL is flushed to disk after each write. Higher durability = lower write throughput = stronger crash safety.
"paranoid"—fsyncafter every write. Maximum durability. Use when data loss is unacceptable (financial records, embeddings that are expensive to regenerate)."standard"(default) — Buffered WAL writes with periodicfsync. Good balance of throughput and durability. May lose up to ~1 s of writes on a power failure."relaxed"— OS page-cache backed writes,fsynconly on checkpoint. High throughput. May lose several seconds of writes on crash."ephemeral"— No WAL, no persistence. Data is kept in memory only and is lost when the process exits. Equivalent topath=":memory:"but can be used with a path argument (which is then ignored for storage purposes).
Config.access_mode(mode_str) -> Config
Config.access_mode(mode_str: str) -> Config
# mode_str ∈ {"read_write", "read_only"}"read_write"(default) — Full read/write access. Acquires an exclusive process lock on the database directory."read_only"— Allows concurrent read access without an exclusive lock. Multiple processes can open the same database read-only simultaneously. Attempting any write operation raisesPermissionError.
Storage Options
Config.segmented_storage(enabled: bool) -> Config
When True, ELIPS splits the vector data file across multiple fixed-size memory-mapped segments rather than a single large mapping. This allows databases larger than the available contiguous virtual address space on 32-bit platforms. On 64-bit systems this option has no practical benefit and adds a small indirection overhead. Defaults to False.
Config.metadata_acceleration(enabled: bool) -> Config
When True (default), ELIPS maintains a secondary in-memory hash map of all record metadata fields. This enables O(1) predicate evaluation during search filtering rather than decoding each record's metadata from the data file on every comparison.
Memory cost is approximately 64 + (avg_metadata_bytes) per record. Disable only if you are operating under severe memory constraints and primarily use full-scan workloads.
Text Embedder
Config.text_embedder(callable, *, provider, model, revision, dimension) -> Config
Config.text_embedder(
fn: callable,
*,
provider: str = "",
model: str = "",
revision: str = "",
dimension: int = 0,
) -> ConfigAttaches a custom Python callable as the text embedder. The callable must accept a single str argument and return a list[float] or np.ndarray of the correct dimension.
The optional keyword arguments populate the EmbeddingLineage stored with each record when using text APIs — useful for auditing which model version produced an embedding.
import openai, elips
client = openai.OpenAI()
def embed(text: str) -> list[float]:
resp = client.embeddings.create(model="text-embedding-3-small", input=text)
return resp.data[0].embedding
cfg = (
Config()
.dimension(1536)
.metric("cosine")
.text_embedder(
embed,
provider="openai",
model="text-embedding-3-small",
revision="2024-01",
dimension=1536,
)
)Config.local_text_embedder(LocalEmbedderConfig) -> Config
Attaches a built-in local embedder backed by a bundled ONNX model. Requires the optional elips[local] package extra.
Config.auto_text_embedder(enabled: bool) -> Config
When True, ELIPS automatically selects and attaches a bundled lightweight embedder for new databases that do not have one configured. On subsequent reopens, the same embedder is reused (identified from persisted lineage metadata). Useful for prototyping when you don't want to wire up an external embedding service.
LocalEmbedderConfig
from elips import LocalEmbedderConfig
lcfg = LocalEmbedderConfig(
model: str = "default",
revision: str = "v1",
storage_path: str = "",
dimension: int = 0,
)Configuration for the bundled ONNX-backed local embedder:
model— Model identifier."default"selects the recommended general-purpose model bundled with the current ELIPS release.revision— Model version tag. Used for lineage tracking.storage_path— Directory where model weights are cached after first download. Empty string uses the default platform cache directory (e.g.,~/.cache/elips/modelson Linux/macOS).dimension— Override output dimension.0uses the model's native dimension.
from elips import Config, LocalEmbedderConfig
cfg = (
Config()
.dimension(384)
.metric("cosine")
.local_text_embedder(LocalEmbedderConfig(model="default", revision="v1"))
)Config.gpu(GpuConfig) -> Config
from elips import GpuConfig
cfg = Config().dimension(1536).metric("cosine").gpu(GpuConfig(device_id=0))Enables GPU-accelerated distance computations and graph construction. Available only in GPU-enabled ELIPS builds (elips[gpu] package extra). Raises RuntimeError if the build does not include GPU support.
GpuConfig fields:
device_id: int = 0— CUDA device index.memory_fraction: float = 0.8— Fraction of GPU memory ELIPS may use.fallback_to_cpu: bool = True— If the GPU is unavailable, fall back to CPU silently rather than raising.
Read Properties
After a database is opened, the resolved Config is available via db.config and exposes read-only properties:
dimension_val: int— Resolved dimension.metric_val: str— Resolved metric string.index_val: str— Resolved index type string.graph_params_val: GraphParams— Active graph parameters.durability_val: str— Active durability level.access_mode_val: str— Active access mode.has_text_embedder: bool— Whether a text embedder is configured.text_embedder_info: dict | None— Provider/model/revision info, if an embedder is attached.
db = elips.open_with_config("/data/v.elips", cfg)
c = db.config
print(c.dimension_val) # 768
print(c.metric_val) # 'cosine'
print(c.graph_params_val.ef_search) # 50
print(c.has_text_embedder) # True/FalseConfigError
elips.ConfigError (subclass of ValueError) is raised when:
- A persisted identity field conflicts with the value stored on disk (dimension, metric, index type mismatch).
- An invalid value is supplied (e.g.,
dimension=0, unknown metric string,ef_search < top). text_embedderis configured but the callable returns vectors of wrong dimension.
try:
db = elips.open("/data/v.elips", dimension=512)
except elips.ConfigError as e:
# e.field → 'dimension'
# e.stored → 768
# e.given → 512
print(f"Config conflict on field '{e.field}': stored={e.stored}, given={e.given}")Two-Phase Ingest/Serve Pattern
A common production pattern is to separate the ingest phase (writing many vectors) from the serve phase (read-only query serving) using two different Config objects against the same database path:
# --- INGEST PHASE ---
# Use relaxed durability for maximum write throughput.
# Disable auto-compaction during bulk load (compaction_ratio=0.0).
ingest_cfg = (
Config()
.dimension(1536)
.metric("cosine")
.index("graph")
.graph_params(GraphParams(max_connections=32, ef_construction=400, compaction_ratio=0.0))
.durability("relaxed")
.metadata_acceleration(True)
)
db = elips.open_with_config("/data/prod.elips", ingest_cfg)
vault = db.vault("embeddings")
# ... bulk insert via place_many() ...
# Compact and checkpoint after ingest
vault.rebuild_index()
db.compact()
db.checkpoint()
db.close()
# --- SERVE PHASE ---
# Reopen with paranoid durability + optimal ef_search, read-only access.
serve_cfg = (
Config()
.graph_params(GraphParams(ef_search=100))
.durability("standard")
.access_mode("read_only")
)
db = elips.open_with_config("/data/prod.elips", serve_cfg)Full Examples
Minimal new database
import elips
from elips import Config
db = elips.open_with_config(
"/data/simple.elips",
Config().dimension(384).metric("cosine"),
)Production configuration from environment
import os, elips
from elips import Config, GraphParams
def build_config() -> Config:
return (
Config()
.dimension(int(os.environ.get("ELIPS_DIM", "768")))
.metric(os.environ.get("ELIPS_METRIC", "cosine"))
.index("graph")
.graph_params(GraphParams(
max_connections=int(os.environ.get("ELIPS_M", "16")),
ef_construction=int(os.environ.get("ELIPS_EF_CONSTRUCTION", "200")),
ef_search=int(os.environ.get("ELIPS_EF_SEARCH", "50")),
))
.durability(os.environ.get("ELIPS_DURABILITY", "standard"))
.metadata_acceleration(True)
)
db = elips.open_with_config(os.environ["ELIPS_PATH"], build_config())