Skip to content

Commit ae9375a

Browse files
authored
Merge branch 'main' into 2603_tts_tests_deterministic
2 parents 9ac8313 + 9a1a745 commit ae9375a

9 files changed

Lines changed: 284 additions & 21 deletions

File tree

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Claude Answer Issue
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: read
9+
issues: write
10+
id-token: write
11+
12+
jobs:
13+
authorize:
14+
if: >-
15+
!github.event.issue.pull_request &&
16+
contains(github.event.comment.body, '/claude answer')
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Check team membership
20+
uses: actions/github-script@v7
21+
with:
22+
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
23+
script: |
24+
const username = context.payload.comment.user.login;
25+
try {
26+
const res = await github.rest.teams.getMembershipForUserInOrg({
27+
org: 'NVIDIA-NeMo',
28+
team_slug: 'speech_team',
29+
username,
30+
});
31+
if (res.data.state !== 'active') {
32+
core.setFailed(`${username} is not an active member of NVIDIA Speech Team`);
33+
}
34+
} catch (e) {
35+
core.setFailed(`${username} is not a member of NVIDIA Speech Team`);
36+
}
37+
38+
acknowledge:
39+
needs: authorize
40+
runs-on: ubuntu-latest
41+
steps:
42+
- name: Add eyes reaction to comment
43+
uses: actions/github-script@v7
44+
with:
45+
script: |
46+
await github.rest.reactions.createForIssueComment({
47+
owner: context.repo.owner,
48+
repo: context.repo.repo,
49+
comment_id: context.payload.comment.id,
50+
content: 'eyes'
51+
});
52+
53+
claude-answer:
54+
needs: acknowledge
55+
runs-on: ubuntu-latest
56+
steps:
57+
- uses: actions/checkout@v4
58+
- uses: anthropics/claude-code-action@v1
59+
with:
60+
prompt: |
61+
You are a helpful assistant for the NeMo repository.
62+
Answer the user's question based on the issue description, comments, and the codebase.
63+
Be concise and provide code references where relevant.
64+
Do NOT make any code changes or create PRs — only answer the question.
65+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

.github/workflows/claude-fix.yml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
name: Claude Fix Issue
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
issues: write
11+
id-token: write
12+
13+
jobs:
14+
authorize:
15+
if: >-
16+
!github.event.issue.pull_request &&
17+
contains(github.event.comment.body, '/claude fix')
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Check team membership
21+
uses: actions/github-script@v7
22+
with:
23+
github-token: ${{ secrets.ORG_TEAM_READ_TOKEN }}
24+
script: |
25+
const username = context.payload.comment.user.login;
26+
try {
27+
const res = await github.rest.teams.getMembershipForUserInOrg({
28+
org: 'NVIDIA-NeMo',
29+
team_slug: 'speech_team',
30+
username,
31+
});
32+
if (res.data.state !== 'active') {
33+
core.setFailed(`${username} is not an active member of NVIDIA Speech Team`);
34+
}
35+
} catch (e) {
36+
core.setFailed(`${username} is not a member of NVIDIA Speech Team`);
37+
}
38+
39+
acknowledge:
40+
needs: authorize
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Add eyes reaction to comment
44+
uses: actions/github-script@v7
45+
with:
46+
script: |
47+
await github.rest.reactions.createForIssueComment({
48+
owner: context.repo.owner,
49+
repo: context.repo.repo,
50+
comment_id: context.payload.comment.id,
51+
content: 'eyes'
52+
});
53+
54+
claude-fix:
55+
needs: acknowledge
56+
runs-on: ubuntu-latest
57+
steps:
58+
- uses: actions/checkout@v4
59+
- uses: anthropics/claude-code-action@v1
60+
id: claude
61+
with:
62+
prompt: |
63+
You are a developer working on the NeMo repository.
64+
Implement a focused fix for the issue based on the issue description and comments.
65+
66+
Requirements:
67+
- Always use `git commit -s` to sign off all commits (DCO requirement).
68+
- Prioritize correctness and minimal scope; avoid unrelated refactors.
69+
- Reproduce or reason about the failure first, then implement the smallest robust fix.
70+
- If required, add or update tests for the changed behavior. If tests are not feasible, explain why.
71+
- If required, update related docs or comments when behavior or usage changes.
72+
73+
PR expectations:
74+
- Create a new branch and open a pull request with your changes.
75+
- Include a concise summary of root cause and fix based on the following PR template:
76+
```
77+
# What does this PR do ?
78+
Add a one line overview of what this PR aims to accomplish.
79+
80+
# Changelog
81+
Add specific line by line info of high level changes in this PR.
82+
83+
# Usage
84+
Add a usage example of the changed functionality.
85+
86+
Related to #${{ github.event.issue.number }}
87+
88+
This PR is created by Claude.
89+
```
90+
91+
92+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
93+
94+
- name: Label PR with agent-contribution
95+
if: steps.claude.outputs.branch_name
96+
uses: actions/github-script@v7
97+
with:
98+
script: |
99+
const prs = await github.rest.pulls.list({
100+
owner: context.repo.owner,
101+
repo: context.repo.repo,
102+
head: `${context.repo.owner}:${process.env.BRANCH_NAME}`,
103+
state: 'open'
104+
});
105+
if (prs.data.length > 0) {
106+
await github.rest.issues.addLabels({
107+
owner: context.repo.owner,
108+
repo: context.repo.repo,
109+
issue_number: prs.data[0].number,
110+
labels: ['agent-contribution']
111+
});
112+
}
113+
env:
114+
BRANCH_NAME: ${{ steps.claude.outputs.branch_name }}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Claude Code Review
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
issues: write
11+
id-token: write
12+
13+
jobs:
14+
acknowledge:
15+
if: >-
16+
github.event.issue.pull_request &&
17+
contains(github.event.comment.body, '/claude review')
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Add eyes reaction to comment
21+
uses: actions/github-script@v7
22+
with:
23+
script: |
24+
await github.rest.reactions.createForIssueComment({
25+
owner: context.repo.owner,
26+
repo: context.repo.repo,
27+
comment_id: context.payload.comment.id,
28+
content: 'eyes'
29+
});
30+
31+
claude-review:
32+
needs: acknowledge
33+
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_claude_review.yml@v0.79.0
34+
with:
35+
prompt: |
36+
You are doing a light code review. Keep it concise and actionable.
37+
38+
Focus ONLY on:
39+
- Critical bugs or logic errors
40+
- Typos in code, comments, or strings
41+
- Missing or insufficient test coverage for changed code
42+
- Outdated or inaccurate documentation affected by the changes
43+
44+
Do NOT comment on:
45+
- Style preferences or formatting
46+
- Minor naming suggestions
47+
- Architectural opinions or refactoring ideas
48+
- Performance unless there is a clear, measurable issue
49+
50+
Provide feedback using inline comments for specific code suggestions.
51+
Use top-level comments for general observations.
52+
53+
IMPORTANT: Do NOT approve the pull request. Only leave comments.
54+
55+
It's perfectly acceptable to not have anything to comment on.
56+
If you do not have anything to comment on, post "LGTM".
57+
secrets:
58+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

