File size: 3,158 Bytes
30f011f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! types.rs — shared types for the inference daemon

use serde::{Deserialize, Serialize};
use tokio::sync::oneshot;

/// A single (claim, chunk) verification request.
/// `responder` routes the result back to the caller without polling.
pub struct VerifyRequest {
    pub input_ids:      Vec<i64>,
    pub attention_mask: Vec<i64>,
    pub token_type_ids: Vec<i64>,
    pub chunk_id:       String,   // canonical ID of the retrieved source chunk
    pub claim_text:     String,   // exact LLM-generated claim string
    pub responder:      oneshot::Sender<VerifyResponse>,
}

/// Response returned through the oneshot channel.
#[derive(Debug, Clone)]
pub struct VerifyResponse {
    pub entailment_score: f32,        // softmax P(Entailment)
    pub label:            Verdict,
    pub attestation_hash: [u8; 32],   // BLAKE3 hash of the serialised attestation
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Verdict {
    Entailment,
    Neutral,
    Contradiction,
}

impl Verdict {
    pub fn from_score(score: f32, threshold: f32) -> Self {
        if score >= threshold {
            Verdict::Entailment
        } else {
            Verdict::Contradiction   // scores below threshold are rejected
        }
    }
}

/// Deterministically serialisable attestation record.
/// bincode serialises this to a canonical byte string before BLAKE3 hashing.
#[derive(Debug, Serialize, Deserialize)]
pub struct EntailmentAttestation {
    pub timestamp_ns:      u64,
    pub chunk_id:          String,
    pub claim_text:        String,
    pub entailment_score:  f32,
    pub verdict:           String,
    pub model_signature:   String,   // e.g. "deberta-v3-fp16-v1.2"
    pub threshold:         f32,
}

impl EntailmentAttestation {
    /// Serialise deterministically with bincode and compute BLAKE3 hash.
    pub fn seal(&self) -> ([u8; 32], Vec<u8>) {
        let payload = bincode::serialize(self)
            .expect("bincode serialisation is infallible for flat structs");
        let hash: [u8; 32] = *blake3::hash(&payload).as_bytes();
        (hash, payload)
    }
}

/// Daemon configuration loaded from config/daemon.json.
#[derive(Debug, Clone, Deserialize)]
pub struct DaemonConfig {
    pub model_path:       String,
    pub trt_cache_dir:    String,
    pub threshold:        f32,       // entailment rejection threshold from calibrate.py
    pub max_batch_size:   usize,
    pub flush_interval_ms: u64,
    pub model_signature:  String,
    pub http_port:        u16,
    pub ledger_path:      String,    // WORM ledger file path
}

impl Default for DaemonConfig {
    fn default() -> Self {
        Self {
            model_path:        "./onnx/cross_encoder_opt_fp16.onnx".into(),
            trt_cache_dir:     "./trt_cache".into(),
            threshold:         0.85,
            max_batch_size:    32,
            flush_interval_ms: 5,
            model_signature:   "deberta-v3-fp16-v1.2".into(),
            http_port:         8080,
            ledger_path:       "./ledger/audit_chain.db".into(),
        }
    }
}