Prefer build_session() over Chutoro::run() when the application needs a
long-lived, incrementally updated index instead of a one-shot batch clustering
run.
The public session surface exposes three types. SessionConfig carries the
validated min_cluster_size, HnswParams, and SessionRefreshPolicy.
SessionRefreshPolicy represents either manual refresh or an append-threshold
trigger. ClusteringSession<D> owns the live session state.
Construct a session through ChutoroBuilder:
use std::sync::Arc;
use chutoro_core::{
ChutoroBuilder, DataSource, DataSourceError, MetricDescriptor,
SessionRefreshPolicy,
};
# struct Dummy(Vec<f32>);
# impl DataSource for Dummy {
# fn len(&self) -> usize { self.0.len() }
# fn name(&self) -> &str { "dummy" }
# fn distance(&self, i: usize, j: usize) -> Result<f32, DataSourceError> {
# let a = self.0.get(i).ok_or(DataSourceError::OutOfBounds { index: i })?;
# let b = self.0.get(j).ok_or(DataSourceError::OutOfBounds { index: j })?;
# Ok((a - b).abs())
# }
# fn metric_descriptor(&self) -> MetricDescriptor {
# MetricDescriptor::new("abs")
# }
# }
#
# fn example(source: Arc<Dummy>) -> Result<(), chutoro_core::ChutoroError> {
let mut session = ChutoroBuilder::new()
.with_min_cluster_size(10)
.with_session_refresh_policy(SessionRefreshPolicy::manual())
.build_session(source)?;
assert_eq!(session.point_count(), 0);
assert_eq!(session.snapshot_version(), 0);
session.append(&[0, 1])?;
session.recompute_core_distances()?;
assert_eq!(session.point_count(), 2);
assert!(session.core_distance(0).is_some());
assert_eq!(session.snapshot_version(), 0);
# Ok(())
# }
Sessions are CPU-only, so ExecutionStrategy::GpuPreferred is rejected during
build_session(). Empty and undersized sources are accepted at construction
time because session creation does not seed HNSW or run the batch bootstrap
path.
After append, newly inserted points have dirty core distances. Calling
core_distance(i) before a recompute returns None for those points. Call
recompute_core_distances() after append batches to compute core distances for
new points and for existing points that appeared near those new points in HNSW.
Treat these recomputed values as provisional until each point has at least
min_cluster_size non-self neighbours. Before that neighbourhood saturation
point, the fallback core-distance rule can still increase; monotonic
non-increase only applies after saturation. Call
recompute_core_distances_full() when the session must re-establish parity
with a from-scratch batch core-distance pass; it searches every inserted point
and is more expensive than the incremental path.
Use append(&[...]) to insert source indices that already exist in the backing
DataSource. The session does not copy or extend source storage; the caller
owns that storage contract. Each index is inserted into the live HNSW index
through the edge-harvesting path, and harvested candidate edges are kept
internally for the later refresh workflow.
append is fail-fast with partial progress. If a slice contains [0, 1, bad]
and the first two inserts succeed, those points remain in the session and their
harvested edges remain pending when the error for bad is returned.
Out-of-bounds indices surface as ChutoroError::DataSource; duplicate indices
and HNSW structural failures surface as ChutoroError::CpuHnswFailure.
Limitations
- The v1 incremental design is append-oriented; deletion and arbitrary in-place mutation are not part of the public session surface.
- Stable cluster identity across snapshots is not guaranteed until roadmap item
12.3.1lands. - Refresh is intended to operate as a micro-batched workflow rather than a per-item online relabelling path.
- A refresh can relabel existing points as well as newly appended points.
Refresh and full batch bootstrap are not yet available on the public session
surface. Those workflows remain future roadmap work. The cpu feature must be
enabled to access build_session(), append(&[usize]), SessionRefreshPolicy,
SessionConfig, and ClusteringSession<D>.