reasoninglayer 0.2.1

Rust client SDK for the Reasoning Layer API
Documentation
//! Entity review DTOs.

use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};

/// Query parameters for `GET /reviews/pending`. Backend requires `tenant_id` and supports
/// optional sort/reason filters plus pagination.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListPendingReviewsQuery {
    pub tenant_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sort: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reason: Option<ReviewReason>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub page: Option<u32>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub page_size: Option<u32>,
}

/// Response shape for `GET /reviews/pending`.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ListPendingReviewsResponse {
    #[serde(default)]
    pub items: Vec<PendingReviewDto>,
    pub total: u64,
    pub page: u32,
    pub page_size: u32,
}

/// Summary of pending reviews for a tenant — `GET /reviews/summary/{tenant_id}`.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ReviewSummaryResponse {
    pub total_pending: u64,
    #[serde(default)]
    pub by_reason: BTreeMap<String, u64>,
    #[serde(default)]
    pub by_sort: BTreeMap<String, u64>,
    pub high_confidence_count: u64,
    pub low_confidence_count: u64,
}

/// Final status of an entity in the review workflow.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReviewStatus {
    #[default]
    Pending,
    Approved,
    Rejected,
    Corrected,
    Merged,
}

/// Response for approve/reject/merge/correct endpoints.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewActionResponse {
    pub success: bool,
    pub status: ReviewStatus,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub term_id: Option<String>,
    pub message: String,
}

/// Response for re-extract endpoint.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ReExtractResponse {
    pub entities_extracted: u64,
    pub entities_changed: u64,
    pub pending_review_count: u64,
}

/// Response for `add` endpoint.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddPendingReviewResponse {
    pub review_id: String,
}

/// Response for bulk-approve / bulk-reject / bulk-merge.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BulkReviewResponse {
    pub success_count: u64,
    pub failure_count: u64,
    #[serde(default)]
    pub succeeded_ids: Vec<String>,
    /// Failures as `[review_id, reason]` pairs.
    #[serde(default)]
    pub failed_ids: Vec<(String, String)>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReviewReason {
    AmbiguousSort,
    LowConfidence,
    MultipleCandidates,
    UnknownSort,
    ConflictingFeatures,
    MissingRequiredFeatures,
    ManualRequest,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ConflictResolution {
    KeepExisting,
    UseNew,
    Merge,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewCandidateMatchDto {
    pub display_name: String,
    #[serde(default)]
    pub features: BTreeMap<String, serde_json::Value>,
    pub similarity: f64,
    pub sort: String,
    pub term_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortSuggestionDto {
    /// Suggested sort name.
    pub sort_name: String,
    /// Confidence that this sort applies (0.0 - 1.0).
    pub confidence: f64,
    /// Why this sort is suggested.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub reason: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PendingReviewDto {
    #[serde(default)]
    pub candidates: Vec<ReviewCandidateMatchDto>,
    pub entity_id: String,
    pub reason: String,
    pub sort: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddPendingReviewRequest {
    #[serde(default)]
    pub candidates: Vec<ReviewCandidateMatchDto>,
    pub confidence: f64,
    pub entity_id: String,
    #[serde(default)]
    pub features: BTreeMap<String, serde_json::Value>,
    pub owner_id: String,
    pub reason: ReviewReason,
    pub sort: String,
    #[serde(default)]
    pub sort_suggestions: Vec<SortSuggestionDto>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source_document: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub source_text: Option<String>,
    pub tenant_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApproveEntityRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub notes: Option<String>,
    pub review_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkApproveRequest {
    pub review_ids: Vec<String>,
    pub reviewed_by: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkMergeRequest {
    pub review_ids: Vec<String>,
    pub reviewed_by: String,
    pub target_term_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BulkRejectRequest {
    pub reason: String,
    pub review_ids: Vec<String>,
    pub reviewed_by: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CorrectEntityRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub features: Option<BTreeMap<String, serde_json::Value>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub notes: Option<String>,
    pub review_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub sort: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MergeEntityRequest {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub conflict_resolution: Option<ConflictResolution>,
    pub review_id: String,
    pub target_term_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReExtractRequest {
    pub document_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extraction_strategy: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub focus_sorts: Option<Vec<String>>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub instructions: Option<String>,
    pub tenant_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RejectEntityRequest {
    pub reason: String,
    pub review_id: String,
}