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 Measurement,
27 Outcome,
28}
29
30impl EntityType {
31 #[must_use]
32 pub fn is_mutable(&self) -> bool {
33 !matches!(
34 self,
35 Self::Decision
36 | Self::Event
37 | Self::Case
38 | Self::Observation
39 | Self::Measurement
40 | Self::Outcome
41 )
42 }
43}
44
45impl fmt::Display for EntityType {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 let s = serde_json::to_value(self)
48 .ok()
49 .and_then(|v| v.as_str().map(String::from))
50 .unwrap_or_else(|| format!("{self:?}"));
51 write!(f, "{s}")
52 }
53}
54
55impl std::str::FromStr for EntityType {
56 type Err = String;
57
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 serde_json::from_value(serde_json::Value::String(s.to_string()))
60 .map_err(|_| format!("unknown entity type: {s}"))
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct NewEntity {
67 pub name: String,
68 pub entity_type: EntityType,
69 pub abstract_text: String,
70 pub overview: Option<String>,
71 pub content: Option<String>,
72 pub attributes: Option<serde_json::Value>,
73 pub source: Option<String>,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Entity {
79 pub id: serde_json::Value,
80 pub name: String,
81 pub entity_type: EntityType,
82 #[serde(rename = "abstract")]
83 pub abstract_text: String,
84 pub overview: String,
85 pub content: Option<String>,
86 pub attributes: Option<serde_json::Value>,
87 #[serde(default)]
88 pub embedding: Option<Vec<f32>>,
89 #[serde(default = "default_true")]
90 pub mutable: bool,
91 #[serde(default)]
92 pub access_count: i64,
93 #[serde(default = "default_utility_score")]
95 pub utility_score: f64,
96 #[serde(default)]
98 pub utility_updates: i64,
99 pub created_at: serde_json::Value,
100 pub updated_at: serde_json::Value,
101 pub source: Option<String>,
102}
103
104impl Entity {
105 #[must_use]
107 pub fn id_string(&self) -> String {
108 match &self.id {
109 serde_json::Value::String(s) => s.clone(),
110 other => other.to_string(),
111 }
112 }
113
114 #[must_use]
116 pub fn updated_at_string(&self) -> String {
117 match &self.updated_at {
118 serde_json::Value::String(s) => s.clone(),
119 other => other.to_string(),
120 }
121 }
122}
123
124fn default_true() -> bool {
125 true
126}
127
128fn default_utility_score() -> f64 {
129 0.5
130}
131
132#[derive(Debug, Clone, Default, Serialize)]
134pub struct EntityUpdate {
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub abstract_text: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub overview: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub content: Option<String>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub attributes: Option<serde_json::Value>,
143}
144
145#[derive(Debug, Clone)]
147pub struct NewRelationship {
148 pub from_entity: String,
149 pub to_entity: String,
150 pub rel_type: String,
151 pub description: Option<String>,
152 pub confidence: Option<f32>,
153 pub source: Option<String>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct Relationship {
159 pub id: serde_json::Value,
160 #[serde(rename = "in")]
161 pub from_id: serde_json::Value,
162 #[serde(rename = "out")]
163 pub to_id: serde_json::Value,
164 pub rel_type: String,
165 pub description: Option<String>,
166 pub valid_from: serde_json::Value,
167 pub valid_until: Option<serde_json::Value>,
168 pub confidence: f64,
169 #[serde(default)]
172 pub last_reinforced: Option<serde_json::Value>,
173 pub source: Option<String>,
174}
175
176impl Relationship {
177 #[must_use]
179 pub fn id_string(&self) -> String {
180 match &self.id {
181 serde_json::Value::String(s) => s.clone(),
182 other => other.to_string(),
183 }
184 }
185}
186
187#[derive(Debug, Clone, Copy)]
189pub enum Direction {
190 Outgoing,
191 Incoming,
192 Both,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct EntitySummary {
200 pub id: serde_json::Value,
201 pub name: String,
202 pub entity_type: EntityType,
203 #[serde(rename = "abstract")]
204 pub abstract_text: String,
205}
206
207impl EntitySummary {
208 #[must_use]
209 pub fn id_string(&self) -> String {
210 match &self.id {
211 serde_json::Value::String(s) => s.clone(),
212 other => other.to_string(),
213 }
214 }
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct EntityDetail {
220 pub id: serde_json::Value,
221 pub name: String,
222 pub entity_type: EntityType,
223 #[serde(rename = "abstract")]
224 pub abstract_text: String,
225 pub overview: String,
226 pub attributes: Option<serde_json::Value>,
227 #[serde(default)]
228 pub access_count: i64,
229 #[serde(default = "default_utility_score")]
230 pub utility_score: f64,
231 pub updated_at: serde_json::Value,
232 pub source: Option<String>,
233}
234
235impl EntityDetail {
236 #[must_use]
237 pub fn id_string(&self) -> String {
238 match &self.id {
239 serde_json::Value::String(s) => s.clone(),
240 other => other.to_string(),
241 }
242 }
243
244 #[must_use]
245 pub fn updated_at_string(&self) -> String {
246 match &self.updated_at {
247 serde_json::Value::String(s) => s.clone(),
248 other => other.to_string(),
249 }
250 }
251}
252
253#[derive(Debug, Clone, Default)]
257pub struct SearchOptions {
258 pub limit: usize,
259 pub entity_type: Option<String>,
260 pub keyword: Option<String>,
261}
262
263#[derive(Debug, Clone)]
265pub enum MatchSource {
266 Semantic,
268 Graph { parent: String, rel_type: String },
270 Keyword,
272}
273
274#[derive(Debug, Clone)]
276pub struct ScoredEntity {
277 pub entity: EntityDetail,
278 pub score: f64,
279 pub source: MatchSource,
280}
281
282#[derive(Debug, Clone)]
284pub struct EpisodeSearchResult {
285 pub episode: Episode,
286 pub score: f64,
287 pub distance: f64,
288}
289
290#[derive(Debug, Clone)]
292pub struct QueryOptions {
293 pub limit: usize,
294 pub entity_type: Option<String>,
295 pub keyword: Option<String>,
296 pub graph_depth: u32,
297 pub include_episodes: bool,
298}
299
300impl Default for QueryOptions {
301 fn default() -> Self {
302 Self {
303 limit: 10,
304 entity_type: None,
305 keyword: None,
306 graph_depth: 1,
307 include_episodes: false,
308 }
309 }
310}
311
312#[derive(Debug, Clone)]
314pub struct QueryResult {
315 pub entities: Vec<ScoredEntity>,
316 pub episodes: Vec<EpisodeSearchResult>,
317}
318
319#[derive(Debug, Clone)]
321pub struct SearchResult {
322 pub entity: Entity,
323 pub score: f64,
324 pub distance: f64,
325}
326
327#[derive(Debug, Clone)]
329pub struct TraversalNode {
330 pub entity: EntitySummary,
331 pub edges: Vec<TraversalEdge>,
332}
333
334#[derive(Debug, Clone)]
336pub struct TraversalEdge {
337 pub rel_type: String,
338 pub direction: String,
339 pub target: TraversalNode,
340 pub valid_from: serde_json::Value,
341 pub valid_until: Option<serde_json::Value>,
342 pub confidence: f64,
343}
344
345#[derive(Debug, Clone, serde::Deserialize)]
347pub struct EdgeRow {
348 pub rel_type: String,
349 pub valid_from: serde_json::Value,
350 pub valid_until: Option<serde_json::Value>,
351 pub target_id: serde_json::Value,
352 #[serde(default = "default_confidence")]
353 pub confidence: f64,
354 #[serde(default)]
355 pub last_reinforced: Option<serde_json::Value>,
356}
357
358fn default_confidence() -> f64 {
359 1.0
360}
361
362impl EdgeRow {
363 #[must_use]
364 pub fn target_id_string(&self) -> String {
365 match &self.target_id {
366 serde_json::Value::String(s) => s.clone(),
367 other => other.to_string(),
368 }
369 }
370}
371
372#[derive(Debug, Clone)]
374pub struct GraphStats {
375 pub entity_count: u64,
376 pub relationship_count: u64,
377 pub episode_count: u64,
378 pub entity_type_counts: HashMap<String, u64>,
379}
380
381#[derive(Debug, Clone)]
385pub struct NewEpisode {
386 pub session_id: String,
387 pub abstract_text: String,
388 pub overview: Option<String>,
389 pub content: Option<String>,
390 pub log_number: Option<u32>,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct Episode {
396 pub id: serde_json::Value,
397 pub session_id: String,
398 pub timestamp: serde_json::Value,
399 #[serde(rename = "abstract")]
400 pub abstract_text: String,
401 pub overview: Option<String>,
402 pub content: Option<String>,
403 #[serde(default)]
404 pub embedding: Option<Vec<f32>>,
405 pub log_number: Option<i64>,
406}
407
408impl Episode {
409 #[must_use]
410 pub fn id_string(&self) -> String {
411 match &self.id {
412 serde_json::Value::String(s) => s.clone(),
413 other => other.to_string(),
414 }
415 }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct ExtractedEntity {
421 pub name: String,
422 #[serde(rename = "type")]
423 pub entity_type: EntityType,
424 #[serde(rename = "abstract")]
425 pub abstract_text: String,
426 pub overview: Option<String>,
427 pub content: Option<String>,
428 pub attributes: Option<serde_json::Value>,
429}
430
431impl ExtractedEntity {
432 #[must_use]
434 pub fn to_new_entity(&self, session_id: &str) -> NewEntity {
435 NewEntity {
436 name: self.name.clone(),
437 entity_type: self.entity_type.clone(),
438 abstract_text: self.abstract_text.clone(),
439 overview: self.overview.clone(),
440 content: self.content.clone(),
441 attributes: self.attributes.clone(),
442 source: Some(session_id.to_string()),
443 }
444 }
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize)]
449pub struct ExtractedRelationship {
450 pub source: String,
451 pub target: String,
452 pub rel_type: String,
453 pub description: Option<String>,
454 #[serde(default)]
455 pub confidence: Option<String>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct ExtractedCase {
461 pub problem: String,
462 pub solution: String,
463 pub context: Option<String>,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct ExtractedPattern {
469 pub name: String,
470 pub process: String,
471 pub conditions: Option<String>,
472}
473
474#[derive(Debug, Clone, Serialize, Deserialize)]
476pub struct ExtractedPreference {
477 pub facet: String,
478 pub value: String,
479 pub context: Option<String>,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize, Default)]
484pub struct ExtractionResult {
485 #[serde(default)]
486 pub entities: Vec<ExtractedEntity>,
487 #[serde(default)]
488 pub relationships: Vec<ExtractedRelationship>,
489 #[serde(default)]
490 pub cases: Vec<ExtractedCase>,
491 #[serde(default)]
492 pub patterns: Vec<ExtractedPattern>,
493 #[serde(default)]
494 pub preferences: Vec<ExtractedPreference>,
495}
496
497#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
499#[serde(rename_all = "snake_case")]
500pub enum DedupDecision {
501 Skip,
502 Create,
503 Merge { target: String },
504}
505
506pub mod pipeline_rels {
510 pub const EVOLVED_FROM: &str = "EVOLVED_FROM";
511 pub const CRYSTALLIZED_FROM: &str = "CRYSTALLIZED_FROM";
512 pub const INFORMED_BY: &str = "INFORMED_BY";
513 pub const EXPLORES: &str = "EXPLORES";
514 pub const GRADUATED_TO: &str = "GRADUATED_TO";
515 pub const ARCHIVED_FROM: &str = "ARCHIVED_FROM";
516 pub const CONNECTED_TO: &str = "CONNECTED_TO";
517 pub const PROMPTED_BY: &str = "PROMPTED_BY";
518 pub const ANSWERED_BY: &str = "ANSWERED_BY";
519}
520
521pub mod vigil_rels {
523 pub const MEASURED_DURING: &str = "MEASURED_DURING";
524 pub const RESULTED_IN: &str = "RESULTED_IN";
525 pub const TRIGGERED_BY: &str = "TRIGGERED_BY";
526}
527
528#[derive(Debug, Clone, Default)]
530pub struct VigilSyncReport {
531 pub measurements_created: u32,
532 pub outcomes_created: u32,
533 pub events_created: u32,
534 pub relationships_created: u32,
535 pub skipped: u32,
536 pub errors: Vec<String>,
537}
538
539#[derive(Debug, Clone, Default)]
541pub struct PipelineDocuments {
542 pub learning: String,
543 pub thoughts: String,
544 pub curiosity: String,
545 pub reflections: String,
546 pub praxis: String,
547}
548
549#[derive(Debug, Clone, Default)]
551pub struct PipelineSyncReport {
552 pub entities_created: u32,
553 pub entities_updated: u32,
554 pub entities_archived: u32,
555 pub relationships_created: u32,
556 pub relationships_skipped: u32,
557 pub errors: Vec<String>,
558}
559
560#[derive(Debug, Clone, Default)]
562pub struct PipelineGraphStats {
563 pub by_stage: HashMap<String, HashMap<String, u64>>,
564 pub stale_thoughts: Vec<EntityDetail>,
565 pub stale_questions: Vec<EntityDetail>,
566 pub orphan_count: u64,
567 pub total_entities: u64,
568 pub last_movement: Option<String>,
569}
570
571#[derive(Debug, Clone)]
573pub struct PipelineEntry {
574 pub title: String,
576 pub body: String,
578 pub status: String,
580 pub stage: String,
582 pub entity_type: EntityType,
584 pub date: Option<String>,
586 pub source_ref: Option<String>,
588 pub destination: Option<String>,
590 pub connected_to: Vec<String>,
592 pub sub_type: Option<String>,
594}
595
596#[derive(Debug, Clone, Default)]
598pub struct IngestionReport {
599 pub episodes_created: u32,
600 pub entities_created: u32,
601 pub entities_merged: u32,
602 pub entities_skipped: u32,
603 pub relationships_created: u32,
604 pub relationships_skipped: u32,
605 pub errors: Vec<String>,
606 pub estimated_tokens: u64,
607}