Skip to content

feat(audio): agent-ready stages + the audio curation agent (full branch, draft) - #2343

Draft
shubhamNvidia wants to merge 6 commits into
NVIDIA-NeMo:mainfrom
shubhamNvidia:agent/adv
Draft

feat(audio): agent-ready stages + the audio curation agent (full branch, draft)#2343
shubhamNvidia wants to merge 6 commits into
NVIDIA-NeMo:mainfrom
shubhamNvidia:agent/adv

Conversation

@shubhamNvidia

Copy link
Copy Markdown
Contributor

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:

  • the _agent/ contract layer: AgentReady, StageContract, semantic roles, input residency,
    discovery, planning and the conformance harness, behind 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

The agent (nemo_curator/audio_agent/) — the part not in any of the 11 PRs:

  • deterministic verbs: discover / describe / context / validate / smoke / run /
    report / verify / reuse-scan / delta-run / plan-checkpoint / doctor
  • knowledge cards describing what each stage's output means, so a host can judge whether a
    pipeline serves the user's intent, not just whether it runs
  • recipes, host skills (Claude Code / Cursor / Codex), an MCP server, and the test suite

Supporting: resource telemetry for smoke calibration (utils/performance_utils.py, opt-in via
RESOURCE_PROBE in backends/base.py) and a nemo-curator-audio console 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/audio

Notes

  • Outside nemo_curator/stages/audio/ and nemo_curator/audio_agent/ this touches five files:
    pyproject.toml (console script + a check-manifest ignore for .agents/), .gitignore,
    AGENTS.md, utils/performance_utils.py and backends/base.py. It makes no change to CI
    configuration
    .
  • tests/audio_agent/ has no CI matrix entry on this branch, so those tests do not run here. The
    stage-side tests under tests/stages/audio/ do run via the existing stages-audio entry.
  • Verified locally: 51/51 stages pass static conformance, the stage-compatibility baseline hash is
    unchanged, and whole-repo ruff check is clean.

@copy-pr-bot

copy-pr-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant