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