qfa.services.clustering#
HDBSCAN clustering + token-budget chunking for hierarchical analysis.
Deterministic services logic (no port): turns dense embedding vectors
into a set of Chunk objects for the
map step. HDBSCAN needs no preset cluster count, no fixed eps, and
labels outliers as noise (-1).
Two invariants, both unit-tested:
Full coverage — the union of all chunk records equals the input set; no record is dropped (outliers go into uncategorised chunks).
Budget — no returned chunk exceeds
max_total_tokens; an over-budget group is split into budget-sized sub-chunks.
Functions
|
Cluster records by their embedding vectors into budget-sized chunks. |
- qfa.services.clustering.cluster_records(*, records: tuple[FeedbackRecordModel, ...], vectors: tuple[tuple[float, ...], ...], min_cluster_size: int, max_total_tokens: int, chars_per_token: int, metric: str = 'euclidean', target_chunk_tokens: int | None = None) tuple[Chunk, ...][source]#
Cluster records by their embedding vectors into budget-sized chunks.
- Parameters:
records (tuple[FeedbackRecordModel, ...]) – The records to cluster (same order/length as
vectors).vectors (tuple[tuple[float, ...], ...]) – Dense embedding vector per record.
min_cluster_size (int) – HDBSCAN
min_cluster_size.max_total_tokens (int) – Per-chunk token ceiling — the hard limit of what one LLM call can hold. No returned chunk ever exceeds it.
chars_per_token (int) – Char-to-token conversion ratio for the budget estimate.
metric (str) – HDBSCAN distance metric (default
euclidean).target_chunk_tokens (int | None) – Desired chunk granularity, decoupled from the ceiling. HDBSCAN clusters are uneven, so a dominant theme can fit the ceiling whole and become one fat, slow map call. When set, a cluster larger than this is split into roughly equal sub-chunks. The effective split budget is
min(target_chunk_tokens, max_total_tokens), so the ceiling always wins.Nonekeeps the old behaviour (split only at the ceiling).their (Records within every chunk are always ordered chronologically by)
last) (created metadata (undated records)
time (so a chunk reads as a)
time-windows. (series and a split cluster yields contiguous)
- Returns:
Chunks whose records partition the input exactly. Noise points are collected into uncategorised chunk(s) with
label == -1.- Return type:
- Raises:
ValueError – If
recordsandvectorsdiffer in length.