Skip to main content

ant_types/
evidence.rs

1//! Antares-native first-class evidence.
2//!
3//! Evidence is a kernel concept, not a user-defined entity. Every fact
4//! (Edge) may reference one or more Evidence records via `evidenced_by`,
5//! and derived records may accumulate evidence from their causes.
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10use crate::author::AuthorStamp;
11use crate::ids::{ProjectId, TenantId};
12
13/// Evidence identifier (newtype over String). Distinct from VertexId
14/// because evidence lives in its own storage plane.
15#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct EvidenceId(pub String);
18
19/// A source-bound piece of supporting material: where it came from,
20/// the literal content, and optional span offsets into the source.
21/// The payload of an `evidence` record.
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct Evidence {
24    /// Evidence id, unique within the scope.
25    pub id: EvidenceId,
26    /// Owning tenant.
27    pub tenant_id: TenantId,
28    /// Owning project.
29    pub project_id: ProjectId,
30    /// URI/path of the source artifact (e.g. "s3://antares/calls/2026-04-29.vtt"
31    /// or "antares://transcripts/meeting_001"). Required.
32    pub source_uri: String,
33    /// What kind of source: "transcript" | "email_event" | "crm_field" | ...
34    pub source_type: String,
35    /// Identifier of the source event/artifact (e.g. "meeting_001",
36    /// "email_001"). Required.
37    pub source_id: String,
38    /// The literal text/data the evidence points at. Required.
39    pub content: String,
40    /// Character offset of the span start in the source, inclusive.
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub char_start: Option<u32>,
43    /// Character offset of the span end in the source, exclusive.
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub char_end: Option<u32>,
46    /// Byte offset of the span start in the source, inclusive.
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub byte_start: Option<u64>,
49    /// Byte offset of the span end in the source, exclusive.
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub byte_end: Option<u64>,
52    /// Wall-clock time the source event happened or was seen.
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub observed_at: Option<DateTime<Utc>>,
55    /// Wall-clock time the extractor produced this record.
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub extracted_at: Option<DateTime<Utc>>,
58    /// Version tag of the producing extractor, verbatim.
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub extractor_version: Option<String>,
61    /// Confidence in `[0,1]`. `None` = treated as 1.0.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub confidence: Option<f32>,
64    /// Free-form metadata. Use sparingly — first-class fields above
65    /// are preferred.
66    #[serde(default, skip_serializing_if = "serde_json::Value::is_null")]
67    pub metadata: serde_json::Value,
68
69    /// Which user persisted this evidence. `None` for older
70    /// records and for anonymous calls.
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub author: Option<AuthorStamp>,
73}
74
75impl Evidence {
76    /// Shorthand constructor for tests/fixtures.
77    pub fn quick(
78        id: &str,
79        tenant: TenantId,
80        project: ProjectId,
81        source_type: &str,
82        source_id: &str,
83        content: &str,
84    ) -> Self {
85        Self {
86            id: EvidenceId(id.to_string()),
87            tenant_id: tenant,
88            project_id: project,
89            source_uri: format!("antares://{source_type}/{source_id}"),
90            source_type: source_type.to_string(),
91            source_id: source_id.to_string(),
92            content: content.to_string(),
93            char_start: None,
94            char_end: None,
95            byte_start: None,
96            byte_end: None,
97            observed_at: None,
98            extracted_at: None,
99            extractor_version: None,
100            confidence: None,
101            metadata: serde_json::Value::Null,
102            author: None,
103        }
104    }
105}