simplify edge config (#7933)

* simplify edge config

* Accept default vector config or explicit vector config map when creating `EdgeConfig`

* fixup! Accept default vector config or explicit vector config map when creating `EdgeConfig`

Update examples

* upd tests

* bump edge version to v0.5.0

---------

Co-authored-by: Roman Titov <ffuugoo@users.noreply.github.com>
This commit is contained in:
Andrey Vasnetsov
2026-01-19 18:32:11 +01:00
committed by GitHub
parent a2550eb938
commit d9393acac6
9 changed files with 75 additions and 59 deletions

2
Cargo.lock generated
View File

@@ -5315,7 +5315,7 @@ dependencies = [
[[package]]
name = "qdrant-edge-py"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"ahash",
"bytemuck",

View File

@@ -1,6 +1,6 @@
[package]
name = "qdrant-edge-py"
version = "0.4.0"
version = "0.5.0"
authors = ["Qdrant Team <info@qdrant.tech>"]
license = "Apache-2.0"
edition = "2024"

View File

@@ -16,20 +16,11 @@ def load_new_shard():
os.makedirs(DATA_DIRECTORY)
# Load Qdrant Edge shard
config = SegmentConfig(
vector_data={
"": VectorDataConfig(
size=4,
distance=Distance.Dot,
storage_type=VectorStorageType.ChunkedMmap,
index=PlainIndexConfig(),
quantization_config=None,
multivector_config=None,
datatype=None,
),
},
sparse_vector_data={},
payload_storage_type=PayloadStorageType.InRamMmap,
config = EdgeConfig(
vector_data=VectorDataConfig(
size=4,
distance=Distance.Dot
)
)
return EdgeShard(DATA_DIRECTORY, config)

View File

@@ -147,4 +147,14 @@ print(f"Total points count: {count}")
print("---- info ----")
info = shard.info()
print(info)
print("---- Close and reopen shard ----")
shard.close()
reopened_shard = EdgeShard(DATA_DIRECTORY)
info = reopened_shard.info()
print(info)

View File

@@ -3,33 +3,27 @@
from qdrant_edge import *
config = SegmentConfig(
vector_data = {
"": VectorDataConfig(
size = 128,
distance = Distance.Cosine,
storage_type = VectorStorageType.ChunkedMmap,
index = HnswIndexConfig(
m = 16,
ef_construct = 16,
full_scan_threshold = 1024,
on_disk = False,
payload_m = 16,
inline_storage = False,
),
)
},
vector_data = VectorDataConfig(
size = 128,
distance = Distance.Cosine,
index = HnswIndexConfig(
m = 16,
ef_construct = 16,
full_scan_threshold = 1024,
on_disk = False,
payload_m = 16,
inline_storage = False,
),
),
sparse_vector_data = {
"sparse": SparseVectorDataConfig(
index = SparseIndexConfig(
full_scan_threshold = 1024,
index_type = SparseIndexType.MutableRam,
datatype = VectorStorageDatatype.Float32,
),
storage_type = SparseVectorStorageType.Mmap,
modifier = Modifier.Idf,
)
},
payload_storage_type = PayloadStorageType.Mmap,
)
print(config)

View File

@@ -15,24 +15,30 @@ pub use self::sparse_vector_data::*;
pub use self::vector_data::*;
use crate::repr::*;
#[pyclass(name = "SegmentConfig")]
#[pyclass(name = "EdgeConfig")]
#[derive(Clone, Debug, Into, TransparentWrapper)]
#[repr(transparent)]
pub struct PySegmentConfig(SegmentConfig);
pub struct PyEdgeConfig(SegmentConfig);
#[pyclass_repr]
#[pymethods]
impl PySegmentConfig {
impl PyEdgeConfig {
#[new]
#[pyo3(signature = (vector_data, sparse_vector_data=None))]
pub fn new(
#[pyo3(from_py_with = vector_data_config_helper)]
// `vector_data_config_helper` accepts either
// - a single default vector config: `VectorDataConfig(...)`
// - or a map of named vector configs: `{ "": VectorDataConfig(...), "named_vector": VectorDataConfig(...) }`
vector_data: HashMap<String, PyVectorDataConfig>,
sparse_vector_data: HashMap<String, PySparseVectorDataConfig>,
payload_storage_type: PyPayloadStorageType,
sparse_vector_data: Option<HashMap<String, PySparseVectorDataConfig>>,
) -> Self {
Self(SegmentConfig {
vector_data: PyVectorDataConfig::peel_map(vector_data),
sparse_vector_data: PySparseVectorDataConfig::peel_map(sparse_vector_data),
payload_storage_type: PayloadStorageType::from(payload_storage_type),
sparse_vector_data: PySparseVectorDataConfig::peel_map(
sparse_vector_data.unwrap_or_default(),
),
payload_storage_type: PayloadStorageType::Mmap,
})
}
@@ -56,7 +62,7 @@ impl PySegmentConfig {
}
}
impl PySegmentConfig {
impl PyEdgeConfig {
fn _getters(self) {
// Every field should have a getter method
let SegmentConfig {
@@ -111,3 +117,25 @@ impl From<PyPayloadStorageType> for PayloadStorageType {
}
}
}
fn vector_data_config_helper(
config: &Bound<'_, PyAny>,
) -> PyResult<HashMap<String, PyVectorDataConfig>> {
#[derive(FromPyObject)]
enum Helper {
Default(PyVectorDataConfig),
Explicit(HashMap<String, PyVectorDataConfig>),
}
let config = match config.extract()? {
Helper::Default(default) => {
let mut config = HashMap::new();
config.insert("".into(), default);
config
}
Helper::Explicit(config) => config,
};
Ok(config)
}

View File

@@ -36,15 +36,11 @@ impl PySparseVectorDataConfig {
#[pymethods]
impl PySparseVectorDataConfig {
#[new]
#[pyo3(signature = (index, storage_type, modifier = None))]
pub fn new(
index: PySparseIndexConfig,
storage_type: PySparseVectorStorageType,
modifier: Option<PyModifier>,
) -> Self {
#[pyo3(signature = (index, modifier = None))]
pub fn new(index: PySparseIndexConfig, modifier: Option<PyModifier>) -> Self {
Self(SparseVectorDataConfig {
index: SparseIndexConfig::from(index),
storage_type: SparseVectorStorageType::from(storage_type),
storage_type: SparseVectorStorageType::Mmap,
modifier: modifier.map(Modifier::from),
})
}
@@ -99,14 +95,13 @@ pub struct PySparseIndexConfig(SparseIndexConfig);
#[pymethods]
impl PySparseIndexConfig {
#[new]
#[pyo3(signature = (index_type, full_scan_threshold = None, datatype = None))]
#[pyo3(signature = (full_scan_threshold = None, datatype = None))]
pub fn new(
index_type: PySparseIndexType,
full_scan_threshold: Option<usize>,
datatype: Option<PyVectorStorageDatatype>,
) -> Self {
Self(SparseIndexConfig {
index_type: SparseIndexType::from(index_type),
index_type: SparseIndexType::MutableRam,
full_scan_threshold,
datatype: datatype.map(VectorStorageDatatype::from),
})

View File

@@ -35,12 +35,10 @@ impl PyVectorDataConfig {
#[pymethods]
impl PyVectorDataConfig {
#[new]
#[pyo3(signature = (size, distance, storage_type, index, quantization_config=None, multivector_config=None, datatype=None))]
#[pyo3(signature = (size, distance, quantization_config=None, multivector_config=None, datatype=None))]
pub fn new(
size: usize,
distance: PyDistance,
storage_type: PyVectorStorageType,
index: PyIndexes,
quantization_config: Option<PyQuantizationConfig>,
multivector_config: Option<PyMultiVectorConfig>,
datatype: Option<PyVectorStorageDatatype>,
@@ -48,8 +46,8 @@ impl PyVectorDataConfig {
Self(VectorDataConfig {
size,
distance: Distance::from(distance),
storage_type: VectorStorageType::from(storage_type),
index: Indexes::from(index),
storage_type: VectorStorageType::InRamChunkedMmap,
index: Indexes::Plain {},
quantization_config: quantization_config.map(QuantizationConfig::from),
multivector_config: multivector_config.map(MultiVectorConfig::from),
datatype: datatype.map(VectorStorageDatatype::from),

View File

@@ -48,7 +48,7 @@ mod qdrant_edge {
PyPlainIndexConfig, PyVectorDataConfig, PyVectorStorageDatatype, PyVectorStorageType,
};
#[pymodule_export]
use super::config::{PyPayloadStorageType, PySegmentConfig};
use super::config::{PyEdgeConfig, PyPayloadStorageType};
#[pymodule_export]
use super::count::PyCountRequest;
#[pymodule_export]
@@ -90,7 +90,7 @@ pub struct PyEdgeShard(Option<edge::EdgeShard>);
impl PyEdgeShard {
#[new]
#[pyo3(signature = (path, config = None))]
pub fn load(path: PathBuf, config: Option<PySegmentConfig>) -> Result<Self> {
pub fn load(path: PathBuf, config: Option<PyEdgeConfig>) -> Result<Self> {
let shard = edge::EdgeShard::load(&path, config.map(SegmentConfig::from))?;
Ok(Self(Some(shard)))
}