feat(audio): agent-ready stages + the audio curation agent (full branch, draft) - #2343
Draft
shubhamNvidia wants to merge 6 commits into
Draft
feat(audio): agent-ready stages + the audio curation agent (full branch, draft)#2343shubhamNvidia wants to merge 6 commits into
shubhamNvidia wants to merge 6 commits into
Conversation
The complete audio agent work as one branch: the same stage-side contracts split across PRs NVIDIA-NeMo#2332-NVIDIA-NeMo#2342, plus the agent that consumes them. Stages (nemo_curator/stages/audio/): - the _agent/ contract layer (AgentReady, StageContract, roles, input residency, discovery, planning, conformance) and the public agent.py entry point - describe() contracts and *_key params across common, preprocessing, io, filtering, metrics, segmentation, tagging, inference, alm, datasets and advanced_pipelines - new stages: ChannelCount, SampleRateFilter, CreateInitialManifestAudioFolder, PreserveByValueConditions, ManifestCheckpoint, ManifestGroupExport, DocumentBatchJsonlWriter Agent (nemo_curator/audio_agent/): - deterministic verbs (discover/describe/validate/smoke/run/report/reuse/delta), knowledge cards, recipes, and the host skills Supporting: resource telemetry used by smoke calibration (utils/performance_utils.py, opt-in via RESOURCE_PROBE in backends/base.py), the nemo-curator-audio console script, and the audio_agent CI matrix entry. Verified: 51/51 stages pass static conformance, the stage-compatibility baseline hash is unchanged, and whole-repo `ruff check` is clean. Signed-off-by: Shubham Bhawsar <shbhawsar@nvidia.com>
(cherry picked from commit 908e476be50f3ab68a6d98349677522130461d55)
``_fix_for`` mapped ``composite`` but not ``composite_unrunnable``, so the one issue the agent raises as a hard error arrived with no fix text at all -- the caller was told a stage cannot run and nothing about what to do instead. The two codes want opposite advice. ``composite`` means the data flow is unknown; this means the executor rejects the composite's SHAPE, so no configuration of it will run and the recipe has to be rewritten with the children at the top level. Signed-off-by: Shubham Bhawsar <shbhawsar@nvidia.com>
Each of these validated clean and then produced, or consumed, the wrong thing at runtime. Landing together because they share the regression module and two of them meet in the planner's walk. 1. A disk-only conversion left the pre-conversion waveform on the row. ``keep_waveform_in_task=False`` only stopped MonoConversionStage and ChannelCountStage from ASSIGNING the converted tensor; it never removed the one already there. With ``input_residency="waveform", write_to_disk=True`` -- an advertised combination -- both wrote a correct mono WAV, set is_mono=True / num_channels=1, and returned the original stereo tensor, which the next stage at ``input_residency="auto"`` prefers. Both constructors also accepted ``keep_waveform_in_task=False, write_to_disk=False``, which converts into nowhere. Now: both sinkless shapes are rejected in __post_init__ (update_audio_filepath implies write_to_disk, since without a file the repoint is dropped), the keys are removed after a successful write -- after, so a write that raises leaves the row as it arrived -- and declared in removes_keys. 2. validate_pipeline never checked task types. ``_agent_ready.py`` documents accepts_task_type/produces_task_type as enabling the check and nothing read them, so a folder source (EmptyTask -> AudioTask) feeding ManifestReaderStage (FileGroupTask -> AudioTask) returned ok=True and then raised FileNotFoundError. The walk now carries the produced type, validate_pipeline takes an initial_task_type, and an incompatible handover is an error: exact name, a union satisfied by any member, or a declared base accepting its subclasses. Only a genuinely unknown type breaks the chain -- an unparametrized generic, a contract that will not build, or an unreadable composite child. Proximity to a composite does not: a composite hides its inner writes, not the generic it is declared over, and treating it as unknown switched the check off for every recipe that begins at ManifestReader. 3. preserves_upstream_keys was declared and then ignored. SegmentConcatenationStage rebuilds task.data -- segments and every unrelated column gone -- while inheriting preserves_upstream_keys=True, and _advance() consulted the field for no stage at all, so the four that already declared False were modelled as preserving everything. Concatenation followed by a filter reading upstream text validated ok=True and raised ValueError on the missing key. Keys, roles and producers now clear before the stage's own writes fold in. Tensor residency deliberately does not: ALMDataBuilderStage sets the flag for one rebuilding branch while ordinarily still carrying the waveform, and clearing residency there retracts the tensor_into_sink block on a pipeline that really does hand a resident waveform to a JSON sink. Every reproduction in the review is a regression in tests/stages/audio/_agent/test_agent_foundation_regressions.py. Signed-off-by: Shubham Bhawsar <shbhawsar@nvidia.com>
validate_pipeline now performs this check itself, so keeping the agent's copy reported the same problem twice for one recipe -- and the foundation's copy arrived with no fix text, because _fix_for had no entry for the code it emits. The remaining duplicate was also the weaker of the two: it compared type names for exact equality, where the foundation accepts a union member and a declared base. Removes the check and gives task_type_mismatch a remediation hint that keeps the DocumentBatch-into-an-AudioTask-sink advice the removed version carried. The contracts docstring no longer offers task_type_mismatch as its example of something ok=True can coexist with: a task-type mismatch is a data-flow fact, so it now lands in ok as an error rather than as a card violation. Signed-off-by: Shubham Bhawsar <shbhawsar@nvidia.com>
The lock has been failing since ManifestWriterStage gained AGENT_STATIC. That commit is the whole diff: its static gates go from the all-default view -- writes_to_disk=False, no output path, no lifecycle side effect, no serialization constraint -- to the four its configured contract has always reported, plus per_row_independent. The other 45 stages in the payload are byte-identical, and no constructor default moved. Verified rather than assumed: the payload was dumped at e89d0e7 (where this SHA was set) and at HEAD and diffed field by field; exactly one entry differs, and only in ``gates``. Dumped again at the commit before the current round of fixes to confirm those three are invisible to this surface -- they change configured contracts (removes_keys, preserves_upstream_keys) and add constructor validation, neither of which this baseline hashes. The change the lock caught is the intended one: static discovery cannot instantiate a stage whose output_path is required, so an agent reading the static view saw a destructive JSON sink as a pure stage. Signed-off-by: Shubham Bhawsar <shbhawsar@nvidia.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — for anyone who wants to try the audio agent end to end. Not intended to merge as-is.
This is the complete audio-agent work in one branch: the same stage-side contracts that are up
for review as #2332–#2342, plus the agent that consumes them. It exists so the agent can be
run and evaluated without waiting for eleven PRs to land — review and merge should still happen
through those, starting with #2332 (Foundation).
What's here
Stages (
nemo_curator/stages/audio/) — identical content to the 11 PRs:_agent/contract layer:AgentReady,StageContract, semantic roles, input residency,discovery, planning and the conformance harness, behind the public
agent.pyentry pointdescribe()contracts and*_keyparams across common, preprocessing, io, filtering, metrics,segmentation, tagging, inference, alm, datasets and advanced_pipelines
ChannelCount,SampleRateFilter,CreateInitialManifestAudioFolder,PreserveByValueConditions,ManifestCheckpoint,ManifestGroupExport,DocumentBatchJsonlWriterThe agent (
nemo_curator/audio_agent/) — the part not in any of the 11 PRs:discover/describe/context/validate/smoke/run/report/verify/reuse-scan/delta-run/plan-checkpoint/doctorpipeline serves the user's intent, not just whether it runs
Supporting: resource telemetry for smoke calibration (
utils/performance_utils.py, opt-in viaRESOURCE_PROBEinbackends/base.py) and anemo-curator-audioconsole script.Trying it
uv sync --extra audio_cuda12 # or --extra audio_cpu python -m nemo_curator.audio_agent discover python -m nemo_curator.audio_agent context --data /path/to/audioNotes
nemo_curator/stages/audio/andnemo_curator/audio_agent/this touches five files:pyproject.toml(console script + acheck-manifestignore for.agents/),.gitignore,AGENTS.md,utils/performance_utils.pyandbackends/base.py. It makes no change to CIconfiguration.
tests/audio_agent/has no CI matrix entry on this branch, so those tests do not run here. Thestage-side tests under
tests/stages/audio/do run via the existingstages-audioentry.unchanged, and whole-repo
ruff checkis clean.