1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Episode {
8 pub id: String,
9 pub content: String,
10 pub source: Option<String>,
11 pub recorded_at: DateTime<Utc>,
12 pub metadata: Option<serde_json::Value>,
13}
14
15pub struct EpisodeBuilder {
17 content: String,
18 source: Option<String>,
19 metadata: serde_json::Map<String, serde_json::Value>,
20 tags: Vec<String>,
21}
22
23impl EpisodeBuilder {
24 pub fn source(mut self, s: &str) -> Self {
25 self.source = Some(s.to_string());
26 self
27 }
28
29 pub fn tag(mut self, t: &str) -> Self {
30 self.tags.push(t.to_string());
31 self
32 }
33
34 pub fn meta(mut self, key: &str, val: impl Into<serde_json::Value>) -> Self {
35 self.metadata.insert(key.to_string(), val.into());
36 self
37 }
38
39 pub fn build(self) -> Episode {
40 let mut metadata = self.metadata;
41 if !self.tags.is_empty() {
42 let tags: Vec<serde_json::Value> = self
43 .tags
44 .into_iter()
45 .map(serde_json::Value::String)
46 .collect();
47 metadata.insert("tags".to_string(), serde_json::Value::Array(tags));
48 }
49
50 let metadata = if metadata.is_empty() {
51 None
52 } else {
53 Some(serde_json::Value::Object(metadata))
54 };
55
56 Episode {
57 id: uuid::Uuid::now_v7().to_string(),
58 content: self.content,
59 source: self.source,
60 recorded_at: Utc::now(),
61 metadata,
62 }
63 }
64}
65
66impl Episode {
67 pub fn builder(content: &str) -> EpisodeBuilder {
68 EpisodeBuilder {
69 content: content.to_string(),
70 source: None,
71 metadata: serde_json::Map::new(),
72 tags: Vec::new(),
73 }
74 }
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct Entity {
80 pub id: String,
81 pub name: String,
82 pub entity_type: String,
83 pub summary: Option<String>,
84 pub created_at: DateTime<Utc>,
85 pub metadata: Option<serde_json::Value>,
86}
87
88impl Entity {
89 pub fn new(name: &str, entity_type: &str) -> Self {
90 Self {
91 id: uuid::Uuid::now_v7().to_string(),
92 name: name.to_string(),
93 entity_type: entity_type.to_string(),
94 summary: None,
95 created_at: Utc::now(),
96 metadata: None,
97 }
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct Edge {
105 pub id: String,
106 pub source_id: String,
107 pub target_id: String,
108 pub relation: String,
109 pub fact: Option<String>,
110 pub valid_from: Option<DateTime<Utc>>,
111 pub valid_until: Option<DateTime<Utc>>,
112 pub recorded_at: DateTime<Utc>,
113 pub confidence: f64,
114 pub episode_id: Option<String>,
115 pub metadata: Option<serde_json::Value>,
116}
117
118impl Edge {
119 pub fn new(source_id: &str, target_id: &str, relation: &str) -> Self {
120 Self {
121 id: uuid::Uuid::now_v7().to_string(),
122 source_id: source_id.to_string(),
123 target_id: target_id.to_string(),
124 relation: relation.to_string(),
125 fact: None,
126 valid_from: None,
127 valid_until: None,
128 recorded_at: Utc::now(),
129 confidence: 1.0,
130 episode_id: None,
131 metadata: None,
132 }
133 }
134
135 pub fn is_current(&self) -> bool {
137 self.valid_until.is_none()
138 }
139
140 pub fn is_valid_at(&self, at: DateTime<Utc>) -> bool {
142 let after_start = self.valid_from.is_none_or(|vf| vf <= at);
143 let before_end = self.valid_until.is_none_or(|vu| vu > at);
144 after_start && before_end
145 }
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct EpisodeResult {
151 pub episode_id: String,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct FusedEpisodeResult {
157 pub episode: Episode,
158 pub score: f64,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct GraphStats {
164 pub episode_count: usize,
165 pub entity_count: usize,
166 pub edge_count: usize,
167 pub sources: Vec<(String, usize)>,
168 pub db_size_bytes: u64,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct EntityContext {
174 pub entity: Entity,
175 pub edges: Vec<Edge>,
176 pub neighbors: Vec<Entity>,
177}