kcode_speech_classification/
protocol.rs1use 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}