Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions crates/rankweave-core/src/semantic_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl std::error::Error for SemanticIndexError {}
pub struct SemanticUnitIndex {
evidence: SemanticIndexSnapshotEvidence,
candidate_ids: Vec<(String, String)>,
candidate_lookup: HashMap<(String, String), usize>,
candidate_lookup: HashMap<String, HashMap<String, usize>>,
normalized_vectors: Vec<f64>,
vector_norms: Vec<f64>,
}
Expand Down Expand Up @@ -186,18 +186,22 @@ impl SemanticUnitIndex {
}

let mut identities = HashSet::new();
let mut candidate_lookup = HashMap::with_capacity(candidate_ids.len());
let mut candidate_lookup: HashMap<String, HashMap<String, usize>> =
HashMap::with_capacity(candidate_ids.len());
let mut normalized_vectors = Vec::with_capacity(candidate_ids.len() * vector_dimension);
let mut vector_norms = Vec::with_capacity(candidate_ids.len());
let vector_byte_count = vector_dimension * size_of::<f64>();
for (index, (item_id, unit_id)) in candidate_ids.iter().enumerate() {
if !identities.insert((item_id.clone(), unit_id.clone())) {
return Err(SemanticIndexError::DuplicateCandidate {
item_id: item_id.clone(),
unit_id: unit_id.clone(),
item_id: (*item_id).to_owned(),
unit_id: (*unit_id).to_owned(),
});
}
candidate_lookup.insert((item_id.clone(), unit_id.clone()), index);
candidate_lookup
.entry(item_id.clone())
.or_default()
.insert(unit_id.clone(), index);
Comment on lines +201 to +204

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Nested lookup preserves identity semantics

Every lookup still requires both item and unit identifiers. Separate pair detection rejects duplicates, while explicit result sorting prevents map order from changing output.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

let start = index * vector_byte_count;
let vector = packed_vectors[start..start + vector_byte_count]
.chunks_exact(8)
Expand Down Expand Up @@ -281,6 +285,19 @@ impl SemanticUnitIndex {
model_identity: &str,
query_vector: &[f64],
authorized_candidate_ids: &[(String, String)],
) -> Result<SemanticIndexRankingReport, SemanticIndexError> {
let authorized_refs = authorized_candidate_ids
.iter()
.map(|(item_id, unit_id)| (item_id.as_str(), unit_id.as_str()))
.collect::<Vec<_>>();
self.rank_authorized_refs(model_identity, query_vector, &authorized_refs)
}

fn rank_authorized_refs(
&self,
model_identity: &str,
query_vector: &[f64],
authorized_candidate_ids: &[(&str, &str)],
) -> Result<SemanticIndexRankingReport, SemanticIndexError> {
let model_digest = digest_bytes(
b"rankweave.semantic-unit-index.model.v1\0",
Expand Down Expand Up @@ -317,17 +334,18 @@ impl SemanticUnitIndex {
for (item_id, unit_id) in authorized_candidate_ids {
if !authorization_seen.insert((item_id, unit_id)) {
return Err(SemanticIndexError::DuplicateAuthorization {
item_id: item_id.clone(),
unit_id: unit_id.clone(),
item_id: (*item_id).to_owned(),
unit_id: (*unit_id).to_owned(),
});
}
let Some(index) = self
.candidate_lookup
.get(&(item_id.clone(), unit_id.clone()))
.get(*item_id)
.and_then(|units| units.get(*unit_id))
else {
return Err(SemanticIndexError::UnknownAuthorizedCandidate {
item_id: item_id.clone(),
unit_id: unit_id.clone(),
item_id: (*item_id).to_owned(),
unit_id: (*unit_id).to_owned(),
});
};
authorized_indices.push(*index);
Expand Down Expand Up @@ -441,7 +459,7 @@ impl SemanticUnitIndex {
if cursor != packed_authorization.len() {
return Err(SemanticIndexError::MalformedPackedAuthorization);
}
self.rank_authorized(model_identity, query_vector, &authorized)
self.rank_authorized_refs(model_identity, query_vector, &authorized)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Both transports retain digest parity

Both entry points converge on rank_authorized_refs, which hashes identical UTF-8 bytes in caller order. Ownership no longer affects validation or output evidence.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}

Expand All @@ -456,7 +474,10 @@ fn read_packed_u64(bytes: &[u8], cursor: &mut usize) -> Result<u64, SemanticInde
))
}

fn read_packed_text(bytes: &[u8], cursor: &mut usize) -> Result<String, SemanticIndexError> {
fn read_packed_text<'a>(
bytes: &'a [u8],
cursor: &mut usize,
) -> Result<&'a str, SemanticIndexError> {
let length = read_packed_u64(bytes, cursor)?;
if length > (bytes.len() - *cursor) as u64 {
return Err(SemanticIndexError::MalformedPackedAuthorization);
Expand All @@ -465,7 +486,7 @@ fn read_packed_text(bytes: &[u8], cursor: &mut usize) -> Result<String, Semantic
let end = *cursor + length;
let value = &bytes[*cursor..end];
*cursor = end;
String::from_utf8(value.to_vec()).map_err(|_| SemanticIndexError::NonUtf8PackedAuthorization)
std::str::from_utf8(value).map_err(|_| SemanticIndexError::NonUtf8PackedAuthorization)
Comment on lines 486 to +489

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Borrowed identity lifetime is bounded

read_packed_text borrows from the immutable request buffer. Ranking finishes before that buffer is released, including lookups, duplicate checks, and digesting.

(Refers to this code)

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}

/// Atomically replace an immutable exact index only after successful validation.
Expand Down