1use std::collections::HashMap;
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9#[serde(rename_all = "snake_case")]
10pub enum EntityType {
11 Person,
12 Project,
13 Tool,
14 Service,
15 Preference,
16 Decision,
17 Event,
18 Concept,
19 Case,
20 Pattern,
21 Thread,
22 Thought,
23 Question,
24 Observation,
25 Policy,
26}
27
28impl EntityType {
29 pub fn is_mutable(&self) -> bool {
30 !matches!(
31 self,
32 Self::Decision | Self::Event | Self::Case | Self::Observation
33 )
34 }
35}
36
37impl fmt::Display for EntityType {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 let s = serde_json::to_value(self)
40 .ok()
41 .and_then(|v| v.as_str().map(String::from))
42 .unwrap_or_else(|| format!("{:?}", self));
43 write!(f, "{}", s)
44 }
45}
46
47impl std::str::FromStr for EntityType {
48 type Err = String;
49
50 fn from_str(s: &str) -> Result<Self, Self::Err> {
51 serde_json::from_value(serde_json::Value::String(s.to_string()))
52 .map_err(|_| format!("unknown entity type: {}", s))
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct NewEntity {
59 pub name: String,
60 pub entity_type: EntityType,
61 pub abstract_text: String,
62 pub overview: Option<String>,
63 pub content: Option<String>,
64 pub attributes: Option<serde_json::Value>,
65 pub source: Option<String>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct Entity {
71 pub id: serde_json::Value,
72 pub name: String,
73 pub entity_type: EntityType,
74 #[serde(rename = "abstract")]
75 pub abstract_text: String,
76 pub overview: String,
77 pub content: Option<String>,
78 pub attributes: Option<serde_json::Value>,
79 #[serde(default)]
80 pub embedding: Option<Vec<f32>>,
81 #[serde(default = "default_true")]
82 pub mutable: bool,
83 #[serde(default)]
84 pub access_count: i64,
85 pub created_at: serde_json::Value,
86 pub updated_at: serde_json::Value,
87 pub source: Option<String>,
88}
89
90impl Entity {
91 pub fn id_string(&self) -> String {
93 match &self.id {
94 serde_json::Value::String(s) => s.clone(),
95 other => other.to_string(),
96 }
97 }
98
99 pub fn updated_at_string(&self) -> String {
101 match &self.updated_at {
102 serde_json::Value::String(s) => s.clone(),
103 other => other.to_string(),
104 }
105 }
106}
107
108fn default_true() -> bool {
109 true
110}
111
112#[derive(Debug, Clone, Default, Serialize)]
114pub struct EntityUpdate {
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub abstract_text: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub overview: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub content: Option<String>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub attributes: Option<serde_json::Value>,
123}
124
125#[derive(Debug, Clone)]
127pub struct NewRelationship {
128 pub from_entity: String,
129 pub to_entity: String,
130 pub rel_type: String,
131 pub description: Option<String>,
132 pub confidence: Option<f32>,
133 pub source: Option<String>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Relationship {
139 pub id: serde_json::Value,
140 #[serde(rename = "in")]
141 pub from_id: serde_json::Value,
142 #[serde(rename = "out")]
143 pub to_id: serde_json::Value,
144 pub rel_type: String,
145 pub description: Option<String>,
146 pub valid_from: serde_json::Value,
147 pub valid_until: Option<serde_json::Value>,
148 pub confidence: f64,
149 pub source: Option<String>,
150}
151
152impl Relationship {
153 pub fn id_string(&self) -> String {
155 match &self.id {
156 serde_json::Value::String(s) => s.clone(),
157 other => other.to_string(),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy)]
164pub enum Direction {
165 Outgoing,
166 Incoming,
167 Both,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct EntitySummary {
175 pub id: serde_json::Value,
176 pub name: String,
177 pub entity_type: EntityType,
178 #[serde(rename = "abstract")]
179 pub abstract_text: String,
180}
181
182impl EntitySummary {
183 pub fn id_string(&self) -> String {
184 match &self.id {
185 serde_json::Value::String(s) => s.clone(),
186 other => other.to_string(),
187 }
188 }
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct EntityDetail {
194 pub id: serde_json::Value,
195 pub name: String,
196 pub entity_type: EntityType,
197 #[serde(rename = "abstract")]
198 pub abstract_text: String,
199 pub overview: String,
200 pub attributes: Option<serde_json::Value>,
201 #[serde(default)]
202 pub access_count: i64,
203 pub updated_at: serde_json::Value,
204 pub source: Option<String>,
205}
206
207impl EntityDetail {
208 pub fn id_string(&self) -> String {
209 match &self.id {
210 serde_json::Value::String(s) => s.clone(),
211 other => other.to_string(),
212 }
213 }
214
215 pub fn updated_at_string(&self) -> String {
216 match &self.updated_at {
217 serde_json::Value::String(s) => s.clone(),
218 other => other.to_string(),
219 }
220 }
221}
222
223#[derive(Debug, Clone, Default)]
227pub struct SearchOptions {
228 pub limit: usize,
229 pub entity_type: Option<String>,
230 pub keyword: Option<String>,
231}
232
233#[derive(Debug, Clone)]
235pub enum MatchSource {
236 Semantic,
238 Graph { parent: String, rel_type: String },
240 Keyword,
242}
243
244#[derive(Debug, Clone)]
246pub struct ScoredEntity {
247 pub entity: EntityDetail,
248 pub score: f64,
249 pub source: MatchSource,
250}
251
252#[derive(Debug, Clone)]
254pub struct EpisodeSearchResult {
255 pub episode: Episode,
256 pub score: f64,
257 pub distance: f64,
258}
259
260#[derive(Debug, Clone)]
262pub struct QueryOptions {
263 pub limit: usize,
264 pub entity_type: Option<String>,
265 pub keyword: Option<String>,
266 pub graph_depth: u32,
267 pub include_episodes: bool,
268}
269
270impl Default for QueryOptions {
271 fn default() -> Self {
272 Self {
273 limit: 10,
274 entity_type: None,
275 keyword: None,
276 graph_depth: 1,
277 include_episodes: false,
278 }
279 }
280}
281
282#[derive(Debug, Clone)]
284pub struct QueryResult {
285 pub entities: Vec<ScoredEntity>,
286 pub episodes: Vec<EpisodeSearchResult>,
287}
288
289#[derive(Debug, Clone)]
291pub struct SearchResult {
292 pub entity: Entity,
293 pub score: f64,
294 pub distance: f64,
295}
296
297#[derive(Debug, Clone)]
299pub struct TraversalNode {
300 pub entity: EntitySummary,
301 pub edges: Vec<TraversalEdge>,
302}
303
304#[derive(Debug, Clone)]
306pub struct TraversalEdge {
307 pub rel_type: String,
308 pub direction: String,
309 pub target: TraversalNode,
310 pub valid_from: serde_json::Value,
311 pub valid_until: Option<serde_json::Value>,
312}
313
314#[derive(Debug, Clone, serde::Deserialize)]
316pub struct EdgeRow {
317 pub rel_type: String,
318 pub valid_from: serde_json::Value,
319 pub valid_until: Option<serde_json::Value>,
320 pub target_id: serde_json::Value,
321}
322
323impl EdgeRow {
324 pub fn target_id_string(&self) -> String {
325 match &self.target_id {
326 serde_json::Value::String(s) => s.clone(),
327 other => other.to_string(),
328 }
329 }
330}
331
332#[derive(Debug, Clone)]
334pub struct GraphStats {
335 pub entity_count: u64,
336 pub relationship_count: u64,
337 pub episode_count: u64,
338 pub entity_type_counts: HashMap<String, u64>,
339}
340
341#[derive(Debug, Clone)]
345pub struct NewEpisode {
346 pub session_id: String,
347 pub abstract_text: String,
348 pub overview: Option<String>,
349 pub content: Option<String>,
350 pub log_number: Option<u32>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct Episode {
356 pub id: serde_json::Value,
357 pub session_id: String,
358 pub timestamp: serde_json::Value,
359 #[serde(rename = "abstract")]
360 pub abstract_text: String,
361 pub overview: Option<String>,
362 pub content: Option<String>,
363 #[serde(default)]
364 pub embedding: Option<Vec<f32>>,
365 pub log_number: Option<i64>,
366}
367
368impl Episode {
369 pub fn id_string(&self) -> String {
370 match &self.id {
371 serde_json::Value::String(s) => s.clone(),
372 other => other.to_string(),
373 }
374 }
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct ExtractedEntity {
380 pub name: String,
381 #[serde(rename = "type")]
382 pub entity_type: EntityType,
383 #[serde(rename = "abstract")]
384 pub abstract_text: String,
385 pub overview: Option<String>,
386 pub content: Option<String>,
387 pub attributes: Option<serde_json::Value>,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct ExtractedRelationship {
393 pub source: String,
394 pub target: String,
395 pub rel_type: String,
396 pub description: Option<String>,
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct ExtractedCase {
402 pub problem: String,
403 pub solution: String,
404 pub context: Option<String>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409pub struct ExtractedPattern {
410 pub name: String,
411 pub process: String,
412 pub conditions: Option<String>,
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize)]
417pub struct ExtractedPreference {
418 pub facet: String,
419 pub value: String,
420 pub context: Option<String>,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize, Default)]
425pub struct ExtractionResult {
426 #[serde(default)]
427 pub entities: Vec<ExtractedEntity>,
428 #[serde(default)]
429 pub relationships: Vec<ExtractedRelationship>,
430 #[serde(default)]
431 pub cases: Vec<ExtractedCase>,
432 #[serde(default)]
433 pub patterns: Vec<ExtractedPattern>,
434 #[serde(default)]
435 pub preferences: Vec<ExtractedPreference>,
436}
437
438#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
440#[serde(rename_all = "snake_case")]
441pub enum DedupDecision {
442 Skip,
443 Create,
444 Merge { target: String },
445}
446
447pub mod pipeline_rels {
451 pub const EVOLVED_FROM: &str = "EVOLVED_FROM";
452 pub const CRYSTALLIZED_FROM: &str = "CRYSTALLIZED_FROM";
453 pub const INFORMED_BY: &str = "INFORMED_BY";
454 pub const EXPLORES: &str = "EXPLORES";
455 pub const GRADUATED_TO: &str = "GRADUATED_TO";
456 pub const ARCHIVED_FROM: &str = "ARCHIVED_FROM";
457 pub const CONNECTED_TO: &str = "CONNECTED_TO";
458}
459
460#[derive(Debug, Clone, Default)]
462pub struct PipelineDocuments {
463 pub learning: String,
464 pub thoughts: String,
465 pub curiosity: String,
466 pub reflections: String,
467 pub praxis: String,
468}
469
470#[derive(Debug, Clone, Default)]
472pub struct PipelineSyncReport {
473 pub entities_created: u32,
474 pub entities_updated: u32,
475 pub entities_archived: u32,
476 pub relationships_created: u32,
477 pub relationships_skipped: u32,
478 pub errors: Vec<String>,
479}
480
481#[derive(Debug, Clone, Default)]
483pub struct PipelineGraphStats {
484 pub by_stage: HashMap<String, HashMap<String, u64>>,
485 pub stale_thoughts: Vec<EntityDetail>,
486 pub stale_questions: Vec<EntityDetail>,
487 pub total_entities: u64,
488 pub last_movement: Option<String>,
489}
490
491#[derive(Debug, Clone)]
493pub struct PipelineEntry {
494 pub title: String,
496 pub body: String,
498 pub status: String,
500 pub stage: String,
502 pub entity_type: EntityType,
504 pub date: Option<String>,
506 pub source_ref: Option<String>,
508 pub destination: Option<String>,
510 pub connected_to: Vec<String>,
512 pub sub_type: Option<String>,
514}
515
516#[derive(Debug, Clone, Default)]
518pub struct IngestionReport {
519 pub episodes_created: u32,
520 pub entities_created: u32,
521 pub entities_merged: u32,
522 pub entities_skipped: u32,
523 pub relationships_created: u32,
524 pub relationships_skipped: u32,
525 pub errors: Vec<String>,
526}