feat(audio-preprocessing): agent-ready mono-conversion + segment concatenation#2172
feat(audio-preprocessing): agent-ready mono-conversion + segment concatenation#2172shubhamNvidia wants to merge 3 commits into
Conversation
…idency resolver Shared base imported by the audio stage modules to make them agent-ready: - _agent_ready.py: AgentReady mixin, StageContract/IOSpec/Gates/Role - _residency.py: input residency resolver (file/waveform/auto) - common.py: agent-ready helpers (ensure_mono/ensure_waveform_2d) Excludes the agent orchestration layer (registry/catalog/conformance/planning/agent).
| def outputs(self) -> tuple[list[str], list[str]]: | ||
| return [], ["waveform", "sample_rate", "is_mono", "duration", "num_samples"] | ||
|
|
||
| def process(self, task: AudioTask) -> AudioTask | list[AudioTask]: | ||
| outputs = [ | ||
| self.waveform_key, | ||
| self.sample_rate_key, | ||
| self.is_mono_key, | ||
| self.duration_key, | ||
| self.num_samples_key, | ||
| ] | ||
| if self.write_to_disk: | ||
| outputs.append(self.output_audio_filepath_key) | ||
| if self.update_audio_filepath: | ||
| outputs.append(self.audio_filepath_key) | ||
| return [], outputs |
There was a problem hiding this comment.
outputs() always declares waveform_key/sample_rate_key regardless of keep_waveform_in_task
outputs() unconditionally includes waveform_key and sample_rate_key in its output list, but process() only writes those keys to task.data when keep_waveform_in_task=True. If the pipeline framework uses outputs() to validate that all declared outputs are present in the resulting task, it will raise a validation error every time keep_waveform_in_task=False, making that flag effectively broken. The describe() method handles this correctly — outputs() needs the same guard.
| def outputs(self) -> tuple[list[str], list[str]]: | |
| return [], ["waveform", "sample_rate", "is_mono", "duration", "num_samples"] | |
| def process(self, task: AudioTask) -> AudioTask | list[AudioTask]: | |
| outputs = [ | |
| self.waveform_key, | |
| self.sample_rate_key, | |
| self.is_mono_key, | |
| self.duration_key, | |
| self.num_samples_key, | |
| ] | |
| if self.write_to_disk: | |
| outputs.append(self.output_audio_filepath_key) | |
| if self.update_audio_filepath: | |
| outputs.append(self.audio_filepath_key) | |
| return [], outputs | |
| def outputs(self) -> tuple[list[str], list[str]]: | |
| outputs = [ | |
| self.is_mono_key, | |
| self.duration_key, | |
| self.num_samples_key, | |
| ] | |
| if self.keep_waveform_in_task: | |
| outputs.extend([self.waveform_key, self.sample_rate_key]) | |
| if self.write_to_disk: |
| write_to_disk: bool = False | ||
| update_audio_filepath: bool = False | ||
| output_dir: str | None = None |
There was a problem hiding this comment.
update_audio_filepath=True silently has no effect when write_to_disk=False
Setting update_audio_filepath=True without write_to_disk=True produces no observable change — the flag only takes effect inside the if self.write_to_disk: branch. A user who sets update_audio_filepath=True expecting the audio_filepath_key to point to the (in-memory) converted audio would see no update and no warning. Consider raising a ValueError in __post_init__ when update_audio_filepath=True and write_to_disk=False, so the misconfiguration surfaces at construction time rather than silently at runtime.
… test Demonstrates _agent_ready + _residency working together end to end: - MonoConversionStage inherits AgentReady and declares a StageContract (reads/writes, accepts=[file, waveform], gates, configurable *_key fields). - process() resolves audio from a file OR an in-memory waveform via _residency. - test covers both input forms, per-item dispatch, and temp-WAV materialize- then-cleanup, plus a minimal contract-consumer + pseudocode standing in for the registry/orchestrator (which fills params/key_roles/dispatch).
…cribe + segment_mappings)
c127b81 to
b3d8537
Compare
Summary
Makes the preprocessing stages agent-ready and lets mono-conversion accept in-memory audio.
preprocessing/mono_conversion.pywaveform+sample_rateor anaudio_filepath(input_residency= file / waveform / auto).write_to_disk(write the mono WAV to a temp file) andupdate_audio_filepath(repointaudio_filepath, preserving the original underoriginal_audio_filepath). Always annotatesis_mono,duration,num_samples.describe()/StageContract.preprocessing/concatenation.py(SegmentConcatenationStage) — N:1segmentsinto one waveform and emitssegment_mappingsmetadata: for each segment, its[concat_start_ms, concat_end_ms]position in the joined audio and the corresponding[original_start_ms, original_end_ms]. This is what lets the timestamp mapper translate results back to the original timeline.Notes for reviewers
write_to_disk=False,update_audio_filepath=False→ same in-memory result as before)."auto"prefers an in-memorywaveformover re-reading the file when both are present; passinput_residency="file"to force a reload.segment_mappingsconstruction (ms math, inter-segment offsets) inconcatenation.py.Test plan
pytest tests/stages/audio/preprocessing -q