hyphae_retrieval/model.rs
1// SPDX-License-Identifier: Apache-2.0
2
3use std::time::Duration;
4
5/// One candidate vector with a globally unique binary key.
6#[derive(Clone, Debug, PartialEq)]
7pub struct VectorRecord {
8 /// Globally unique nonempty key.
9 pub key: Vec<u8>,
10 /// Finite nonzero semantic vector.
11 pub vector: Vec<f64>,
12}
13
14impl VectorRecord {
15 /// Creates a candidate. Vector invariants are validated globally during
16 /// search so malformed shards cannot yield partial results.
17 pub fn new(key: impl Into<Vec<u8>>, vector: impl Into<Vec<f64>>) -> Self {
18 Self {
19 key: key.into(),
20 vector: vector.into(),
21 }
22 }
23}
24
25/// Exact cosine search and abstention policy.
26#[derive(Clone, Debug, PartialEq)]
27pub struct RetrievalRequest {
28 /// Finite nonzero query vector.
29 pub query: Vec<f64>,
30 /// Maximum returned matches; must be nonzero.
31 pub limit: usize,
32 /// Inclusive minimum cosine score in `[-1, 1]`.
33 pub minimum_score: f64,
34 /// Minimum difference between the best and runner-up score in `[0, 2]`.
35 pub minimum_margin: f64,
36}
37
38/// Runtime and shape limits for exact global retrieval.
39#[derive(Clone, Debug, PartialEq)]
40pub struct RetrievalLimits {
41 /// Maximum candidates inspected across every shard.
42 pub max_candidates: u64,
43 /// Maximum vector dimension.
44 pub max_dimensions: usize,
45 /// Maximum requested result count.
46 pub max_returned: usize,
47 /// Cooperative monotonic timeout.
48 pub timeout: Duration,
49}
50
51impl Default for RetrievalLimits {
52 fn default() -> Self {
53 Self {
54 max_candidates: 100_000,
55 max_dimensions: 4_096,
56 max_returned: 1_000,
57 timeout: Duration::from_secs(30),
58 }
59 }
60}
61
62/// One globally ranked exact match.
63#[derive(Clone, Debug, PartialEq)]
64pub struct RetrievalMatch {
65 /// Candidate key.
66 pub key: Vec<u8>,
67 /// Exact cosine similarity in `[-1, 1]`.
68 pub score: f64,
69}
70
71/// Stable reason why semantic retrieval declined to return matches.
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73pub enum AbstentionReason {
74 /// No candidates were available.
75 NoCandidates,
76 /// The best score was below the configured threshold.
77 BelowThreshold,
78 /// The best and runner-up scores were too close.
79 Ambiguous,
80}
81
82/// Evidence for a normal abstention outcome.
83#[derive(Clone, Debug, PartialEq)]
84pub struct Abstention {
85 /// Stable machine-readable reason.
86 pub reason: AbstentionReason,
87 /// Best observed score when candidates existed.
88 pub best_score: Option<f64>,
89 /// Runner-up score when at least two candidates existed.
90 pub runner_up_score: Option<f64>,
91 /// Global candidates inspected.
92 pub scanned_candidates: u64,
93}
94
95/// Complete exact retrieval outcome.
96#[derive(Clone, Debug, PartialEq)]
97pub enum RetrievalOutcome {
98 /// Policy accepted the globally ranked matches.
99 Matches {
100 /// Matches after global sort and final limit.
101 matches: Vec<RetrievalMatch>,
102 /// Global candidates inspected.
103 scanned_candidates: u64,
104 },
105 /// Policy explicitly declined to return semantic matches.
106 Abstained(Abstention),
107}