ainl_memory/snapshot.rs
1//! Serializable graph snapshots and validation reports.
2//!
3//! - **[`AgentGraphSnapshot`]**: nodes + edges for one `agent_id`, `exported_at`, and `schema_version`.
4//! - **[`SnapshotEdge`]**: maps to `ainl_graph_edges` (`source_id`↔`from_id`, `target_id`↔`to_id`, `edge_type`↔`label`).
5//! - **[`GraphValidationReport`]**: counts, dangling pairs, optional [`DanglingEdgeDetail`] (includes edge label),
6//! `cross_agent_boundary_edges`, orphan node ids, `is_valid`.
7
8use crate::node::AinlMemoryNode;
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11use std::borrow::Cow;
12use uuid::Uuid;
13
14/// Schema version string embedded in [`AgentGraphSnapshot`] for forward compatibility.
15pub const SNAPSHOT_SCHEMA_VERSION: &str = "1.0";
16
17fn default_snapshot_schema_cow() -> Cow<'static, str> {
18 Cow::Borrowed(SNAPSHOT_SCHEMA_VERSION)
19}
20
21/// Full export of one agent's subgraph (nodes + interconnecting edges).
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct AgentGraphSnapshot {
24 pub agent_id: String,
25 pub exported_at: DateTime<Utc>,
26 #[serde(default = "default_snapshot_schema_cow")]
27 pub schema_version: Cow<'static, str>,
28 pub nodes: Vec<AinlMemoryNode>,
29 pub edges: Vec<SnapshotEdge>,
30}
31
32/// One directed edge in a snapshot (maps to `ainl_graph_edges` rows).
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SnapshotEdge {
35 pub source_id: Uuid,
36 pub target_id: Uuid,
37 pub edge_type: String,
38 #[serde(default = "default_edge_weight")]
39 pub weight: f32,
40 #[serde(default)]
41 pub metadata: Option<serde_json::Value>,
42}
43
44fn default_edge_weight() -> f32 {
45 1.0
46}
47
48/// One edge row that references a missing `ainl_graph_nodes` endpoint (see [`GraphValidationReport`]).
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct DanglingEdgeDetail {
51 pub source_id: String,
52 pub target_id: String,
53 pub edge_type: String,
54}
55
56/// Result of [`crate::SqliteGraphStore::validate_graph`].
57#[derive(Debug, Clone)]
58pub struct GraphValidationReport {
59 pub agent_id: String,
60 pub node_count: usize,
61 pub edge_count: usize,
62 /// `(source_id, target_id)` edge endpoint pairs that reference a missing node row.
63 pub dangling_edges: Vec<(String, String)>,
64 /// Same dangling rows as [`Self::dangling_edges`], including edge label for diagnostics.
65 pub dangling_edge_details: Vec<DanglingEdgeDetail>,
66 /// Edges that touch this agent’s node set on exactly one side while both node rows exist
67 /// (often a shared/global neighbor or another agent’s node — informational, not invalid).
68 pub cross_agent_boundary_edges: usize,
69 /// Node ids (for this agent) that do not appear in any edge as source or target.
70 pub orphan_nodes: Vec<String>,
71 pub is_valid: bool,
72}