use std::sync::{Arc, LazyLock};
use crate::cache::OnceCache;
use crate::node::{GkNode, NodeMeta, Port, PortType, Slot, Value};
use vectordata::TestDataGroup;
use vectordata::TestDataView;
use vectordata::io::{VectorReader, VvecReader};
use vectordata::catalog::sources::CatalogSources;
use vectordata::catalog::resolver::Catalog;
static DATASET_CACHE: LazyLock<OnceCache<String, Arc<TestDataGroup>>> =
LazyLock::new(OnceCache::new);
static FACET_CACHE: LazyLock<OnceCache<(String, String, String), Arc<dyn std::any::Any + Send + Sync>>> =
LazyLock::new(OnceCache::new);
static PREBUFFER_CACHE: LazyLock<OnceCache<String, Arc<DatasetHandle>>> =
LazyLock::new(OnceCache::new);
fn parse_source_specifier(source: &str) -> (&str, &str) {
if source.starts_with("http://") || source.starts_with("https://") {
return (source, "default");
}
if let Some(pos) = source.find(':') {
(&source[..pos], &source[pos + 1..])
} else {
(source, "default")
}
}
fn run_blocking_io<R>(body: impl FnOnce() -> R) -> R {
if tokio::runtime::Handle::try_current().is_ok() {
tokio::task::block_in_place(body)
} else {
body()
}
}
fn load_dataset_group(source: &str) -> Result<Arc<TestDataGroup>, String> {
let (dataset_name, _profile) = parse_source_specifier(source);
DATASET_CACHE.get_or_init(dataset_name.to_string(), || {
run_blocking_io(|| {
let catalog = Catalog::of(&CatalogSources::new().configure_default());
catalog.open(dataset_name)
.map(Arc::new)
.map_err(|e| format!("failed to load dataset '{dataset_name}': {e}"))
})
})
}
pub(crate) struct UniformDataset<T: Send + Sync + 'static> {
reader: Arc<dyn VectorReader<T>>,
count: usize,
dim: usize,
}
fn load_uniform_facet<T: Send + Sync + 'static>(
source: &str,
profile: &str,
facet: &str,
open_fn: impl FnOnce(&dyn TestDataView) -> std::result::Result<Arc<dyn VectorReader<T>>, vectordata::Error>,
) -> Result<Arc<UniformDataset<T>>, String> {
let key = (source.to_string(), profile.to_string(), facet.to_string());
let any = FACET_CACHE.get_or_init(key, || {
let group = load_dataset_group(source)?;
let view = group.profile(profile)
.ok_or_else(|| format!("profile '{profile}' not found in '{source}'"))?;
crate::audit::record_opened(source, profile, facet, "uniform");
let reader = run_blocking_io(|| open_fn(view.as_ref()))
.map_err(|e| format!("failed to access {facet} from '{source}': {e}"))?;
let count = reader.count();
let dim = reader.dim();
let arc: Arc<UniformDataset<T>> = Arc::new(UniformDataset { reader, count, dim });
Ok(arc as Arc<dyn std::any::Any + Send + Sync>)
})?;
any.downcast::<UniformDataset<T>>()
.map_err(|_| format!(
"facet cache type mismatch for '{source}:{profile}/{facet}' — \
this should be impossible; please file a bug."))
}
type F32Dataset = UniformDataset<f32>;
type I32Dataset = UniformDataset<i32>;
impl F32Dataset {
fn load(source: &str, profile: &str, facet: &str) -> Result<Arc<Self>, String> {
let facet_name = facet.to_string();
load_uniform_facet(source, profile, facet, move |view| {
match facet_name.as_str() {
"base" => view.base_vectors(),
"query" => view.query_vectors(),
"neighbor_distances" => view.neighbor_distances(),
"filtered_neighbor_distances" => view.filtered_neighbor_distances(),
other => Err(vectordata::Error::MissingFacet(format!("unknown f32 facet: '{other}'"))),
}
})
}
}
impl I32Dataset {
fn load(source: &str, profile: &str, facet: &str) -> Result<Arc<Self>, String> {
let facet_name = facet.to_string();
load_uniform_facet(source, profile, facet, move |view| {
match facet_name.as_str() {
"neighbor_indices" => view.neighbor_indices(),
"filtered_neighbor_indices" => view.filtered_neighbor_indices(),
other => Err(vectordata::Error::MissingFacet(format!("unknown i32 facet: '{other}'"))),
}
})
}
}
#[derive(Clone)]
pub(crate) enum DatasetHandle {
F32(Arc<F32Dataset>),
I32(Arc<I32Dataset>),
Ivvec32(Arc<Ivvec32Dataset>),
Generic(Arc<GenericFacetDataset>),
Group(Arc<TestDataGroup>),
Prebuffered { _group: Arc<TestDataGroup>, source: String },
}
impl DatasetHandle {
fn open(source: &str, facet: &str) -> Result<Self, String> {
let (_, profile) = parse_source_specifier(source);
match facet {
"base" | "query" | "neighbor_distances" | "filtered_neighbor_distances" => {
F32Dataset::load(source, profile, facet).map(DatasetHandle::F32)
}
"neighbor_indices" | "filtered_neighbor_indices" => {
I32Dataset::load(source, profile, facet).map(DatasetHandle::I32)
}
"metadata_indices" => {
Ivvec32Dataset::load(source, profile).map(DatasetHandle::Ivvec32)
}
_ => GenericFacetDataset::load(source, profile, facet).map(DatasetHandle::Generic),
}
}
fn open_group(source: &str) -> Result<Self, String> {
load_dataset_group(source).map(DatasetHandle::Group)
}
}
fn handle_of(v: &Value) -> &DatasetHandle {
if matches!(v, Value::None) {
panic!(
"dataset handle is None — an upstream dataset_open / \
dataset_group_open failed to resolve. Check the audit \
log for the underlying open error (catalog miss, missing \
facet on disk, or transport failure). The dataset's name \
is in the audit message; this is the most common fault \
when running a workload on a system whose vectordata \
catalog isn't configured for the requested dataset."
);
}
v.as_handle::<DatasetHandle>()
}
pub struct DatasetOpen {
meta: NodeMeta,
}
impl DatasetOpen {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "dataset_open".into(),
outs: vec![Port::handle("output")],
ins: vec![
Slot::Wire(Port::str("source")),
Slot::Wire(Port::str("facet")),
],
},
}
}
}
impl Default for DatasetOpen {
fn default() -> Self { Self::new() }
}
impl GkNode for DatasetOpen {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let source = inputs[0].as_str();
let facet = inputs[1].as_str();
outputs[0] = match DatasetHandle::open(source, facet) {
Ok(h) => Value::handle(Arc::new(h)),
Err(e) => {
let msg = format!(
"dataset_open: failed to resolve '{source}' facet='{facet}': {e}"
);
crate::audit::error(&msg);
panic!("{msg}");
}
};
}
}
pub struct DatasetGroupOpen {
meta: NodeMeta,
}
impl DatasetGroupOpen {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "dataset_group_open".into(),
outs: vec![Port::handle("output")],
ins: vec![Slot::Wire(Port::str("source"))],
},
}
}
}
impl Default for DatasetGroupOpen {
fn default() -> Self { Self::new() }
}
impl GkNode for DatasetGroupOpen {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let source = inputs[0].as_str();
outputs[0] = match DatasetHandle::open_group(source) {
Ok(h) => Value::handle(Arc::new(h)),
Err(e) => {
let msg = format!(
"dataset_group_open: failed to resolve '{source}': {e}"
);
crate::audit::error(&msg);
panic!("{msg}");
}
};
}
}
macro_rules! handle_indexed_node {
(
$(#[$meta:meta])*
$name:ident, $func_name:literal, $out_port:ident,
facet = $facet:literal,
eval = $eval_fn:expr
) => {
$(#[$meta])*
pub struct $name {
meta: NodeMeta,
}
impl $name {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: $func_name.into(),
outs: vec![Port::new("output", PortType::$out_port)],
ins: vec![
Slot::Wire(Port::handle("handle")),
Slot::Wire(Port::u64("index")),
],
},
}
}
}
impl Default for $name {
fn default() -> Self { Self::new() }
}
impl GkNode for $name {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let handle = handle_of(&inputs[0]);
let resolved = handle.resolve_facet($facet);
let index = inputs[1].as_u64() as usize;
outputs[0] = ($eval_fn)(resolved.as_ref(), index);
}
}
};
}
impl DatasetHandle {
fn resolve_facet<'a>(&'a self, facet: &str) -> std::borrow::Cow<'a, DatasetHandle> {
match self {
DatasetHandle::Prebuffered { source, .. } => {
match DatasetHandle::open(source, facet) {
Ok(opened) => std::borrow::Cow::Owned(opened),
Err(e) => panic!(
"DatasetHandle::resolve_facet: failed to open \
'{facet}' from prebuffered '{source}': {e}"
),
}
}
_ => std::borrow::Cow::Borrowed(self),
}
}
}
fn f32_vec_at(h: &DatasetHandle, index: usize) -> Value {
match h {
DatasetHandle::F32(d) => Value::VecF32(slice_arc_from_uniform(d, index)),
other => panic!(
"expected F32 dataset handle, got {}",
dataset_handle_kind(other)
),
}
}
fn i32_vec_at(h: &DatasetHandle, index: usize) -> Value {
match h {
DatasetHandle::I32(d) => Value::VecI32(slice_arc_from_uniform(d, index)),
other => panic!(
"expected I32 dataset handle, got {}",
dataset_handle_kind(other)
),
}
}
fn ivvec32_vec_at(h: &DatasetHandle, index: usize) -> Value {
match h {
DatasetHandle::Ivvec32(d) => {
if d.count == 0 {
return Value::VecI32(crate::node::SliceArc::from_vec(Vec::<i32>::new()));
}
let v = d.reader.get(index % d.count).unwrap_or_default();
Value::VecI32(crate::node::SliceArc::from_vec(v))
}
other => panic!(
"expected Ivvec32 dataset handle, got {}",
dataset_handle_kind(other)
),
}
}
fn slice_arc_from_uniform<T>(
d: &Arc<UniformDataset<T>>,
index: usize,
) -> crate::node::SliceArc<T>
where
T: Send + Sync + Copy + 'static,
{
if d.count == 0 {
return crate::node::SliceArc::from_vec(Vec::<T>::new());
}
let idx = index % d.count;
if let Some(slice) = d.reader.get_slice(idx) {
let ptr_len = (slice.as_ptr(), slice.len());
let owner = d.clone();
let owner_dyn: Arc<dyn std::any::Any + Send + Sync> = owner;
return unsafe {
crate::node::SliceArc::from_borrowed(
owner_dyn,
std::slice::from_raw_parts(ptr_len.0, ptr_len.1),
)
};
}
crate::node::SliceArc::from_vec(d.reader.get(idx).unwrap_or_default())
}
fn dataset_handle_kind(h: &DatasetHandle) -> &'static str {
match h {
DatasetHandle::F32(_) => "F32",
DatasetHandle::I32(_) => "I32",
DatasetHandle::Ivvec32(_) => "Ivvec32",
DatasetHandle::Generic(_) => "Generic",
DatasetHandle::Group(_) => "Group",
DatasetHandle::Prebuffered { .. } => "Prebuffered",
}
}
fn group_of(handle: &DatasetHandle) -> &TestDataGroup {
match handle {
DatasetHandle::Group(g) => g.as_ref(),
other => panic!(
"expected Group handle, got {}",
dataset_handle_kind(other)
),
}
}
handle_indexed_node!(
VectorAt, "vector_at", VecF32, facet = "base", eval = f32_vec_at
);
handle_indexed_node!(
QueryVectorAt, "query_vector_at", VecF32, facet = "query", eval = f32_vec_at
);
handle_indexed_node!(
NeighborIndicesAt, "neighbor_indices_at", VecI32, facet = "neighbor_indices", eval = i32_vec_at
);
handle_indexed_node!(
NeighborDistancesAt, "neighbor_distances_at", VecF32, facet = "neighbor_distances", eval = f32_vec_at
);
handle_indexed_node!(
FilteredNeighborIndicesAt, "filtered_neighbor_indices_at", VecI32, facet = "filtered_neighbor_indices", eval = i32_vec_at
);
handle_indexed_node!(
FilteredNeighborDistancesAt, "filtered_neighbor_distances_at", VecF32, facet = "filtered_neighbor_distances", eval = f32_vec_at
);
macro_rules! handle_metadata_node {
(
$(#[$meta:meta])*
$name:ident, $func_name:literal, $out_port:ident,
eval = $eval_fn:expr
) => {
$(#[$meta])*
pub struct $name {
meta: NodeMeta,
}
impl $name {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: $func_name.into(),
outs: vec![Port::new("output", PortType::$out_port)],
ins: vec![Slot::Wire(Port::handle("handle"))],
},
}
}
}
impl Default for $name {
fn default() -> Self { Self::new() }
}
impl GkNode for $name {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let handle = handle_of(&inputs[0]);
outputs[0] = ($eval_fn)(handle);
}
}
};
}
handle_metadata_node!(
VectorDim, "vector_dim", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::F32(d) => Value::U64(d.dim as u64),
DatasetHandle::I32(d) => Value::U64(d.dim as u64),
_ => Value::U64(0),
}
);
macro_rules! source_only_node {
(
$(#[$meta:meta])*
$name:ident, $func_name:literal,
out_port = $out:ident,
eval = |$src:ident| $body:expr
) => {
$(#[$meta])*
pub struct $name {
meta: NodeMeta,
}
impl $name {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: $func_name.into(),
outs: vec![Port::new("output", PortType::$out)],
ins: vec![Slot::Wire(Port::str("source"))],
},
}
}
}
impl Default for $name {
fn default() -> Self { Self::new() }
}
impl GkNode for $name {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let $src = inputs[0].as_str();
outputs[0] = $body;
}
}
};
}
handle_metadata_node!(
VectorCount, "vector_count", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::F32(d) => Value::U64(d.count as u64),
DatasetHandle::I32(d) => Value::U64(d.count as u64),
DatasetHandle::Ivvec32(d) => Value::U64(d.count as u64),
DatasetHandle::Generic(d) => Value::U64(d.count as u64),
DatasetHandle::Group(_) => panic!("vector_count: expected facet handle, got Group"),
DatasetHandle::Prebuffered { source, .. } => {
match DatasetHandle::open(source, "base") {
Ok(DatasetHandle::F32(d)) => Value::U64(d.count as u64),
Ok(other) => panic!(
"vector_count: expected F32 base facet, got {}",
dataset_handle_kind(&other)),
Err(e) => panic!(
"vector_count: failed to open 'base' from prebuffered '{source}': {e}"),
}
}
}
);
handle_metadata_node!(
QueryCount, "query_count", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::F32(d) => Value::U64(d.count as u64),
DatasetHandle::I32(d) => Value::U64(d.count as u64),
DatasetHandle::Ivvec32(d) => Value::U64(d.count as u64),
DatasetHandle::Generic(d) => Value::U64(d.count as u64),
DatasetHandle::Group(_) => panic!("query_count: expected facet handle, got Group"),
DatasetHandle::Prebuffered { source, .. } => {
match DatasetHandle::open(source, "query") {
Ok(DatasetHandle::F32(d)) => Value::U64(d.count as u64),
Ok(other) => panic!(
"query_count: expected F32 query facet, got {}",
dataset_handle_kind(&other)),
Err(e) => panic!(
"query_count: failed to open 'query' from prebuffered '{source}': {e}"),
}
}
}
);
handle_metadata_node!(
NeighborCount, "neighbor_count", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::I32(d) => Value::U64(d.dim as u64),
_ => Value::U64(0),
}
);
handle_metadata_node!(
DatasetDistanceFunction, "dataset_distance_function", Str,
eval = |h: &DatasetHandle| {
let group = group_of(h);
let raw = group
.attribute("distance_function")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let df = match raw.to_uppercase().as_str() {
"L2" | "EUCLIDEAN" => "EUCLIDEAN",
"L1" | "MANHATTAN" => "MANHATTAN",
"COSINE" => "COSINE",
"DOT_PRODUCT" | "DOTPRODUCT" | "DOT" | "INNER_PRODUCT" | "IP" => "DOT_PRODUCT",
_ => raw,
};
Value::Str(df.to_string().into())
}
);
pub(crate) struct Ivvec32Dataset {
reader: Arc<dyn VvecReader<i32>>,
count: usize,
}
impl Ivvec32Dataset {
fn load(source: &str, profile: &str) -> Result<Arc<Self>, String> {
let key = (source.to_string(), profile.to_string(), "metadata_indices".to_string());
let any = FACET_CACHE.get_or_init(key, || {
let group = load_dataset_group(source)?;
let view = group.profile(profile)
.ok_or_else(|| format!("profile '{profile}' not found in '{source}'"))?;
crate::audit::record_opened(source, profile, "metadata_indices", "ivvec32");
let reader = run_blocking_io(|| view.metadata_indices())
.map_err(|e| format!("failed to access metadata_indices from '{source}': {e}"))?;
let count = reader.count();
let arc: Arc<Self> = Arc::new(Self { reader, count });
Ok(arc as Arc<dyn std::any::Any + Send + Sync>)
})?;
any.downcast::<Self>()
.map_err(|_| format!(
"facet cache type mismatch for '{source}:{profile}/metadata_indices'"))
}
}
handle_indexed_node!(
MetadataIndicesAt, "metadata_indices_at", VecI32, facet = "metadata_indices", eval = ivvec32_vec_at
);
handle_indexed_node!(
MetadataIndicesLenAt, "metadata_indices_len_at", U64, facet = "metadata_indices",
eval = |h: &DatasetHandle, idx: usize| match h {
DatasetHandle::Ivvec32(d) if d.count > 0 => {
let len = d.reader.dim_at(idx % d.count).unwrap_or(0);
Value::U64(len as u64)
}
_ => Value::U64(0),
}
);
handle_metadata_node!(
MetadataIndicesCount, "metadata_indices_count", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::Ivvec32(d) => Value::U64(d.count as u64),
_ => Value::U64(0),
}
);
handle_metadata_node!(
DatasetFacets, "dataset_facets", Str,
eval = |h: &DatasetHandle| {
let group = group_of(h);
let names = group.profile_names();
if let Some(first) = names.first() {
if let Some(view) = group.profile(first) {
let manifest = view.facet_manifest();
let mut names: Vec<&String> = manifest.keys().collect();
names.sort();
return Value::Str(
names.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", ").into()
);
}
}
Value::Str(String::new().into())
}
);
handle_metadata_node!(
DatasetProfileCount, "dataset_profile_count", U64,
eval = |h: &DatasetHandle| Value::U64(group_of(h).profile_names().len() as u64)
);
handle_metadata_node!(
DatasetProfileNames, "dataset_profile_names", Str,
eval = |h: &DatasetHandle| Value::Str(group_of(h).profile_names().join(", ").into())
);
pub struct MatchingProfiles {
meta: NodeMeta,
}
impl MatchingProfiles {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "matching_profiles".into(),
outs: vec![Port::str("output")],
ins: vec![
Slot::Wire(Port::handle("group")),
Slot::Wire(Port::str("prefix")),
],
},
}
}
}
impl Default for MatchingProfiles {
fn default() -> Self { Self::new() }
}
impl GkNode for MatchingProfiles {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let group = group_of(handle_of(&inputs[0]));
let prefix = inputs[1].as_str();
let all = group.profile_names();
let mut matched: Vec<&str> = if prefix.is_empty() {
all.iter().map(|s| s.as_str()).collect()
} else {
all.iter()
.filter(|s| s.starts_with(prefix))
.map(|s| s.as_str())
.collect()
};
matched.sort_by(|a, b| natural_cmp(a, b));
outputs[0] = Value::Str(matched.join(",").into());
}
}
fn natural_cmp(a: &str, b: &str) -> std::cmp::Ordering {
let mut ai = a.chars().peekable();
let mut bi = b.chars().peekable();
loop {
match (ai.peek().copied(), bi.peek().copied()) {
(None, None) => return std::cmp::Ordering::Equal,
(None, _) => return std::cmp::Ordering::Less,
(_, None) => return std::cmp::Ordering::Greater,
(Some(ac), Some(bc)) => {
if ac.is_ascii_digit() && bc.is_ascii_digit() {
let mut na: u64 = 0;
while let Some(c) = ai.peek().copied()
&& c.is_ascii_digit()
{
na = na.saturating_mul(10).saturating_add((c as u8 - b'0') as u64);
ai.next();
}
let mut nb: u64 = 0;
while let Some(c) = bi.peek().copied()
&& c.is_ascii_digit()
{
nb = nb.saturating_mul(10).saturating_add((c as u8 - b'0') as u64);
bi.next();
}
match na.cmp(&nb) {
std::cmp::Ordering::Equal => continue,
non_eq => return non_eq,
}
} else {
match ac.cmp(&bc) {
std::cmp::Ordering::Equal => { ai.next(); bi.next(); }
non_eq => return non_eq,
}
}
}
}
}
}
pub struct DatasetProfileNameAt {
meta: NodeMeta,
}
impl DatasetProfileNameAt {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "dataset_profile_name_at".into(),
outs: vec![Port::str("output")],
ins: vec![
Slot::Wire(Port::handle("group")),
Slot::Wire(Port::u64("index")),
],
},
}
}
}
impl Default for DatasetProfileNameAt {
fn default() -> Self { Self::new() }
}
impl GkNode for DatasetProfileNameAt {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let group = group_of(handle_of(&inputs[0]));
let idx = inputs[1].as_u64() as usize;
let names = group.profile_names();
outputs[0] = if names.is_empty() {
Value::Str(String::new().into())
} else {
Value::Str(names[idx % names.len()].clone().into())
};
}
}
pub struct ProfileBaseCount {
meta: NodeMeta,
}
impl ProfileBaseCount {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "profile_base_count".into(),
outs: vec![Port::u64("output")],
ins: vec![
Slot::Wire(Port::handle("group")),
Slot::Wire(Port::u64("index")),
],
},
}
}
}
impl Default for ProfileBaseCount {
fn default() -> Self { Self::new() }
}
impl GkNode for ProfileBaseCount {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let group = group_of(handle_of(&inputs[0]));
let idx = inputs[1].as_u64() as usize;
let names = group.profile_names();
let result = if names.is_empty() {
0
} else {
let name = &names[idx % names.len()];
group
.profile(name)
.and_then(|view| view.base_count())
.unwrap_or(0)
};
outputs[0] = Value::U64(result);
}
}
pub struct ProfileFacets {
meta: NodeMeta,
}
impl ProfileFacets {
pub fn new() -> Self {
Self {
meta: NodeMeta {
name: "profile_facets".into(),
outs: vec![Port::str("output")],
ins: vec![
Slot::Wire(Port::handle("group")),
Slot::Wire(Port::u64("index")),
],
},
}
}
}
impl Default for ProfileFacets {
fn default() -> Self { Self::new() }
}
impl GkNode for ProfileFacets {
fn meta(&self) -> &NodeMeta { &self.meta }
fn eval(&self, inputs: &[Value], outputs: &mut [Value]) {
let group = group_of(handle_of(&inputs[0]));
let idx = inputs[1].as_u64() as usize;
let names = group.profile_names();
let facets = if names.is_empty() {
String::new()
} else {
let name = &names[idx % names.len()];
match group.profile(name) {
Some(view) => {
let manifest = view.facet_manifest();
let mut fnames: Vec<&String> = manifest.keys().collect();
fnames.sort();
fnames
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(", ")
}
None => String::new(),
}
};
outputs[0] = Value::Str(facets.into());
}
}
source_only_node!(
DatasetPrebuffer, "dataset_prebuffer",
out_port = Handle,
eval = |source| do_dataset_prebuffer(source)
);
fn do_dataset_prebuffer(source: &str) -> Value {
match PREBUFFER_CACHE.get_or_init(source.to_string(), || {
crate::audit::record_prebuffer_entered(source);
do_dataset_prebuffer_inner(source)
}) {
Ok(handle) => Value::handle(handle),
Err(_) => Value::None,
}
}
fn do_dataset_prebuffer_inner(source: &str) -> Result<Arc<DatasetHandle>, String> {
let group = match load_dataset_group(source) {
Ok(g) => g,
Err(e) => {
let msg = format!("dataset_prebuffer: cannot resolve '{source}': {e}");
crate::audit::error(&msg);
return Err(msg);
}
};
let group_for_handle = group.clone();
let (_, profile) = parse_source_specifier(source);
let view = match group.profile(profile) {
Some(v) => v,
None => {
crate::audit::error(&format!(
"dataset_prebuffer: profile '{profile}' not found in '{source}'"));
return Ok(Arc::new(DatasetHandle::Prebuffered {
_group: group_for_handle,
source: source.to_string(),
}));
}
};
let mut facet_count: u64 = 0;
let mut last_log_at = std::time::Instant::now();
let log_interval = std::time::Duration::from_secs(1);
for (name, _descriptor) in view.facet_manifest() {
if view.facet_element_type(&name).is_err() { continue; }
let storage = match run_blocking_io(|| view.open_facet_storage(&name)) {
Ok(s) => s,
Err(e) => {
crate::audit::warn(&format!(
"dataset_prebuffer: open '{name}' for prebuffer failed: {e}"));
continue;
}
};
let prebuf_source = source.to_string();
let prebuf_profile = profile.to_string();
let prebuf_facet = name.clone();
let mut last_done: u64 = 0;
let prebuf_result = run_blocking_io(|| storage.prebuffer_with_progress(|p| {
let now = std::time::Instant::now();
if now.duration_since(last_log_at) < log_interval {
return;
}
last_log_at = now;
let total_b = p.total_bytes();
let done_b = p.downloaded_bytes();
let total_c = p.total_chunks();
let done_c = p.completed_chunks();
let pct = p.fraction() * 100.0;
let delta_mb = (done_b.saturating_sub(last_done)) as f64 / (1024.0 * 1024.0);
last_done = done_b;
crate::audit::info(&format!(
"prebuffer: progress {prebuf_source}:{prebuf_profile}/{prebuf_facet} \
{pct:5.1}% ({done_c}/{total_c} chunks, \
{done_mb:.1}/{total_mb:.1} MB, +{delta_mb:.1} MB last sec)",
done_mb = done_b as f64 / (1024.0 * 1024.0),
total_mb = total_b as f64 / (1024.0 * 1024.0),
));
}));
if let Err(e) = prebuf_result {
crate::audit::warn(&format!(
"dataset_prebuffer: download error for '{source}' facet '{name}': {e}"));
continue;
}
facet_count = facet_count.saturating_add(1);
crate::audit::record_prebuffered(source, profile, &name);
}
crate::audit::log_prebuffer_summary(source, profile, facet_count);
Ok(Arc::new(DatasetHandle::Prebuffered {
_group: group_for_handle,
source: source.to_string(),
}))
}
pub(crate) struct GenericFacetDataset {
reader: vectordata::typed_access::TypedReader<i64>,
count: usize,
}
impl GenericFacetDataset {
fn load(source: &str, profile: &str, facet: &str) -> Result<Arc<Self>, String> {
let key = (source.to_string(), profile.to_string(), facet.to_string());
let any = FACET_CACHE.get_or_init(key, || {
let group = load_dataset_group(source)?;
let gv = group.generic_view(profile)
.ok_or_else(|| format!("profile '{profile}' not found in '{source}'"))?;
crate::audit::record_opened(source, profile, facet, "generic-typed");
let reader = run_blocking_io(|| gv.open_facet_typed::<i64>(facet))
.map_err(|e| format!("failed to open {facet} from '{source}:{profile}': {e}"))?;
let count = reader.count();
let arc: Arc<Self> = Arc::new(Self { reader, count });
Ok(arc as Arc<dyn std::any::Any + Send + Sync>)
})?;
any.downcast::<Self>()
.map_err(|_| format!(
"facet cache type mismatch for '{source}:{profile}/{facet}'"))
}
fn get_scalar(&self, index: usize) -> i64 {
if self.count == 0 { return 0; }
self.reader.get_value(index % self.count).unwrap_or(0)
}
fn format_scalar(&self, index: usize) -> String {
self.get_scalar(index).to_string()
}
}
fn generic_str_at(h: &DatasetHandle, idx: usize) -> Value {
match h {
DatasetHandle::Generic(d) => Value::Str(d.format_scalar(idx).into()),
_ => Value::Str(String::new().into()),
}
}
handle_indexed_node!(
MetadataValueAt, "metadata_value_at", Str, facet = "metadata_content", eval = generic_str_at
);
handle_indexed_node!(
PredicateValueAt, "predicate_value_at", Str, facet = "metadata_predicates", eval = generic_str_at
);
handle_metadata_node!(
MetadataContentCount, "metadata_content_count", U64,
eval = |h: &DatasetHandle| match h {
DatasetHandle::Generic(d) => Value::U64(d.count as u64),
_ => Value::U64(0),
}
);
use crate::dsl::registry::{Arity, DefaultResolver, FuncCategory, FuncSig, ParamSpec};
use crate::node::SlotType;
macro_rules! sig_handle_indexed {
($name:literal, $resolver:expr, $desc:literal, $help:literal) => {
FuncSig {
name: $name, category: FuncCategory::RealData, outputs: 1,
description: $desc, help: $help,
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "handle", slot_type: SlotType::Wire, required: true, example: "base", constraint: None },
ParamSpec { name: "index", slot_type: SlotType::Wire, required: true, example: "cycle", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some($resolver),
output_type: crate::dsl::registry::OutputType::Fixed,
}
};
}
macro_rules! sig_handle_metadata {
($name:literal, $resolver:expr, $desc:literal, $help:literal) => {
FuncSig {
name: $name, category: FuncCategory::RealData, outputs: 1,
description: $desc, help: $help,
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "handle", slot_type: SlotType::Wire, required: true, example: "base", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some($resolver),
output_type: crate::dsl::registry::OutputType::Fixed,
}
};
}
pub fn signatures() -> &'static [FuncSig] {
use FuncCategory as C;
&[
FuncSig {
name: "dataset_open", category: C::RealData, outputs: 1,
description: "open a dataset facet, returning a handle",
help: "Resolve a (source, facet) pair to a typed handle. The handle\nflows on a wire to per-cycle accessors, which downcast and read\nat the requested index — no per-cycle string lookup.\nProvenance follows source/facet inputs; with scope-extern\ninputs this evaluates once per iteration via the engine's\nstandard provenance caching.\nExample: const base := dataset_open(\"glove-25\", \"base\")",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "source", slot_type: SlotType::Wire, required: true, example: "\"glove-25\"", constraint: None },
ParamSpec { name: "facet", slot_type: SlotType::Wire, required: true, example: "\"base\"", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: None,
output_type: crate::dsl::registry::OutputType::Fixed,
},
FuncSig {
name: "dataset_group_open", category: C::RealData, outputs: 1,
description: "open a dataset group, returning a handle",
help: "Resolve a dataset to a group-level handle. Used by group\nmetadata accessors (dataset_profile_count, dataset_facets, ...)\nthat operate on TestDataGroup before any profile/facet is selected.\nExample: const group := dataset_group_open(\"glove-25\")",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "source", slot_type: SlotType::Wire, required: true, example: "\"glove-25\"", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: None,
output_type: crate::dsl::registry::OutputType::Fixed,
},
sig_handle_indexed!("vector_at", DefaultResolver::Facet("base"),
"access f32 vector by index",
"Read an f32 vector from a facet handle as a typed VecF32.\nAuto-promotes a string source via dataset_open(_,\"base\").\nExample: vector_at(base, cycle)"),
sig_handle_indexed!("query_vector_at", DefaultResolver::Facet("query"),
"access query vector by index",
"Alias for vector_at over a query-facet handle.\nAuto-promotes a string source via dataset_open(_,\"query\")."),
sig_handle_indexed!("neighbor_indices_at", DefaultResolver::Facet("neighbor_indices"),
"ground-truth neighbor indices for a query",
"Read ground-truth k-nearest neighbor indices for a query as a\ntyped VecI32. Auto-promotes a string source via\ndataset_open(_,\"neighbor_indices\")."),
sig_handle_indexed!("neighbor_distances_at", DefaultResolver::Facet("neighbor_distances"),
"ground-truth neighbor distances for a query",
"Read ground-truth distances for a query's k-nearest neighbors\nas a typed VecF32."),
sig_handle_indexed!("filtered_neighbor_indices_at", DefaultResolver::Facet("filtered_neighbor_indices"),
"filtered ground-truth neighbor indices",
"Read filtered ground-truth indices for a query as a typed VecI32.\nUsed for filtered-ANN recall verification."),
sig_handle_indexed!("filtered_neighbor_distances_at", DefaultResolver::Facet("filtered_neighbor_distances"),
"filtered ground-truth neighbor distances",
"Read filtered ground-truth distances for a query as a typed VecF32."),
sig_handle_indexed!("metadata_indices_len_at", DefaultResolver::Facet("metadata_indices"),
"length of metadata indices for a query",
"Return the per-record matching-base count for a query without\nloading the full index list (reads only the 4-byte header)."),
sig_handle_indexed!("metadata_indices_at", DefaultResolver::Facet("metadata_indices"),
"matching base ordinals for a query predicate",
"Variable-length list of base vector ordinals matching a query's\npredicate."),
sig_handle_indexed!("metadata_value_at", DefaultResolver::Facet("metadata_content"),
"scalar metadata value per base vector",
"Read a metadata value for a base vector by ordinal.\nReads from the metadata_content facet."),
sig_handle_indexed!("predicate_value_at", DefaultResolver::Facet("metadata_predicates"),
"scalar predicate value per query",
"Read a predicate value for a query by ordinal.\nReads from the metadata_predicates facet."),
sig_handle_metadata!("vector_dim", DefaultResolver::Facet("base"),
"vector dimensionality of a facet handle",
"Return the per-record element count (dimension) of a vector facet."),
sig_handle_metadata!("vector_count", DefaultResolver::Facet("base"),
"record count of a facet handle",
"Return the number of records in a facet handle (base vectors,\nquery vectors, ...). Auto-promotes a string source via\ndataset_open(_,\"base\")."),
sig_handle_metadata!("query_count", DefaultResolver::Facet("query"),
"record count of a query-facet handle",
"Same as vector_count but defaults to dataset_open(_,\"query\")\nfor string sources."),
sig_handle_metadata!("neighbor_count", DefaultResolver::Facet("neighbor_indices"),
"ground-truth neighbors per query (maxk)",
"Return the per-record neighbor count (k) of a neighbor-indices handle."),
sig_handle_metadata!("metadata_indices_count", DefaultResolver::Facet("metadata_indices"),
"number of predicate result sets",
"Return the record count of a metadata-indices handle."),
sig_handle_metadata!("metadata_content_count", DefaultResolver::Facet("metadata_content"),
"number of metadata content records",
"Return the record count of a metadata-content handle."),
sig_handle_metadata!("dataset_distance_function", DefaultResolver::Group,
"dataset distance/similarity function name",
"Return the distance function declared in the dataset metadata\n('COSINE','EUCLIDEAN','DOT_PRODUCT','MANHATTAN'). Group-level."),
sig_handle_metadata!("dataset_facets", DefaultResolver::Group,
"list available facets in default profile",
"Comma-separated facet names available in the group's default profile."),
sig_handle_metadata!("dataset_profile_count", DefaultResolver::Group,
"total number of profiles in a dataset",
"Number of profiles defined in the dataset group."),
sig_handle_metadata!("dataset_profile_names", DefaultResolver::Group,
"comma-separated list of profile names",
"All profile names in canonical sort order (by base_count)."),
FuncSig {
name: "matching_profiles", category: C::RealData, outputs: 1,
description: "profile names matching a prefix",
help: "Profile names from a group handle that start with the given prefix.\nIf prefix is empty, returns all profile names. Used by for_each:\nphase templates to discover profiles dynamically.",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "group", slot_type: SlotType::Wire, required: true, example: "group", constraint: None },
ParamSpec { name: "prefix", slot_type: SlotType::Wire, required: false, example: "\"label_\"", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some(DefaultResolver::Group),
output_type: crate::dsl::registry::OutputType::Fixed,
},
FuncSig {
name: "dataset_profile_name_at", category: C::RealData, outputs: 1,
description: "profile name by index from sorted list",
help: "Profile name at a given index from the canonical sorted list.\nIndex wraps modulo profile count.",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "group", slot_type: SlotType::Wire, required: true, example: "group", constraint: None },
ParamSpec { name: "index", slot_type: SlotType::Wire, required: true, example: "cycle", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some(DefaultResolver::Group),
output_type: crate::dsl::registry::OutputType::Fixed,
},
FuncSig {
name: "profile_base_count", category: C::RealData, outputs: 1,
description: "base vector count for profile at index",
help: "Base vector count of the profile at a given index in the dataset's\ncanonical sorted profile list.",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "group", slot_type: SlotType::Wire, required: true, example: "group", constraint: None },
ParamSpec { name: "index", slot_type: SlotType::Wire, required: true, example: "cycle", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some(DefaultResolver::Group),
output_type: crate::dsl::registry::OutputType::Fixed,
},
FuncSig {
name: "profile_facets", category: C::RealData, outputs: 1,
description: "available facets for profile at index",
help: "Comma-separated facet names for the profile at a given index in\nthe dataset's canonical sorted profile list.",
identity: None, variadic_ctor: None,
params: &[
ParamSpec { name: "group", slot_type: SlotType::Wire, required: true, example: "group", constraint: None },
ParamSpec { name: "index", slot_type: SlotType::Wire, required: true, example: "cycle", constraint: None },
],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: Some(DefaultResolver::Group),
output_type: crate::dsl::registry::OutputType::Fixed,
},
FuncSig {
name: "dataset_prebuffer", category: C::RealData, outputs: 1,
description: "eagerly download dataset facets to local cache",
help: "Downloads all facets for a dataset to the local cache. Returns 0\n(side-effect resolver). Subsequent loads use fast local mmap access.\nKept on a string source — typically called once per workload at\ninit time.\nExample: const _pb := dataset_prebuffer(\"example\")",
identity: None, variadic_ctor: None,
params: &[ParamSpec { name: "source", slot_type: SlotType::Wire, required: true, example: "\"test\"", constraint: None }],
arity: Arity::Fixed,
commutativity: crate::node::Commutativity::Positional,
default_resolver: None,
output_type: crate::dsl::registry::OutputType::Fixed,
},
]
}
#[cfg(feature = "vectordata")]
pub(crate) fn build_node(name: &str, _wires: &[crate::assembly::WireRef], _wire_types: &[crate::node::PortType], _consts: &[crate::dsl::factory::ConstArg]) -> Option<Result<Box<dyn crate::node::GkNode>, String>> {
match name {
"dataset_open" => Some(Ok(Box::new(DatasetOpen::new()) as Box<dyn crate::node::GkNode>)),
"dataset_group_open" => Some(Ok(Box::new(DatasetGroupOpen::new()) as Box<dyn crate::node::GkNode>)),
"vector_at" => Some(Ok(Box::new(VectorAt::new()) as Box<dyn crate::node::GkNode>)),
"query_vector_at" => Some(Ok(Box::new(QueryVectorAt::new()) as Box<dyn crate::node::GkNode>)),
"neighbor_indices_at" => Some(Ok(Box::new(NeighborIndicesAt::new()) as Box<dyn crate::node::GkNode>)),
"neighbor_distances_at" => Some(Ok(Box::new(NeighborDistancesAt::new()) as Box<dyn crate::node::GkNode>)),
"filtered_neighbor_indices_at" => Some(Ok(Box::new(FilteredNeighborIndicesAt::new()) as Box<dyn crate::node::GkNode>)),
"filtered_neighbor_distances_at" => Some(Ok(Box::new(FilteredNeighborDistancesAt::new()) as Box<dyn crate::node::GkNode>)),
"dataset_distance_function" => Some(Ok(Box::new(DatasetDistanceFunction::new()) as Box<dyn crate::node::GkNode>)),
"vector_dim" => Some(Ok(Box::new(VectorDim::new()) as Box<dyn crate::node::GkNode>)),
"vector_count" => Some(Ok(Box::new(VectorCount::new()) as Box<dyn crate::node::GkNode>)),
"query_count" => Some(Ok(Box::new(QueryCount::new()) as Box<dyn crate::node::GkNode>)),
"neighbor_count" => Some(Ok(Box::new(NeighborCount::new()) as Box<dyn crate::node::GkNode>)),
"metadata_indices_len_at" => Some(Ok(Box::new(MetadataIndicesLenAt::new()) as Box<dyn crate::node::GkNode>)),
"metadata_indices_at" => Some(Ok(Box::new(MetadataIndicesAt::new()) as Box<dyn crate::node::GkNode>)),
"metadata_indices_count" => Some(Ok(Box::new(MetadataIndicesCount::new()) as Box<dyn crate::node::GkNode>)),
"dataset_facets" => Some(Ok(Box::new(DatasetFacets::new()) as Box<dyn crate::node::GkNode>)),
"dataset_profile_count" => Some(Ok(Box::new(DatasetProfileCount::new()) as Box<dyn crate::node::GkNode>)),
"dataset_profile_names" => Some(Ok(Box::new(DatasetProfileNames::new()) as Box<dyn crate::node::GkNode>)),
"matching_profiles" => Some(Ok(Box::new(MatchingProfiles::new()) as Box<dyn crate::node::GkNode>)),
"dataset_profile_name_at" => Some(Ok(Box::new(DatasetProfileNameAt::new()) as Box<dyn crate::node::GkNode>)),
"profile_base_count" => Some(Ok(Box::new(ProfileBaseCount::new()) as Box<dyn crate::node::GkNode>)),
"profile_facets" => Some(Ok(Box::new(ProfileFacets::new()) as Box<dyn crate::node::GkNode>)),
"dataset_prebuffer" => Some(Ok(Box::new(DatasetPrebuffer::new()) as Box<dyn crate::node::GkNode>)),
"metadata_value_at" => Some(Ok(Box::new(MetadataValueAt::new()) as Box<dyn crate::node::GkNode>)),
"predicate_value_at" => Some(Ok(Box::new(PredicateValueAt::new()) as Box<dyn crate::node::GkNode>)),
"metadata_content_count" => Some(Ok(Box::new(MetadataContentCount::new()) as Box<dyn crate::node::GkNode>)),
_ => None,
}
}
#[cfg(feature = "vectordata")]
crate::register_nodes!(signatures, build_node);
#[cfg(feature = "vectordata")]
fn vectordata_sugar(
source_name: &str,
constructor: &crate::dsl::ast::Expr,
) -> Result<Option<crate::dsl::cursor_sugar::CursorSugar>, String> {
use crate::dsl::ast::{Arg, CallExpr, Expr};
use crate::dsl::compile::positional_str_lit;
use crate::dsl::cursor_sugar::{AuxBinding, CursorSugar};
let Expr::Call(call) = constructor else { return Ok(None); };
let (dataset, profile, facet) = match call.func.as_str() {
"vectordata_source" => {
let d = positional_str_lit(call.args.first()).ok_or_else(|| format!(
"cursor '{source_name}': vectordata_source(dataset, profile, facet) — first arg must be a string literal"
))?;
let p = positional_str_lit(call.args.get(1)).ok_or_else(|| format!(
"cursor '{source_name}': vectordata_source(dataset, profile, facet) — second arg must be a string literal"
))?;
let f = positional_str_lit(call.args.get(2)).ok_or_else(|| format!(
"cursor '{source_name}': vectordata_source(dataset, profile, facet) — third arg must be a string literal (\"base\" or \"query\")"
))?;
(d, p, f)
}
"vectordata_base" | "vectordata_query" => {
let f = call.func.strip_prefix("vectordata_").unwrap().to_string();
let d = positional_str_lit(call.args.first()).ok_or_else(|| format!(
"cursor '{source_name}': {}(dataset, profile) — first arg must be a string literal",
call.func,
))?;
let p = positional_str_lit(call.args.get(1)).ok_or_else(|| format!(
"cursor '{source_name}': {}(dataset, profile) — second arg must be a string literal",
call.func,
))?;
(d, p, f)
}
_ => return Ok(None),
};
if facet != "base" && facet != "query" {
return Err(format!(
"cursor '{source_name}': vectordata facet must be \"base\" or \"query\", got \"{facet}\""
));
}
let (count_func, vector_func) = match facet.as_str() {
"base" => ("vector_count", "vector_at"),
"query" => ("query_count", "query_vector_at"),
_ => unreachable!(),
};
let combined = format!("{dataset}:{profile}");
let span = call.span;
let lit = |s: String| Expr::StringLit(s, span);
let positional = |e: Expr| Arg::Positional(e);
let effective_constructor = Expr::Call(CallExpr {
func: "range".into(),
args: vec![
positional(Expr::IntLit(0, span)),
positional(Expr::Call(CallExpr {
func: count_func.into(),
args: vec![positional(lit(combined.clone()))],
span,
})),
],
span,
});
let prebuffer_binding = AuxBinding {
name: format!("__{source_name}_prebuffer"),
value: Expr::Call(CallExpr {
func: "dataset_prebuffer".into(),
args: vec![positional(lit(combined.clone()))],
span,
}),
projection: None,
};
let vector_binding = AuxBinding {
name: format!("{source_name}__vector"),
value: Expr::Call(CallExpr {
func: vector_func.into(),
args: vec![
positional(lit(combined)),
positional(Expr::Ident(format!("{source_name}__ordinal"), span)),
],
span,
}),
projection: Some(("vector".into(), crate::node::PortType::VecF32)),
};
Ok(Some(CursorSugar {
effective_constructor,
aux_bindings: vec![prebuffer_binding, vector_binding],
}))
}
#[cfg(feature = "vectordata")]
inventory::submit! {
crate::dsl::cursor_sugar::CursorSugarRegistration {
handler: vectordata_sugar,
name: "vectordata",
}
}