nemo/collections/common/data/blendable_dataset.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,40 @@ def __init__(self, datasets, weights, size):
4848
self.dataset_sample_index = np.zeros(self.size, dtype=np.int64)
4949

5050
app_state = AppState()
51+
52+
# Determine if we are in a distributed environment
53+
is_dist = torch.distributed.is_available() and torch.distributed.is_initialized()
54+
5155
try:
52-
if app_state.local_rank == 0:
56+
# Defensive check for local_rank in AppState
57+
local_rank = getattr(app_state, 'local_rank', 0) if is_dist else 0
58+
59+
if local_rank == 0:
5360
compile_helper()
54-
torch.distributed.barrier()
61+
62+
if is_dist:
63+
torch.distributed.barrier()
64+
65+
# pylint: disable=import-outside-toplevel
5566
from nemo.collections.common.data import helpers
56-
except ImportError:
67+
except ImportError as exc:
5768
raise ImportError(
5869
'Could not compile megatron dataset C++ helper functions and therefore '
5970
'cannot import helpers python file.'
60-
)
71+
) from exc
72+
73+
# Only the main process (rank 0) should handle logging/progress within helpers
74+
is_main_process = (torch.distributed.get_rank() == 0) if is_dist else True
6175

6276
helpers.build_blending_indices(
6377
self.dataset_index,
6478
self.dataset_sample_index,
6579
weights,
6680
num_datasets,
6781
self.size,
68-
torch.distributed.get_rank() == 0,
69-
)
70-
logging.info(
71-
'> elapsed time for building blendable dataset indices: ' '{:.2f} (sec)'.format(time.time() - start_time)
82+
is_main_process,
7283
)
84+
logging.info(f'> elapsed time for building blendable dataset indices: {time.time() - start_time:.2f} (sec)')
7385

7486
def __len__(self):
7587
return self.size

