- Add `content: Option<String>` to SourceRecord with rkyv schema evolution (LegacySourceRecord compat deserializer for backward compatibility) - Add MAX_SOURCE_CONTENT_LEN (1MB) limit with API validation - Strip content from list responses, include in single-source GET - Update Go SDK RegisterSourceRequest with Content field - FCM pipeline extracts PDF text via pdftotext and passes to registration - Dashboard impact panel fetches and displays source content with expand/collapse - Add feed endpoint, dashboard feed panel, and signed assertion support - Update data-structures.md, API docs, and storage docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
281 lines
8.9 KiB
Rust
281 lines
8.9 KiB
Rust
//! Response DTOs for query results and API operations.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use utoipa::ToSchema;
|
|
|
|
use super::enums::{LifecycleDto, ObjectValueDto, SignatureDto, SourceClassDto};
|
|
|
|
// ============================================================================
|
|
// Source Status Warning (P3.2 Cascade Flagging)
|
|
// ============================================================================
|
|
|
|
/// Warning attached to assertions citing non-Active sources.
|
|
///
|
|
/// Enables "show with warning" behavior per P3.2 requirements.
|
|
/// When a source is deprecated or quarantined, assertions citing it
|
|
/// receive this warning instead of being silently filtered.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct SourceWarningDto {
|
|
/// Warning type: "deprecated" or "quarantined"
|
|
pub warning_type: String,
|
|
|
|
/// Human-readable explanation
|
|
pub message: String,
|
|
|
|
/// Source label from registry (if available)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub source_label: Option<String>,
|
|
|
|
/// When the source status was last updated (Unix timestamp)
|
|
pub status_updated_at: u64,
|
|
}
|
|
|
|
// ============================================================================
|
|
// Response DTOs
|
|
// ============================================================================
|
|
|
|
/// Response containing a single assertion.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct AssertionResponse {
|
|
/// Content-addressed hash of this assertion (hex-encoded)
|
|
pub hash: String,
|
|
|
|
/// The subject entity
|
|
pub subject: String,
|
|
|
|
/// The predicate/relation
|
|
pub predicate: String,
|
|
|
|
/// The object value
|
|
pub object: ObjectValueDto,
|
|
|
|
/// Hash of parent assertion (hex-encoded, optional)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub parent_hash: Option<String>,
|
|
|
|
/// Hash of source evidence (hex-encoded)
|
|
pub source_hash: String,
|
|
|
|
/// Source authority tier
|
|
pub source_class: SourceClassDto,
|
|
|
|
/// Perceptual hash for visual anchoring (hex-encoded, optional)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub visual_hash: Option<String>,
|
|
|
|
/// Epoch ID (hex-encoded, optional)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub epoch: Option<String>,
|
|
|
|
/// Lifecycle stage
|
|
pub lifecycle: LifecycleDto,
|
|
|
|
/// Agent signatures
|
|
pub signatures: Vec<SignatureDto>,
|
|
|
|
/// Confidence score (0.0 to 1.0)
|
|
pub confidence: f32,
|
|
|
|
/// Creation timestamp (Unix epoch)
|
|
pub timestamp: u64,
|
|
|
|
/// Semantic embedding vector (optional)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub vector: Option<Vec<f32>>,
|
|
|
|
/// Structured source metadata as a JSON string (optional).
|
|
/// Domain-specific schema (journal, DOI, sample_size, etc.).
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub source_metadata: Option<String>,
|
|
|
|
/// Free-text narrative explaining methodology, limitations, bias, and caveats.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub narrative: Option<String>,
|
|
|
|
/// Warning if this assertion cites a quarantined or deprecated source.
|
|
///
|
|
/// Present when the assertion's source has a non-Active status in the
|
|
/// Source Registry. Enables "show with warning" behavior for enterprise
|
|
/// compliance workflows.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub source_warning: Option<SourceWarningDto>,
|
|
}
|
|
|
|
/// Response from a query operation.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct QueryResponse {
|
|
/// Matching assertions
|
|
pub assertions: Vec<AssertionResponse>,
|
|
|
|
/// Total number of results returned
|
|
pub total_count: usize,
|
|
|
|
/// Whether there are more results beyond the limit
|
|
pub has_more: bool,
|
|
|
|
/// Degree of disagreement among candidates (0.0 = unanimous, 1.0 = max conflict).
|
|
/// See `stemedb_lens::compute_conflict_score()` for the canonical algorithm.
|
|
/// Only present when a lens is applied.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub conflict_score: Option<f32>,
|
|
|
|
/// Confidence in the resolution (0.0 to 1.0).
|
|
/// Only present when a lens is applied.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub resolution_confidence: Option<f32>,
|
|
|
|
/// Changelog entries for MV changes since the `since` parameter.
|
|
/// Only present when `since` parameter was provided.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub changes_since: Option<Vec<ChangeEntryDto>>,
|
|
}
|
|
|
|
/// A changelog entry from a "since" query.
|
|
///
|
|
/// Represents a single change to a materialized view's winner.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct ChangeEntryDto {
|
|
/// Unix timestamp when the change occurred.
|
|
pub timestamp: u64,
|
|
|
|
/// Hash of the previous winner (hex-encoded, None if first MV).
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub previous_winner_hash: Option<String>,
|
|
|
|
/// Hash of the new winner (hex-encoded).
|
|
pub new_winner_hash: String,
|
|
|
|
/// Subject of the changed pair.
|
|
pub subject: String,
|
|
|
|
/// Predicate of the changed pair.
|
|
pub predicate: String,
|
|
|
|
/// Which lens was used for resolution.
|
|
pub lens_name: String,
|
|
}
|
|
|
|
/// Error response.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct ErrorResponse {
|
|
/// Human-readable error message
|
|
pub error: String,
|
|
|
|
/// Machine-readable error code
|
|
pub code: String,
|
|
}
|
|
|
|
/// Health check response.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct HealthResponse {
|
|
/// Service status (e.g., "healthy")
|
|
pub status: String,
|
|
|
|
/// API version
|
|
pub version: String,
|
|
|
|
/// Total number of assertions in the database
|
|
pub assertions_count: u64,
|
|
}
|
|
|
|
/// Response from retrieving a source document.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct ProvenanceResponse {
|
|
/// BLAKE3 hash of the content (hex-encoded).
|
|
pub hash: String,
|
|
|
|
/// The source document content (base64-encoded).
|
|
pub content: String,
|
|
|
|
/// MIME type of the content.
|
|
pub content_type: String,
|
|
|
|
/// Size of the content in bytes.
|
|
pub size: usize,
|
|
}
|
|
|
|
/// Per-tier resolution result from LayeredConsensus lens.
|
|
///
|
|
/// Represents the consensus within a single source class tier.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct TierResolutionDto {
|
|
/// The tier number (0-5). Lower = higher authority.
|
|
pub tier: u8,
|
|
|
|
/// The source class for this tier.
|
|
pub source_class: SourceClassDto,
|
|
|
|
/// The winning assertion from within-tier consensus, if any candidates.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub winner: Option<AssertionResponse>,
|
|
|
|
/// Number of candidates in this tier.
|
|
pub candidates_count: usize,
|
|
|
|
/// Within-tier conflict score (0.0 = unanimous, 1.0 = max conflict).
|
|
#[schema(minimum = 0.0, maximum = 1.0)]
|
|
pub conflict_score: f32,
|
|
|
|
/// Within-tier resolution confidence (0.0 to 1.0).
|
|
#[schema(minimum = 0.0, maximum = 1.0)]
|
|
pub resolution_confidence: f32,
|
|
}
|
|
|
|
/// Response from the admin rebuild-indexes endpoint.
|
|
///
|
|
/// Reports how many assertion indexes were rebuilt, how many were
|
|
/// skipped (e.g., deserialization failures), and how long the
|
|
/// operation took.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct RebuildIndexesResponse {
|
|
/// Number of assertions whose indexes were rebuilt.
|
|
pub rebuilt_count: u64,
|
|
|
|
/// Number of keys that were skipped (deserialization failures).
|
|
pub skipped_count: u64,
|
|
|
|
/// Wall-clock time for the operation in milliseconds.
|
|
pub elapsed_ms: u64,
|
|
|
|
/// Human-readable status message.
|
|
pub status: String,
|
|
|
|
/// First error encountered (for diagnostics). Absent when all succeed.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub first_error: Option<String>,
|
|
}
|
|
|
|
/// Response from a LayeredConsensus query.
|
|
///
|
|
/// Provides per-tier resolution results plus an overall winner.
|
|
/// Use this to see "What does Tier 0 say? What does Tier 5 say?"
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
|
pub struct LayeredQueryResponse {
|
|
/// The subject that was queried.
|
|
pub subject: String,
|
|
|
|
/// The predicate that was queried.
|
|
pub predicate: String,
|
|
|
|
/// Per-tier consensus results, ordered by tier (0 = highest authority first).
|
|
/// Only tiers with at least one candidate are included.
|
|
pub tiers: Vec<TierResolutionDto>,
|
|
|
|
/// Overall winner: winner from the highest-authority tier that has candidates.
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub overall_winner: Option<AssertionResponse>,
|
|
|
|
/// Cross-tier disagreement score (0.0 = tiers agree, 1.0 = tiers disagree).
|
|
#[schema(minimum = 0.0, maximum = 1.0)]
|
|
pub overall_conflict_score: f32,
|
|
|
|
/// Total candidates considered across all tiers.
|
|
pub total_candidates: usize,
|
|
|
|
/// Unix timestamp when this view was computed.
|
|
pub computed_at: u64,
|
|
|
|
/// Which lens was used (always "LayeredConsensus").
|
|
pub lens_name: String,
|
|
}
|