kcode-k1-audio-classification-format 0.5.0

Binary formats for K1 audio-classification events and fragments
Documentation
use kcode_k1_audio_classification_format::{
    AudioClassificationEventV3, TranscriptionCompleteV1, TxId, encode_event,
};
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 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(),
        },
    }
}

#[test]
fn speaker_alignment_preserves_analysis_and_complete_event_bytes() {
    let current = current_analysis();
    let previous = previous_analysis();

    assert_eq!(
        postcard::to_allocvec(&current).expect("serialize current analysis"),
        postcard::to_allocvec(&previous).expect("serialize previous analysis")
    );

    let fragment_bytes = [7; 12];
    let current_event = encode_event(&AudioClassificationEventV3::TranscriptionComplete(
        TranscriptionCompleteV1 {
            fragment_id: TxId::from_bytes(fragment_bytes),
            analysis: current,
        },
    ))
    .expect("encode current event");

    let mut previous_event = vec![4, 2];
    previous_event.extend(
        postcard::to_allocvec(&PreviousTranscriptionCompleteV1 {
            fragment_id: fragment_bytes,
            analysis: previous,
        })
        .expect("encode previous event body"),
    );

    assert_eq!(current_event, previous_event);
}