use std::collections::HashMap;
use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum EntityType {
Person,
Project,
Tool,
Service,
Preference,
Decision,
Event,
Concept,
Case,
Pattern,
Thread,
Thought,
Question,
Observation,
Policy,
}
impl EntityType {
pub fn is_mutable(&self) -> bool {
!matches!(
self,
Self::Decision | Self::Event | Self::Case | Self::Observation
)
}
}
impl fmt::Display for EntityType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = serde_json::to_value(self)
.ok()
.and_then(|v| v.as_str().map(String::from))
.unwrap_or_else(|| format!("{:?}", self));
write!(f, "{}", s)
}
}
impl std::str::FromStr for EntityType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(serde_json::Value::String(s.to_string()))
.map_err(|_| format!("unknown entity type: {}", s))
}
}
#[derive(Debug, Clone)]
pub struct NewEntity {
pub name: String,
pub entity_type: EntityType,
pub abstract_text: String,
pub overview: Option<String>,
pub content: Option<String>,
pub attributes: Option<serde_json::Value>,
pub source: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Entity {
pub id: serde_json::Value,
pub name: String,
pub entity_type: EntityType,
#[serde(rename = "abstract")]
pub abstract_text: String,
pub overview: String,
pub content: Option<String>,
pub attributes: Option<serde_json::Value>,
#[serde(default)]
pub embedding: Option<Vec<f32>>,
#[serde(default = "default_true")]
pub mutable: bool,
#[serde(default)]
pub access_count: i64,
pub created_at: serde_json::Value,
pub updated_at: serde_json::Value,
pub source: Option<String>,
}
impl Entity {
pub fn id_string(&self) -> String {
match &self.id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
pub fn updated_at_string(&self) -> String {
match &self.updated_at {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct EntityUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
pub abstract_text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overview: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<serde_json::Value>,
}
#[derive(Debug, Clone)]
pub struct NewRelationship {
pub from_entity: String,
pub to_entity: String,
pub rel_type: String,
pub description: Option<String>,
pub confidence: Option<f32>,
pub source: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Relationship {
pub id: serde_json::Value,
#[serde(rename = "in")]
pub from_id: serde_json::Value,
#[serde(rename = "out")]
pub to_id: serde_json::Value,
pub rel_type: String,
pub description: Option<String>,
pub valid_from: serde_json::Value,
pub valid_until: Option<serde_json::Value>,
pub confidence: f64,
pub source: Option<String>,
}
impl Relationship {
pub fn id_string(&self) -> String {
match &self.id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Direction {
Outgoing,
Incoming,
Both,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntitySummary {
pub id: serde_json::Value,
pub name: String,
pub entity_type: EntityType,
#[serde(rename = "abstract")]
pub abstract_text: String,
}
impl EntitySummary {
pub fn id_string(&self) -> String {
match &self.id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityDetail {
pub id: serde_json::Value,
pub name: String,
pub entity_type: EntityType,
#[serde(rename = "abstract")]
pub abstract_text: String,
pub overview: String,
pub attributes: Option<serde_json::Value>,
#[serde(default)]
pub access_count: i64,
pub updated_at: serde_json::Value,
pub source: Option<String>,
}
impl EntityDetail {
pub fn id_string(&self) -> String {
match &self.id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
pub fn updated_at_string(&self) -> String {
match &self.updated_at {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct SearchOptions {
pub limit: usize,
pub entity_type: Option<String>,
pub keyword: Option<String>,
}
#[derive(Debug, Clone)]
pub enum MatchSource {
Semantic,
Graph { parent: String, rel_type: String },
Keyword,
}
#[derive(Debug, Clone)]
pub struct ScoredEntity {
pub entity: EntityDetail,
pub score: f64,
pub source: MatchSource,
}
#[derive(Debug, Clone)]
pub struct EpisodeSearchResult {
pub episode: Episode,
pub score: f64,
pub distance: f64,
}
#[derive(Debug, Clone)]
pub struct QueryOptions {
pub limit: usize,
pub entity_type: Option<String>,
pub keyword: Option<String>,
pub graph_depth: u32,
pub include_episodes: bool,
}
impl Default for QueryOptions {
fn default() -> Self {
Self {
limit: 10,
entity_type: None,
keyword: None,
graph_depth: 1,
include_episodes: false,
}
}
}
#[derive(Debug, Clone)]
pub struct QueryResult {
pub entities: Vec<ScoredEntity>,
pub episodes: Vec<EpisodeSearchResult>,
}
#[derive(Debug, Clone)]
pub struct SearchResult {
pub entity: Entity,
pub score: f64,
pub distance: f64,
}
#[derive(Debug, Clone)]
pub struct TraversalNode {
pub entity: EntitySummary,
pub edges: Vec<TraversalEdge>,
}
#[derive(Debug, Clone)]
pub struct TraversalEdge {
pub rel_type: String,
pub direction: String,
pub target: TraversalNode,
pub valid_from: serde_json::Value,
pub valid_until: Option<serde_json::Value>,
}
#[derive(Debug, Clone, serde::Deserialize)]
pub struct EdgeRow {
pub rel_type: String,
pub valid_from: serde_json::Value,
pub valid_until: Option<serde_json::Value>,
pub target_id: serde_json::Value,
}
impl EdgeRow {
pub fn target_id_string(&self) -> String {
match &self.target_id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct GraphStats {
pub entity_count: u64,
pub relationship_count: u64,
pub episode_count: u64,
pub entity_type_counts: HashMap<String, u64>,
}
#[derive(Debug, Clone)]
pub struct NewEpisode {
pub session_id: String,
pub abstract_text: String,
pub overview: Option<String>,
pub content: Option<String>,
pub log_number: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Episode {
pub id: serde_json::Value,
pub session_id: String,
pub timestamp: serde_json::Value,
#[serde(rename = "abstract")]
pub abstract_text: String,
pub overview: Option<String>,
pub content: Option<String>,
#[serde(default)]
pub embedding: Option<Vec<f32>>,
pub log_number: Option<i64>,
}
impl Episode {
pub fn id_string(&self) -> String {
match &self.id {
serde_json::Value::String(s) => s.clone(),
other => other.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedEntity {
pub name: String,
#[serde(rename = "type")]
pub entity_type: EntityType,
#[serde(rename = "abstract")]
pub abstract_text: String,
pub overview: Option<String>,
pub content: Option<String>,
pub attributes: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedRelationship {
pub source: String,
pub target: String,
pub rel_type: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedCase {
pub problem: String,
pub solution: String,
pub context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedPattern {
pub name: String,
pub process: String,
pub conditions: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtractedPreference {
pub facet: String,
pub value: String,
pub context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ExtractionResult {
#[serde(default)]
pub entities: Vec<ExtractedEntity>,
#[serde(default)]
pub relationships: Vec<ExtractedRelationship>,
#[serde(default)]
pub cases: Vec<ExtractedCase>,
#[serde(default)]
pub patterns: Vec<ExtractedPattern>,
#[serde(default)]
pub preferences: Vec<ExtractedPreference>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DedupDecision {
Skip,
Create,
Merge { target: String },
}
pub mod pipeline_rels {
pub const EVOLVED_FROM: &str = "EVOLVED_FROM";
pub const CRYSTALLIZED_FROM: &str = "CRYSTALLIZED_FROM";
pub const INFORMED_BY: &str = "INFORMED_BY";
pub const EXPLORES: &str = "EXPLORES";
pub const GRADUATED_TO: &str = "GRADUATED_TO";
pub const ARCHIVED_FROM: &str = "ARCHIVED_FROM";
pub const CONNECTED_TO: &str = "CONNECTED_TO";
}
#[derive(Debug, Clone, Default)]
pub struct PipelineDocuments {
pub learning: String,
pub thoughts: String,
pub curiosity: String,
pub reflections: String,
pub praxis: String,
}
#[derive(Debug, Clone, Default)]
pub struct PipelineSyncReport {
pub entities_created: u32,
pub entities_updated: u32,
pub entities_archived: u32,
pub relationships_created: u32,
pub relationships_skipped: u32,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct PipelineGraphStats {
pub by_stage: HashMap<String, HashMap<String, u64>>,
pub stale_thoughts: Vec<EntityDetail>,
pub stale_questions: Vec<EntityDetail>,
pub total_entities: u64,
pub last_movement: Option<String>,
}
#[derive(Debug, Clone)]
pub struct PipelineEntry {
pub title: String,
pub body: String,
pub status: String,
pub stage: String,
pub entity_type: EntityType,
pub date: Option<String>,
pub source_ref: Option<String>,
pub destination: Option<String>,
pub connected_to: Vec<String>,
pub sub_type: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct IngestionReport {
pub episodes_created: u32,
pub entities_created: u32,
pub entities_merged: u32,
pub entities_skipped: u32,
pub relationships_created: u32,
pub relationships_skipped: u32,
pub errors: Vec<String>,
}