Skip to main content

khive_runtime/atomic_prepare/
embedding_outcome.rs

1use super::{apply_one_post_commit_effect, KhiveRuntime, NamespaceToken};
2use crate::atomic_plan::PostCommitEffect;
3use crate::atomic_runner::CommittedPostCommitEffects;
4use crate::error::RuntimeError;
5use crate::retrieval::EmbeddingTruncationReport;
6
7/// The eligible-model stage that prevented a note's vector refresh.
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub enum ReindexModelStage {
10    Embedding,
11    VectorValidation,
12    VectorStore,
13    VectorPublication,
14}
15
16impl ReindexModelStage {
17    pub const fn as_str(self) -> &'static str {
18        match self {
19            Self::Embedding => "embedding",
20            Self::VectorValidation => "vector_validation",
21            Self::VectorStore => "vector_store",
22            Self::VectorPublication => "vector_publication",
23        }
24    }
25}
26
27/// An eligible-model failure after the note and its lexical index committed.
28#[derive(Clone, Debug, PartialEq, Eq)]
29pub struct ReindexModelFailure {
30    pub model: String,
31    pub stage: ReindexModelStage,
32    pub error: String,
33}
34
35/// Metadata produced by a reindex effect whose fail-closed stages succeeded.
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct PostCommitEmbeddingOutcome {
38    pub effect: PostCommitEffect,
39    pub truncation: EmbeddingTruncationReport,
40    /// Partial vector failures do not undo a committed mutation. Report-aware
41    /// callers can disclose them while legacy callers retain best-effort success.
42    pub failures: Vec<ReindexModelFailure>,
43}
44
45/// Everything one committed unit's post-commit pass produced. A failing effect
46/// never discards the outcomes of the effects that completed, so callers can
47/// still disclose their model failures and truncation advisories.
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct PostCommitEffectsReport {
50    /// Outcomes of the reindex effects that completed, in effect order.
51    pub outcomes: Vec<PostCommitEmbeddingOutcome>,
52    /// One `effect[<index>] <effect>: <error>` entry per effect that failed.
53    pub failures: Vec<String>,
54}
55
56impl PostCommitEffectsReport {
57    /// The aggregate error for the effects that failed, or `None` when every
58    /// effect completed.
59    pub fn failure_error(&self) -> Option<RuntimeError> {
60        if self.failures.is_empty() {
61            return None;
62        }
63        Some(RuntimeError::Internal(format!(
64            "post-commit effects failed after commit: {}",
65            self.failures.join("; ")
66        )))
67    }
68}
69
70/// Failure-preserving form of [`super::apply_post_commit_effects_with_report`].
71/// Runs every effect, collecting the outcomes of those that completed and the
72/// error of each that did not, instead of replacing the outcomes with the
73/// first failure.
74pub async fn apply_post_commit_effects_with_failures(
75    runtime: &KhiveRuntime,
76    token: &NamespaceToken,
77    effects: CommittedPostCommitEffects,
78) -> PostCommitEffectsReport {
79    let mut report = PostCommitEffectsReport {
80        outcomes: Vec::new(),
81        failures: Vec::new(),
82    };
83    for (index, effect) in effects.into_effects().into_iter().enumerate() {
84        let identity = format!("{effect:?}");
85        match apply_one_post_commit_effect(runtime, token, effect).await {
86            Ok(Some(outcome)) => report.outcomes.push(outcome),
87            Ok(None) => {}
88            Err(error) => report
89                .failures
90                .push(format!("effect[{index}] {identity}: {error}")),
91        }
92    }
93    report
94}