Skip to main content

kcode_speech_classification/
protocol.rs

1use crate::model::{
2    Cohort, DeleteOutcome, FeatureRow, IdentifyOutcome, ObservationKey, TrainOutcome,
3};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
7#[serde(tag = "operation", rename_all = "snake_case")]
8pub enum ProtocolRequest {
9    Identify {
10        key: ObservationKey,
11        cohort: Cohort,
12        row: FeatureRow,
13        threshold: f64,
14    },
15    Train {
16        key: ObservationKey,
17        cohort: Cohort,
18        row: FeatureRow,
19        speaker_id: String,
20    },
21    Delete {
22        key: ObservationKey,
23    },
24}
25
26#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
27#[serde(tag = "operation", content = "outcome", rename_all = "snake_case")]
28pub enum ProtocolResult {
29    Identify(IdentifyOutcome),
30    Train(TrainOutcome),
31    Delete(DeleteOutcome),
32}
33
34#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
35pub struct ProtocolError {
36    pub code: String,
37    pub message: String,
38}
39
40#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
41#[serde(tag = "status", rename_all = "snake_case")]
42pub enum ProtocolResponse {
43    Success { result: ProtocolResult },
44    Error { error: ProtocolError },
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::model::tests::{sample_cohort, sample_row};
51    use crate::model::{CandidateEvidence, IdentifyEvidence};
52
53    fn key() -> ObservationKey {
54        ObservationKey {
55            object_id: "object-17".to_owned(),
56            piece_index: 3,
57        }
58    }
59
60    #[test]
61    fn every_request_round_trips() {
62        let requests = [
63            ProtocolRequest::Identify {
64                key: key(),
65                cohort: sample_cohort(),
66                row: sample_row(),
67                threshold: 2.5,
68            },
69            ProtocolRequest::Train {
70                key: key(),
71                cohort: sample_cohort(),
72                row: sample_row(),
73                speaker_id: "speaker-a".to_owned(),
74            },
75            ProtocolRequest::Delete { key: key() },
76        ];
77
78        for request in requests {
79            let json = serde_json::to_string(&request).unwrap();
80            let decoded: ProtocolRequest = serde_json::from_str(&json).unwrap();
81            assert_eq!(decoded, request);
82        }
83    }
84
85    #[test]
86    fn success_and_error_responses_round_trip() {
87        let evidence = IdentifyEvidence {
88            best: CandidateEvidence {
89                speaker_id: "speaker-a".to_owned(),
90                cost: 4.0,
91            },
92            runner_up: Some(CandidateEvidence {
93                speaker_id: "speaker-b".to_owned(),
94                cost: 7.0,
95            }),
96            background_population_cost: 8.0,
97            absolute_gap: 4.0,
98            runner_up_gap: Some(3.0),
99            confidence_score: 3.0,
100        };
101        let responses = [
102            ProtocolResponse::Success {
103                result: ProtocolResult::Identify(IdentifyOutcome {
104                    speaker_id: Some("speaker-a".to_owned()),
105                    evidence: Some(evidence),
106                }),
107            },
108            ProtocolResponse::Success {
109                result: ProtocolResult::Train(TrainOutcome::Corrected),
110            },
111            ProtocolResponse::Success {
112                result: ProtocolResult::Delete(DeleteOutcome::NotFound),
113            },
114            ProtocolResponse::Error {
115                error: ProtocolError {
116                    code: "validation".to_owned(),
117                    message: "bad input".to_owned(),
118                },
119            },
120        ];
121
122        for response in responses {
123            let json = serde_json::to_string(&response).unwrap();
124            let decoded: ProtocolResponse = serde_json::from_str(&json).unwrap();
125            assert_eq!(decoded, response);
126        }
127    }
128
129    #[test]
130    fn request_rejects_an_unknown_operation() {
131        let json = r#"{"operation":"export"}"#;
132        assert!(serde_json::from_str::<ProtocolRequest>(json).is_err());
133    }
134}