use kcode_k1_audio_classification_format as facade;
use kcode_speaker_v3_analysis as current;
use kcode_speaker_v3_analysis_0_1 as previous;
use serde::Serialize;
#[derive(Serialize)]
struct PreviousTranscriptionCompleteV1 {
fragment_id: [u8; 12],
analysis: previous::ExecutedAnalysis,
}
fn txid(seed: u8) -> facade::TxId {
let mut bytes = [0; 12];
for (index, value) in bytes.iter_mut().enumerate() {
*value = seed.wrapping_add(index as u8);
}
facade::TxId::from_bytes(bytes)
}
fn current_analysis() -> current::ExecutedAnalysis {
let mut ogg = vec![0; 29];
ogg[..4].copy_from_slice(b"OggS");
ogg[26] = 1;
ogg[27] = 1;
current::ExecutedAnalysis {
envelope: current::AnalysisEnvelope {
audio: current::OggAudioMetadata::from_bytes(&ogg, 1_250, Some("sample.ogg".into()))
.expect("current Ogg metadata"),
analysis: current::StructuredAnalysis {
transcript: "Speaker 1: hello".into(),
speakers: vec![current::StructuredSpeaker {
speaker: current::LocalSpeakerLabel::new(1).expect("current speaker label"),
language: "English".into(),
features: current::FeatureVector24::default(),
features_usable_for_training: true,
}],
},
gemini: current::GeminiCohort {
model_id: "gemini".into(),
transcript_prompt_revision: "transcript-r1".into(),
feature_prompt_revisions: [
"feature-r1".into(),
"feature-r2".into(),
"feature-r3".into(),
],
feature_schema_revision: "schema-r1".into(),
},
structurer: current::StructurerProvenance {
model_id: "terra".into(),
prompt_revision: "structuring-r1".into(),
},
},
label_extractor: current::StructurerProvenance {
model_id: "terra".into(),
prompt_revision: "labels-r1".into(),
},
}
}
fn previous_analysis() -> previous::ExecutedAnalysis {
let mut ogg = vec![0; 29];
ogg[..4].copy_from_slice(b"OggS");
ogg[26] = 1;
ogg[27] = 1;
previous::ExecutedAnalysis {
envelope: previous::AnalysisEnvelope {
audio: previous::OggAudioMetadata::from_bytes(&ogg, 1_250, Some("sample.ogg".into()))
.expect("previous Ogg metadata"),
analysis: previous::StructuredAnalysis {
transcript: "Speaker 1: hello".into(),
speakers: vec![previous::StructuredSpeaker {
speaker: previous::LocalSpeakerLabel::new(1).expect("previous speaker label"),
language: "English".into(),
features: previous::FeatureVector24::default(),
features_usable_for_training: true,
}],
},
gemini: previous::GeminiCohort {
model_id: "gemini".into(),
transcript_prompt_revision: "transcript-r1".into(),
feature_prompt_revisions: [
"feature-r1".into(),
"feature-r2".into(),
"feature-r3".into(),
],
feature_schema_revision: "schema-r1".into(),
},
structurer: previous::StructurerProvenance {
model_id: "terra".into(),
prompt_revision: "structuring-r1".into(),
},
},
label_extractor: previous::StructurerProvenance {
model_id: "terra".into(),
prompt_revision: "labels-r1".into(),
},
}
}
fn legacy_event<T: Serialize>(tag: u8, value: &T) -> Vec<u8> {
let mut bytes = vec![4, tag];
bytes.extend(postcard::to_allocvec(value).expect("serialize legacy event body"));
bytes
}
fn assert_event<T: Serialize>(tag: u8, event: facade::AudioClassificationEventV3, value: &T) {
let bytes = legacy_event(tag, value);
assert_eq!(facade::encode_event(&event), Ok(bytes.clone()));
assert_eq!(facade::decode_event(&bytes), Ok(event));
}
#[test]
fn all_public_paths_compile_and_event_bytes_remain_frozen() {
let speaker = facade::LocalSpeakerLabel::new(1).expect("speaker label");
let queue = facade::QueueV2 {
audio_object_id: txid(1),
};
assert_event(
1,
facade::AudioClassificationEventV3::Queue(queue.clone()),
&queue,
);
let updates = [
facade::ProgressUpdateV1::LlmJobStarted {
sequence: 1,
stage: facade::FragmentStageV1::Transcript,
name: "transcribe".into(),
},
facade::ProgressUpdateV1::LlmJobSucceeded { sequence: 2 },
facade::ProgressUpdateV1::LlmJobFailed {
sequence: 3,
error: "failed".into(),
},
facade::ProgressUpdateV1::StageCompleted {
stage: facade::FragmentStageV1::Structuring,
},
];
for update in updates {
let progress = facade::ProgressV1 {
fragment_id: txid(2),
update,
};
assert_event(
6,
facade::AudioClassificationEventV3::Progress(progress.clone()),
&progress,
);
}
let transcription = facade::TranscriptionCompleteV1 {
fragment_id: txid(3),
analysis: current_analysis(),
};
assert_event(
2,
facade::AudioClassificationEventV3::TranscriptionComplete(transcription.clone()),
&transcription,
);
let failed = facade::FailedV2 {
fragment_id: txid(4),
stage: facade::FragmentStageV1::SpeakerFeatures,
llm_job_sequence: Some(7),
error: "failure".into(),
};
assert_event(
3,
facade::AudioClassificationEventV3::Failed(failed.clone()),
&failed,
);
let discarded = facade::DiscardedV2 {
fragment_id: txid(5),
};
assert_event(
4,
facade::AudioClassificationEventV3::Discarded(discarded.clone()),
&discarded,
);
let confirmation = facade::LabelConfirmationV1 {
fragment_id: txid(6),
interim_txid: txid(7),
speakers: vec![facade::SpeakerLabelV1 {
speaker,
person_id: "person-1".into(),
}],
};
assert_event(
5,
facade::AudioClassificationEventV3::LabelConfirmation(confirmation.clone()),
&confirmation,
);
let _ = [
facade::FragmentStageV1::Queue,
facade::FragmentStageV1::Transcript,
facade::FragmentStageV1::SpeakerLabels,
facade::FragmentStageV1::SpeakerFeatures,
facade::FragmentStageV1::Structuring,
facade::FragmentStageV1::LabelConfirmation,
];
let _ = facade::FormatError::InvalidTxIdSlotLength(facade::TxIdSlot::LEN);
}
#[test]
fn speaker_versions_preserve_analysis_and_transcription_complete_bytes() {
let current = current_analysis();
let previous = previous_analysis();
assert_eq!(
postcard::to_allocvec(¤t).expect("serialize current analysis"),
postcard::to_allocvec(&previous).expect("serialize previous analysis")
);
let fragment_bytes = [7; 12];
let current_event =
facade::encode_event(&facade::AudioClassificationEventV3::TranscriptionComplete(
facade::TranscriptionCompleteV1 {
fragment_id: facade::TxId::from_bytes(fragment_bytes),
analysis: current,
},
))
.expect("encode current event");
let previous_event = legacy_event(
2,
&PreviousTranscriptionCompleteV1 {
fragment_id: fragment_bytes,
analysis: previous,
},
);
assert_eq!(current_event, previous_event);
}
#[test]
fn representative_fragment_and_path_bytes_remain_compatible() {
let features = facade::FeatureVector24::default();
let staged = facade::StagedFragmentV1 {
analysis_txid: txid(20),
transcript: "hello".into(),
speakers: vec![facade::StagedSpeakerV1 {
speaker: facade::LocalSpeakerLabel::new(1).expect("speaker label"),
language: "English".into(),
features: features.clone(),
usable_for_training: true,
}],
};
let staged_bytes = facade::encode_staged_fragment(&staged).expect("encode staged");
assert_eq!(
staged_bytes[..16],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(facade::decode_staged_fragment(&staged_bytes), Ok(staged));
let final_value = facade::FinalFragmentV1 {
analysis_txid: txid(30),
confirmation_txid: txid(40),
transcript: "hello".into(),
speakers: vec![facade::FinalSpeakerV1 {
speaker: facade::LocalSpeakerLabel::new(1).expect("speaker label"),
person_id: "person-1".into(),
language: "English".into(),
features,
usable_for_training: false,
}],
};
let final_bytes = facade::encode_final_fragment(&final_value).expect("encode final");
assert_eq!(
final_bytes[..16],
[1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(facade::decode_final_fragment(&final_bytes), Ok(final_value));
let slot = facade::TxIdSlot::new(txid(50));
assert_eq!(facade::TxIdSlot::decode(&slot.encode()), Ok(slot));
assert_eq!(
facade::txid_from_path(facade::txid_path(txid(60))),
Ok(txid(60))
);
}