reasoninglayer 1.0.3

Rust client SDK for the Reasoning Layer API
Documentation
//! Advanced reasoning DTOs (entailment, residuation, temporal planning, coordination).

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

use crate::api_spec::QueryTerm;
use super::osf::OsfClauseDto;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntailmentRequest {
    pub antecedent: OsfClauseDto,
    pub consequent: OsfClauseDto,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub facts: Vec<OsfClauseDto>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntailmentResponse {
    pub computation_time_ms: u64,
    pub confidence: f64,
    pub entails: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisentailmentRequest {
    pub antecedent: OsfClauseDto,
    pub consequent: OsfClauseDto,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub facts: Vec<OsfClauseDto>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisentailmentResponse {
    pub computation_time_ms: u64,
    pub confidence: f64,
    pub holds: bool,
    pub violated: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceAssessmentRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub aggregation_strategy: Option<String>,
    pub evidence_link_field: String,
    pub evidence_sort_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub penalty_field: Option<String>,
    pub quality_field: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub quality_threshold: Option<f64>,
    pub subject_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub subject_penalty_field: Option<String>,
    pub support_field: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceItemDto {
    pub contribution: f64,
    pub quality_weight: f64,
    pub supports: bool,
    pub term_id: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EvidenceAssessmentResponse {
    pub avg_contradict_quality: f64,
    pub avg_support_quality: f64,
    pub color: String,
    pub computation_time_ms: u64,
    #[serde(default)]
    pub contradicting_evidence: Vec<EvidenceItemDto>,
    pub evidence_count: u64,
    #[serde(default)]
    pub explanation: Vec<String>,
    pub label: String,
    pub max_quality: f64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub penalty_applied: Option<f64>,
    pub residuated: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub residuation_reason: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub residuation_trigger: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub score_before_penalty: Option<f64>,
    pub subject_id: String,
    #[serde(default)]
    pub supporting_evidence: Vec<EvidenceItemDto>,
    pub truthfulness: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResiduationRequest {
    pub term_id: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResiduationResponse {
    pub computation_time_ms: u64,
    #[serde(default)]
    pub resumption_triggers: Vec<String>,
    #[serde(default)]
    pub suspended_operations: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GoalResiduationRequest {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub available_sorts: Vec<String>,
    pub goal_sort_name: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_results: Option<u32>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResiduatedEntryDto {
    pub feature_name: String,
    pub info_gain: f64,
    #[serde(default)]
    pub needed_by: Vec<String>,
    pub rule_count: u64,
    pub wake_trigger: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GoalResiduationResponse {
    pub computation_time_ms: u64,
    #[serde(default)]
    pub entries: Vec<ResiduatedEntryDto>,
    pub rules_analysed: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceSpec {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub fuzzy_query: Option<QueryTerm>,
    pub match_field: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub min_degree: Option<f64>,
    pub sort_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceCoordinationRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub compatibility_threshold: Option<f64>,
    pub num_selections: u32,
    pub resources: Vec<ResourceSpec>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CoordinatedResourceSet {
    pub compatibility_score: f64,
    pub resource_ids: Vec<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ResourceCoordinationResponse {
    pub avg_compatibility: f64,
    #[serde(default)]
    pub candidates_per_resource: Vec<u64>,
    pub computation_time_ms: u64,
    #[serde(default)]
    pub coordinated_sets: Vec<CoordinatedResourceSet>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalPlanRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub distance_metric: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub fuzzy_query: Option<QueryTerm>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub group_by_field: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_distance_km: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub min_degree: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub no_repeat_fields: Option<Vec<String>>,
    pub num_selections: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub proximity_weight: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sequence_by_field: Option<String>,
    pub sort_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub top_k: Option<BTreeMap<String, f64>>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TemporalPlanResponse {
    pub candidates_count: u64,
    pub computation_time_ms: u64,
    pub diversity_score: f64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub proximity_score: Option<f64>,
    #[serde(default)]
    pub selected_term_ids: Vec<String>,
}

// ─── Fuzzy merge / subsumption / residuate (shared with fuzzy but accessed via reasoning path) ─
//
// These are the reasoning-surface versions: backend dto/reasoning.rs declares its own
// FuzzySubsumptionResponse / FuzzyMergeResponse with `computation_time_ms`, distinct from the
// fuzzy-surface DTOs which expose elapsed time differently.

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FuzzySubsumptionRequestReasoning {
    pub general_term_id: String,
    pub specific_term_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub threshold: Option<f64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub lenient: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FuzzySubsumptionResponseReasoning {
    pub degree: f64,
    pub subsumes: bool,
    pub computation_time_ms: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FuzzyMergeRequestReasoning {
    pub term1_id: String,
    pub term2_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub strategy: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FuzzyMergeResponseReasoning {
    pub merged_features: u64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub strategy: Option<String>,
    pub computation_time_ms: u64,
}