#![forbid(unsafe_code)]
use kcode_audio_ingress::{ConfirmationState, RecordingState, RecordingStatus};
use serde_json::{Value, json};
use uuid::Uuid;
const ESTIMATED_CHARACTERS_PER_TOKEN: u64 = 4;
const INGRESS_CONTEXT_DIVISOR: u64 = 4;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Error {
InvalidInput(&'static str),
Conflict(&'static str),
Internal(&'static str),
}
impl std::fmt::Display for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match self {
Self::InvalidInput(message) | Self::Conflict(message) | Self::Internal(message) => {
message
}
};
formatter.write_str(message)
}
}
impl std::error::Error for Error {}
#[derive(Debug, Eq, PartialEq)]
pub struct TranscriptPlan {
pieces: Vec<TranscriptPiece>,
}
impl TranscriptPlan {
pub fn new(recording: &RecordingStatus, effective_context_tokens: u64) -> Result<Self, Error> {
let maximum_piece_characters = maximum_piece_characters(effective_context_tokens)?;
let RecordingState::Complete { transcript } = &recording.state else {
return Ok(Self { pieces: Vec::new() });
};
let texts = split_transcript(transcript, maximum_piece_characters)?;
let count = u32::try_from(texts.len())
.map_err(|_| Error::Internal("audio transcript contains too many pieces"))?;
let pieces = texts
.into_iter()
.enumerate()
.map(|(index, text)| {
let index = u32::try_from(index)
.map_err(|_| Error::Internal("audio transcript piece index exceeds u32"))?;
Ok(TranscriptPiece {
id: audio_piece_id(recording.id, index),
index,
count,
estimated_tokens: estimate_tokens(&text),
text,
})
})
.collect::<Result<_, Error>>()?;
Ok(Self { pieces })
}
pub fn pieces(&self) -> &[TranscriptPiece] {
&self.pieces
}
}
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct TranscriptPiece {
pub id: String,
pub index: u32,
pub count: u32,
pub text: String,
pub estimated_tokens: u64,
}
impl TranscriptPiece {
pub fn metadata(&self, recording: &RecordingStatus) -> Value {
json!({
"kind":"audio-transcript",
"recordingId":recording.id.to_string(),
"sha256":recording.sha256,
"originalFilename":recording.original_filename,
"extension":file_name_extension(&recording.original_filename),
"mimeType":"audio/wav",
"sizeBytes":recording.size_bytes,
"sourceCreatedAt":recording.recorded_at.to_rfc3339(),
"pieceIndex":self.index,
"pieceCount":self.count,
"speakerConfirmationState":recording.correction_packet.as_ref().map(|packet| match packet.confirmation_state {
ConfirmationState::Unconfirmed => "unconfirmed",
ConfirmationState::AutomaticallyTrained => "automatically_trained",
ConfirmationState::Confirmed => "confirmed",
}),
})
}
pub fn formatted_text(&self, recording: &RecordingStatus) -> Result<String, Error> {
let speaker_mapping = confirmed_speaker_mapping(recording)?;
Ok(format!(
"Vnote final transcript piece\n\nRecording began: {}\nRecording SHA-256: {}\nOriginal filename: {}\nExtension: {}\nMIME type: audio/wav\nSize: {} bytes\nTranscript piece: {} of {}{}\n\n{}",
recording.recorded_at.to_rfc3339(),
recording.sha256,
recording.original_filename,
file_name_extension(&recording.original_filename),
recording.size_bytes,
self.index + 1,
self.count,
speaker_mapping,
self.text,
))
}
}
pub fn parse_audio_piece_id(value: &str) -> Option<(Uuid, u32)> {
let mut components = value.split(':');
if components.next()? != "audio" {
return None;
}
let recording_id = Uuid::parse_str(components.next()?).ok()?;
let piece_index = components.next()?.parse::<u32>().ok()?;
if components.next().is_some() || audio_piece_id(recording_id, piece_index) != value {
return None;
}
Some((recording_id, piece_index))
}
fn audio_piece_id(recording_id: Uuid, piece_index: u32) -> String {
format!("audio:{recording_id}:{piece_index}")
}
fn maximum_piece_characters(effective_context_tokens: u64) -> Result<usize, Error> {
let piece_tokens = effective_context_tokens / INGRESS_CONTEXT_DIVISOR;
if piece_tokens == 0 {
return Err(Error::InvalidInput(
"effective ingress context must contain at least four tokens",
));
}
let characters = piece_tokens
.checked_mul(ESTIMATED_CHARACTERS_PER_TOKEN)
.ok_or(Error::InvalidInput(
"effective ingress context is too large",
))?;
usize::try_from(characters)
.map_err(|_| Error::InvalidInput("effective ingress context exceeds platform limits"))
}
fn split_transcript(
transcript: &str,
maximum_piece_characters: usize,
) -> Result<Vec<String>, Error> {
let mut remaining = transcript.trim();
if remaining.is_empty() {
return Err(Error::Internal("completed audio transcript is empty"));
}
let mut pieces = Vec::new();
while remaining.chars().count() > maximum_piece_characters {
let cutoff = remaining
.char_indices()
.nth(maximum_piece_characters)
.map(|(index, _)| index)
.unwrap_or(remaining.len());
let prefix = &remaining[..cutoff];
let minimum = prefix
.char_indices()
.nth(maximum_piece_characters / 2)
.map(|(index, _)| index)
.unwrap_or(0);
let boundary = prefix
.rfind("\n\n")
.filter(|index| *index >= minimum)
.or_else(|| prefix.rfind('\n').filter(|index| *index >= minimum))
.unwrap_or(cutoff);
let piece = remaining[..boundary].trim();
if piece.is_empty() {
return Err(Error::Internal("could not split audio transcript"));
}
pieces.push(piece.to_owned());
remaining = remaining[boundary..].trim();
}
if !remaining.is_empty() {
pieces.push(remaining.to_owned());
}
Ok(pieces)
}
fn estimate_tokens(value: &str) -> u64 {
(value.chars().count() as u64).div_ceil(ESTIMATED_CHARACTERS_PER_TOKEN)
}
fn confirmed_speaker_mapping(recording: &RecordingStatus) -> Result<String, Error> {
let Some(packet) = recording.correction_packet.as_ref() else {
return Ok(String::new());
};
if packet.confirmation_state != ConfirmationState::Confirmed {
return Err(Error::Conflict(
"audio speaker labels require exact human confirmation before ingress",
));
}
let mut lines = Vec::new();
for chunk in &packet.chunks {
for observation in &chunk.observations {
let confirmed = observation
.confirmed_full_name
.as_deref()
.ok_or(Error::Internal(
"confirmed audio packet omitted an observation label",
))?;
let local_label = serde_json::to_string(&observation.local_label)
.map_err(|_| Error::Internal("could not render confirmed audio speaker mapping"))?;
let confirmed = serde_json::to_string(confirmed)
.map_err(|_| Error::Internal("could not render confirmed audio speaker mapping"))?;
let candidate = observation
.identified_full_name
.as_deref()
.map(serde_json::to_string)
.transpose()
.map_err(|_| Error::Internal("could not render confirmed audio speaker mapping"))?
.unwrap_or_else(|| "null".into());
lines.push(format!(
"- chunk {}/{}, source {:.3}-{:.3}s: localLabel={}, confirmedFullName={}, classifierCandidate={}",
chunk.chunk_index + 1,
chunk.chunk_count,
chunk.audio_start_ms as f64 / 1_000.0,
chunk.audio_end_ms as f64 / 1_000.0,
local_label,
confirmed,
candidate,
));
}
}
if lines.is_empty() {
return Err(Error::Internal(
"confirmed audio packet contains no speaker observations",
));
}
Ok(format!(
"\n\nHuman-confirmed speaker-label data (authoritative; do not infer alternatives):\n{}",
lines.join("\n")
))
}
fn file_name_extension(file_name: &str) -> String {
file_name
.rsplit_once('.')
.and_then(|(stem, extension)| {
(!stem.is_empty() && !extension.is_empty()).then_some(extension)
})
.map(|extension| format!(".{extension}"))
.unwrap_or_else(|| "(none)".into())
}
#[cfg(test)]
mod tests {
use super::*;
use kcode_audio_ingress::{
CorrectionChunk, CorrectionObservation, CorrectionPacket, ObservationKey, ParsedChunk,
};
fn completed_recording(transcript: impl Into<String>) -> RecordingStatus {
let recorded_at = "2026-08-02T03:04:05Z".parse().unwrap();
RecordingStatus {
id: Uuid::parse_str("abcdef01-2345-6789-abcd-ef0123456789").unwrap(),
user_id: "user".into(),
sha256: "a".repeat(64),
original_filename: "meeting.final.WAV".into(),
size_bytes: 42,
recorded_at,
received_at: recorded_at,
transcription_model: "transcription-model".into(),
reconciliation_model: "reconciliation-model".into(),
reconciliation_reasoning: "xhigh".into(),
state: RecordingState::Complete {
transcript: transcript.into(),
},
correction_packet: None,
}
}
fn with_speaker_packet(
mut recording: RecordingStatus,
state: ConfirmationState,
confirmed_name: Option<&str>,
) -> RecordingStatus {
recording.correction_packet = Some(CorrectionPacket {
recording_id: recording.id,
user_id: recording.user_id.clone(),
sha256: recording.sha256.clone(),
original_filename: recording.original_filename.clone(),
size_bytes: recording.size_bytes,
recorded_at: recording.recorded_at,
clean: true,
chunk_count: 1,
chunks: vec![CorrectionChunk {
chunk_index: 0,
chunk_count: 1,
audio_start_ms: 0,
audio_end_ms: 1_500,
raw_gemini_response: "raw".into(),
parsed: ParsedChunk {
utterances: Vec::new(),
notes: Vec::new(),
clip_valid: true,
clip_validity_reason: None,
speakers: Vec::new(),
},
observations: vec![CorrectionObservation {
local_label: "Speaker A".into(),
speaker_ordinal: 0,
observation_key: ObservationKey {
object_id: format!(
"kcode-audio-ingress/recording/{}/chunk/0",
recording.id
),
piece_index: 0,
},
candidate: None,
identified_full_name: Some("Classifier Candidate".into()),
confirmed_full_name: confirmed_name.map(str::to_owned),
}],
clean: true,
}],
confirmation_state: state,
});
recording
}
#[test]
fn quarter_context_plans_transcript_only_pieces() {
let recording = completed_recording("a".repeat(801));
let plan = TranscriptPlan::new(&recording, 400).unwrap();
assert_eq!(plan.pieces().len(), 3);
assert_eq!(
plan.pieces()
.iter()
.map(|piece| piece.text.chars().count())
.collect::<Vec<_>>(),
vec![400, 400, 1]
);
assert_eq!(
plan.pieces()
.iter()
.map(|piece| piece.estimated_tokens)
.collect::<Vec<_>>(),
vec![100, 100, 1]
);
assert_eq!(plan.pieces()[2].id, format!("audio:{}:2", recording.id));
}
#[test]
fn splitting_prefers_late_paragraphs_and_counts_unicode_scalars() {
let paragraph_recording =
completed_recording(format!("{}\n\n{}", "a".repeat(250), "b".repeat(200)));
let paragraph_plan = TranscriptPlan::new(¶graph_recording, 400).unwrap();
assert_eq!(paragraph_plan.pieces()[0].text, "a".repeat(250));
assert_eq!(paragraph_plan.pieces()[1].text, "b".repeat(200));
let unicode_recording = completed_recording("😀".repeat(5));
let unicode_plan = TranscriptPlan::new(&unicode_recording, 4).unwrap();
assert_eq!(
unicode_plan
.pieces()
.iter()
.map(|piece| piece.text.chars().count())
.collect::<Vec<_>>(),
vec![4, 1]
);
}
#[test]
fn canonical_audio_piece_ids_are_strict() {
let recording_id = Uuid::parse_str("abcdef01-2345-6789-abcd-ef0123456789").unwrap();
let canonical = format!("audio:{recording_id}:2");
assert_eq!(parse_audio_piece_id(&canonical), Some((recording_id, 2)));
assert!(parse_audio_piece_id(&format!("audio:{recording_id}:02")).is_none());
assert!(parse_audio_piece_id("audio:ABCDEF01-2345-6789-ABCD-EF0123456789:2").is_none());
assert!(parse_audio_piece_id(&format!("audio:{recording_id}:2:extra")).is_none());
assert!(parse_audio_piece_id("audio:not-a-uuid:0").is_none());
}
#[test]
fn metadata_reproduces_existing_keys_and_values() {
let recording = completed_recording("Transcript");
let plan = TranscriptPlan::new(&recording, 400).unwrap();
assert_eq!(
plan.pieces()[0].metadata(&recording),
json!({
"kind": "audio-transcript",
"recordingId": "abcdef01-2345-6789-abcd-ef0123456789",
"sha256": "a".repeat(64),
"originalFilename": "meeting.final.WAV",
"extension": ".WAV",
"mimeType": "audio/wav",
"sizeBytes": 42,
"sourceCreatedAt": "2026-08-02T03:04:05+00:00",
"pieceIndex": 0,
"pieceCount": 1,
"speakerConfirmationState": null,
})
);
}
#[test]
fn legacy_rendering_reproduces_existing_text() {
let recording = completed_recording("Transcript");
let plan = TranscriptPlan::new(&recording, 400).unwrap();
assert_eq!(
plan.pieces()[0].formatted_text(&recording).unwrap(),
format!(
"Vnote final transcript piece\n\nRecording began: 2026-08-02T03:04:05+00:00\nRecording SHA-256: {}\nOriginal filename: meeting.final.WAV\nExtension: .WAV\nMIME type: audio/wav\nSize: 42 bytes\nTranscript piece: 1 of 1\n\nTranscript",
"a".repeat(64)
)
);
}
#[test]
fn confirmed_rendering_includes_the_complete_mapping() {
let recording = with_speaker_packet(
completed_recording("Transcript"),
ConfirmationState::Confirmed,
Some("Human Choice"),
);
let plan = TranscriptPlan::new(&recording, 400).unwrap();
assert_eq!(
plan.pieces()[0].formatted_text(&recording).unwrap(),
format!(
"Vnote final transcript piece\n\nRecording began: 2026-08-02T03:04:05+00:00\nRecording SHA-256: {}\nOriginal filename: meeting.final.WAV\nExtension: .WAV\nMIME type: audio/wav\nSize: 42 bytes\nTranscript piece: 1 of 1\n\nHuman-confirmed speaker-label data (authoritative; do not infer alternatives):\n- chunk 1/1, source 0.000-1.500s: localLabel=\"Speaker A\", confirmedFullName=\"Human Choice\", classifierCandidate=\"Classifier Candidate\"\n\nTranscript",
"a".repeat(64)
)
);
assert_eq!(
plan.pieces()[0]
.metadata(&recording)
.get("speakerConfirmationState"),
Some(&json!("confirmed"))
);
}
#[test]
fn unconfirmed_speaker_rendering_is_a_conflict() {
let recording = with_speaker_packet(
completed_recording("Transcript"),
ConfirmationState::AutomaticallyTrained,
None,
);
let plan = TranscriptPlan::new(&recording, 400).unwrap();
assert!(matches!(
plan.pieces()[0].formatted_text(&recording),
Err(Error::Conflict(_))
));
assert_eq!(
plan.pieces()[0]
.metadata(&recording)
.get("speakerConfirmationState"),
Some(&json!("automatically_trained"))
);
}
}