reasoninglayer 0.1.2

Rust client SDK for the Reasoning Layer API
Documentation
//! Visualization (graphs, lattice, residuation) DTOs.

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

// ─── Enum-like types (represented as String for forward compat; custom variants use map form) ─

/// Layout algorithm — known values plus `{custom: "name"}`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LayoutAlgorithmDto {
    Named(String),
    Custom { custom: String },
}

/// Layout direction.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LayoutDirectionDto {
    TopBottom,
    BottomTop,
    LeftRight,
    RightLeft,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum GlbLubOperation {
    Glb,
    Lub,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResiduationKind {
    Inference,
    Fuzzy,
    Extraction,
    All,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResiduationStateFilter {
    Pending,
    Suspended,
    Ready,
    Completed,
    All,
}

/// Node type — predefined values plus `{custom: "name"}`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum NodeTypeDto {
    Named(String),
    Custom { custom: String },
}

/// Edge type — predefined values plus `{custom: "name"}`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum EdgeTypeDto {
    Named(String),
    Custom { custom: String },
}

/// Hyperedge type — predefined values plus `{custom: "name"}`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HyperedgeTypeDto {
    Named(String),
    Custom { custom: String },
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WaitingConditionType {
    FeatureBinding,
    SortNarrowing,
    TermCreation,
    ExternalInput,
    ConstraintSatisfaction,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResiduationStateDto {
    Pending,
    Suspended,
    Ready,
    Completed,
    Failed,
}

// ─── Graph primitives ────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphNodeDto {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub color: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub entity_id: Option<String>,
    pub id: String,
    pub label: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub level: Option<u32>,
    pub node_type: NodeTypeDto,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub properties: BTreeMap<String, String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub size: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphEdgeDto {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub color: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub curvature: Option<f64>,
    pub edge_type: EdgeTypeDto,
    pub id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub label: Option<String>,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub properties: BTreeMap<String, String>,
    pub source: String,
    pub target: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub weight: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HyperedgeDto {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub color: Option<String>,
    pub hyperedge_type: HyperedgeTypeDto,
    pub id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub label: Option<String>,
    #[serde(default)]
    pub nodes: Vec<String>,
    pub ordered: bool,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub properties: BTreeMap<String, String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source_term_id: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GraphMetadataDto {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub edge_count: u64,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub extra: BTreeMap<String, String>,
    pub hyperedge_count: u64,
    pub is_directed: bool,
    pub node_count: u64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LayoutHintsDto {
    pub algorithm: LayoutAlgorithmDto,
    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
    pub clusters: BTreeMap<String, Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub direction: Option<LayoutDirectionDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub level_spacing: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub node_spacing: Option<f64>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VisualizationGraphDto {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub dot_output: Option<String>,
    #[serde(default)]
    pub edges: Vec<GraphEdgeDto>,
    pub graph_id: String,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub hyperedges: Vec<HyperedgeDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub layout: Option<LayoutHintsDto>,
    pub metadata: GraphMetadataDto,
    #[serde(default)]
    pub nodes: Vec<GraphNodeDto>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortSummaryDto {
    pub id: String,
    pub is_synthetic: bool,
    pub name: String,
}

// ─── Hypergraph ──────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HypergraphRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generate_dot: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub include_hyperedges: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_depth: Option<u32>,
    pub root_term_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub s_threshold: Option<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentDto {
    pub id: String,
    #[serde(default)]
    pub nodes: Vec<String>,
    pub s_value: f64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DegreeDistributionDto {
    pub avg_node_degree: f64,
    #[serde(default)]
    pub hyperedge_sizes: BTreeMap<String, u64>,
    pub max_node_degree: u64,
    #[serde(default)]
    pub node_degrees: BTreeMap<String, u64>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HypergraphStats {
    pub component_count: u64,
    pub total_coreferences: u64,
    pub total_features: u64,
    pub total_terms: u64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct HypergraphResponse {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub components: Option<Vec<ComponentDto>>,
    #[serde(default)]
    pub degree_distribution: DegreeDistributionDto,
    pub graph: VisualizationGraphDto,
    #[serde(default)]
    pub stats: HypergraphStats,
}

// ─── Lattice ─────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LatticeVisualizationRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generate_dot: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub include_synthetic: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub layout_algorithm: Option<LayoutAlgorithmDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub layout_direction: Option<LayoutDirectionDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_depth: Option<u32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub root_sort_id: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LatticeStats {
    pub leaf_sorts: u64,
    pub max_depth: u32,
    pub multiple_inheritance_count: u64,
    pub root_sorts: u64,
    pub synthetic_sorts: u64,
    pub total_sorts: u64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LatticeVisualizationResponse {
    pub graph: VisualizationGraphDto,
    #[serde(default)]
    pub stats: LatticeStats,
}

// ─── GLB/LUB trace ───────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlbLubTraceRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generate_dot: Option<bool>,
    pub operation: GlbLubOperation,
    pub sort1_id: String,
    pub sort2_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlbLubTraceStep {
    pub created_synthetic: bool,
    pub description: String,
    #[serde(default)]
    pub involved_sorts: Vec<String>,
    pub step: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlbLubComputationTrace {
    pub cache_hit: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub graph: Option<VisualizationGraphDto>,
    pub operation: GlbLubOperation,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub result: Option<SortSummaryDto>,
    pub sort1: SortSummaryDto,
    pub sort2: SortSummaryDto,
    #[serde(default)]
    pub steps: Vec<GlbLubTraceStep>,
    pub synthetic_created: bool,
}

// ─── Residuation ─────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WaitingConditionDto {
    pub condition_type: WaitingConditionType,
    pub description: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub feature_name: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub target_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResiduatedTermDto {
    pub created_at: u64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub id: String,
    pub kind: ResiduationKind,
    pub state: ResiduationStateDto,
    pub term_id: String,
    #[serde(default)]
    pub waiting_for: Vec<WaitingConditionDto>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResiduationStats {
    pub completed: u64,
    pub failed: u64,
    pub pending: u64,
    pub ready: u64,
    pub suspended: u64,
    pub total: u64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResiduationStateRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generate_dot: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub include_dependencies: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub kind: Option<ResiduationKind>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub state: Option<ResiduationStateFilter>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResiduationStateResponse {
    #[serde(default)]
    pub dependency_edges: Vec<GraphEdgeDto>,
    #[serde(default)]
    pub dependency_nodes: Vec<GraphNodeDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub graph: Option<VisualizationGraphDto>,
    #[serde(default)]
    pub residuated_terms: Vec<ResiduatedTermDto>,
    #[serde(default)]
    pub stats: ResiduationStats,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TriggerDependencyRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub generate_dot: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub residuation_id: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TriggerDependencyResponse {
    #[serde(default)]
    pub critical_paths: Vec<Vec<String>>,
    pub graph: VisualizationGraphDto,
}