nemo/collections/tts/models/magpietts.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,10 @@ def __init__(self, cfg: DictConfig, trainer: 'Trainer' = None):
601601
self.register_buffer('_baked_embedding_T', None) # Time dimension
602602
self.register_buffer('_baked_embedding_D', None) # Embedding dimension
603603
self.register_buffer('baked_context_embedding_len', None) # Per-speaker lengths (N,)
604+
# Probability of bypassing the context encoder during training and instead feeding
605+
# batch-shuffled raw context embeddings, so the model learns not to clone voices
606+
# from untransformed (i.e. not encoded by the context encoder) input.
607+
self.train_shuffle_context_embedding_prob = cfg.get('train_shuffle_context_embedding_prob', 0.0)
604608
else:
605609
raise ValueError(f"Unsupported model type {self.model_type}")
606610

@@ -2299,9 +2303,24 @@ def _prepare_decoder_context(
22992303
context_input_lens = context_input_lens.to(text.device)
23002304
context_mask = get_mask_from_lengths(context_input_lens)
23012305
else:
2302-
context_embeddings = self.context_encoder(
2303-
context_input_embedded, context_mask, cond=None, cond_mask=None
2304-
)['output']
2306+
# Zero-shot disable: with some probability, bypass the context encoder and feed
2307+
# batch-shuffled raw embeddings so the model learns to not clone from untransformed input.
2308+
# Skip when batch_size == 1: rolling a single sample maps it back to itself,
2309+
# so the context would remain matched to the correct speaker.
2310+
batch_size = context_input_embedded.size(0)
2311+
if (
2312+
self.training
2313+
and batch_size > 1
2314+
and self.train_shuffle_context_embedding_prob > 0
2315+
and random.random() < self.train_shuffle_context_embedding_prob
2316+
):
2317+
shift = random.randint(1, batch_size - 1)
2318+
context_embeddings = context_input_embedded.roll(shift, dims=0)
2319+
context_mask = context_mask.roll(shift, dims=0)
2320+
else:
2321+
context_embeddings = self.context_encoder(
2322+
context_input_embedded, context_mask, cond=None, cond_mask=None
2323+
)['output']
23052324
else:
23062325
raise ValueError(f"Unsupported model type for decoder context: {self.model_type}")
23072326

nemo/core/classes/mixins/hf_io_mixin.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,7 @@ def search_huggingface_models(cls, model_filter: Optional[Dict[str, Any]] = None
116116
# Setup extra arguments for model filtering
117117
all_results = [] # type: List[ModelInfo]
118118

119-
results = api.list_models(
120-
token=hf_token, sort="lastModified", direction=-1, **model_filter
121-
) # type: Iterable[ModelInfo]
119+
results = api.list_models(token=hf_token, sort="lastModified", **model_filter) # type: Iterable[ModelInfo]
122120

123121
return results
124122

requirements/requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ numexpr<2.14.0 # WAR for attempted use of nonexistent numpy.typing
77
numpy>=1.22
88
onnx>=1.7.0
99
# Align with upstream PyTorch requirements
10-
protobuf>=6.33
1110
python-dateutil
1211
ruamel.yaml
1312
scikit-learn

tests/collections/tts/metrics/test_eou_classifier.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717

1818
from nemo.collections.tts.metrics.eou_classifier import EoUClassification, EoUClassifier, EoUType, TokenSegment
1919

20-
# ---------------------------------------------------------------------------
21-
# TODO: Fill in (audio_path, text) pairs per EoU class.
22-
# Paths are relative to the repo root. Multiple examples per class are supported.
23-
# ---------------------------------------------------------------------------
20+
# Path to the test data
2421
DATA_PATH = "/home/TestData/tts/eou_classifier_unit_test"
22+
2523
# TEST_NAME, EoU_TYPE, AUDIO_PATH, TEXT
2624
_CLASSIFICATION_CASES: list[tuple[str, EoUType, str, str]] = [
2725
(

tests/functional_tests/L2_TTS_InferEvaluate_Magpietts_SeenSpeakers.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD=1 coverage run -a --data-file=/workspace/.cover
2222
--cfg_scale 2.5 \
2323
--num_repeats 1 \
2424
--temperature 0.6 \
25-
--nemo_files /home/TestData/tts/2602_MagpieTTS/feb26_Magpie-TTS-ML-V1--val_cer_gt=0.3258-step=1000.nemo \
25+
--nemo_files /home/TestData/tts/2602_MagpieTTS/magpie_tts_multilingual_357m.nemo \
2626
--apply_attention_prior \
2727
--apply_prior_to_layers "3,4,5,6,7,8,9" \
2828
--estimate_alignment_from_layers "3,4,5,6" \
2929
--use_local_transformer \
3030
--run_evaluation \
3131
--clean_up_disk \
3232
--cer_target 0.3 \
33-
--ssim_target 0.5
33+
--ssim_target 0.0

0 commit comments

Comments
 (0)