Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions .github/workflows/export-kitten.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: export-kitten-to-onnx

on:
push:
branches:
- kitten-tts

workflow_dispatch:

concurrency:
group: export-kitten-to-onnx-${{ github.ref }}
cancel-in-progress: true

jobs:
export-kitten-to-onnx:
if: github.repository_owner == 'k2-fsa' || github.repository_owner == 'csukuangfj'
name: export kitten ${{ matrix.version }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.10"]

Comment on lines +17 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unused matrix.version reference.

The workflow references ${{ matrix.version }} in the job name but doesn't define version in the matrix strategy. This creates an undefined variable reference.

-    name: export kitten ${{ matrix.version }}
+    name: export kitten
🧰 Tools
🪛 actionlint (1.7.7)

17-17: property "version" is not defined in object type {os: string; python-version: number}

(expression)

🤖 Prompt for AI Agents
In .github/workflows/export-kitten.yaml around lines 17 to 24, the job name uses
the undefined variable matrix.version, which is not declared in the matrix
strategy. Remove the reference to ${ matrix.version } from the job name to avoid
the undefined variable error.

steps:
- uses: actions/checkout@v4

- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install Python dependencies
shell: bash
run: |

Copilot AI Aug 7, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The version constraints are very specific (e.g., "numpy<=1.26.4", "onnx==1.16.0"). Consider documenting why these specific versions are required or using more flexible version ranges if possible.

Suggested change
run: |
run: |
# NOTE: The following version constraints are required due to compatibility issues:
# - numpy<=1.26.4: Newer versions may break compatibility with onnxruntime or other dependencies.
# - onnx==1.16.0: This version is known to work with the current export scripts; newer versions may introduce breaking changes.
# - onnxruntime==1.17.1: Required for compatibility with the exported ONNX models and numpy version.

Copilot uses AI. Check for mistakes.
pip install "numpy<=1.26.4" onnx==1.16.0 onnxruntime==1.17.1 librosa soundfile piper_phonemize -f https://k2-fsa.github.io/icefall/piper_phonemize.html

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pin piper_phonemize version for reproducibility.

The workflow installs piper_phonemize without a specific version, which could lead to inconsistent builds if the package is updated.

-          pip install "numpy<=1.26.4" onnx==1.16.0 onnxruntime==1.17.1 librosa soundfile piper_phonemize -f https://k2-fsa.github.io/icefall/piper_phonemize.html
+          pip install "numpy<=1.26.4" onnx==1.16.0 onnxruntime==1.17.1 librosa soundfile "piper_phonemize>=1.0.0" -f https://k2-fsa.github.io/icefall/piper_phonemize.html
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pip install "numpy<=1.26.4" onnx==1.16.0 onnxruntime==1.17.1 librosa soundfile piper_phonemize -f https://k2-fsa.github.io/icefall/piper_phonemize.html
pip install "numpy<=1.26.4" onnx==1.16.0 onnxruntime==1.17.1 librosa soundfile "piper_phonemize>=1.0.0" -f https://k2-fsa.github.io/icefall/piper_phonemize.html
🤖 Prompt for AI Agents
In .github/workflows/export-kitten.yaml at line 36, the pip install command
includes piper_phonemize without a version specifier, risking inconsistent
builds. Modify the command to pin piper_phonemize to a specific version by
adding '==<version>' after the package name, ensuring reproducible and stable
workflow runs.


- name: Run
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
shell: bash
run: |
cd scripts/kitten-tts/nano_v0_1
./run.sh

- name: Collect results
shell: bash
run: |
curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/espeak-ng-data.tar.bz2
tar xf espeak-ng-data.tar.bz2
rm espeak-ng-data.tar.bz2

src=scripts/kitten-tts/nano_v0_1

d=kitten-nano-en-v0_1-fp16

mkdir $d
cp -a LICENSE $d/LICENSE
cp -a espeak-ng-data $d/
cp -v $src/model.fp16.onnx $d/model.fp16.onnx
cp -v $src/voices.bin $d/
cp -v $src/tokens.txt $d/
cp -v $src/../README.md $d/README.md
ls -lh $d/
tar cjfv $d.tar.bz2 $d

ls -lh $d.tar.bz2

- name: Release
if: github.repository_owner == 'csukuangfj'
uses: svenstaro/upload-release-action@v2
with:
file_glob: true
file: ./*.tar.bz2
overwrite: true
repo_name: k2-fsa/sherpa-onnx
repo_token: ${{ secrets.UPLOAD_GH_SHERPA_ONNX_TOKEN }}
tag: tts-models

- name: Release
if: github.repository_owner == 'k2-fsa'
uses: svenstaro/upload-release-action@v2
with:
file_glob: true
file: ./*.tar.bz2
overwrite: true
tag: tts-models

- name: Publish to huggingface
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
uses: nick-fields/retry@v3
with:
max_attempts: 20
timeout_seconds: 200
shell: bash
command: |
git config --global user.email "csukuangfj@gmail.com"
git config --global user.name "Fangjun Kuang"

dirs=(
kitten-nano-en-v0_1-fp16
)

export GIT_LFS_SKIP_SMUDGE=1
export GIT_CLONE_PROTECTION_ACTIVE=false

for d in ${dirs[@]}; do
rm -rf huggingface

git clone https://csukuangfj:$HF_TOKEN@huggingface.co/csukuangfj/$d huggingface
cd huggingface
rm -rf ./*

git lfs track "*.onnx"
git lfs track af_dict
git lfs track ar_dict
git lfs track cmn_dict
git lfs track da_dict en_dict fa_dict hu_dict ia_dict it_dict lb_dict phondata ru_dict ta_dict
git lfs track ur_dict yue_dict

cp -a ../$d/* ./

git add .

ls -lh

git status

git commit -m "add models"
git push https://csukuangfj:$HF_TOKEN@huggingface.co/csukuangfj/$d main || true
done
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,5 @@ README-DEV.txt
.idea
sherpa-onnx-dolphin-base-ctc-multi-lang-int8-2025-04-02
dict
*.npz
voices.bin
76 changes: 76 additions & 0 deletions scripts/kitten-tts/nano_v0_1/add_meta_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)


import argparse

import numpy as np
import onnx

from generate_voices_bin import speaker2id


def get_args():
parser = argparse.ArgumentParser()
parser.add_argument(
"--model", type=str, required=True, help="input and output onnx model"
)

return parser.parse_args()


def main():
args = get_args()
print(args.model)

model = onnx.load(args.model)

style = np.load("./voices.npz")

Copilot AI Aug 7, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using list(style.keys())[0] assumes the dictionary has at least one key but doesn't handle the empty case. Consider adding a check or using a more explicit approach to get the first speaker's style shape.

Suggested change
style = np.load("./voices.npz")
style = np.load("./voices.npz")
if not style.keys():
raise ValueError("No styles found in voices.npz. The file may be empty or corrupted.")

Copilot uses AI. Check for mistakes.
style_shape = style[list(style.keys())[0]].shape
Comment on lines +28 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for file loading operations.

The script loads voices.npz and accesses its keys without error handling, which could fail if the file doesn't exist or has an unexpected structure.

-    style = np.load("./voices.npz")
-    style_shape = style[list(style.keys())[0]].shape
+    try:
+        style = np.load("./voices.npz")
+        if len(style.keys()) == 0:
+            raise ValueError("voices.npz contains no data")
+        style_shape = style[list(style.keys())[0]].shape
+    except FileNotFoundError:
+        raise FileNotFoundError("voices.npz file not found. Ensure generate_voices_bin.py was run first.")
+    except Exception as e:
+        raise RuntimeError(f"Failed to load voice embeddings: {e}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
style = np.load("./voices.npz")
style_shape = style[list(style.keys())[0]].shape
try:
style = np.load("./voices.npz")
if len(style.keys()) == 0:
raise ValueError("voices.npz contains no data")
style_shape = style[list(style.keys())[0]].shape
except FileNotFoundError:
raise FileNotFoundError(
"voices.npz file not found. Ensure generate_voices_bin.py was run first."
)
except Exception as e:
raise RuntimeError(f"Failed to load voice embeddings: {e}")
🤖 Prompt for AI Agents
In scripts/kitten-tts/nano_v0_1/add_meta_data.py around lines 28 to 29, add
error handling for loading the voices.npz file and accessing its keys. Wrap the
np.load call and subsequent key access in a try-except block to catch exceptions
like FileNotFoundError or KeyError, and handle them gracefully by logging an
error message or exiting the script with a clear explanation.


speaker2id_str = ""
id2speaker_str = ""
sep = ""
for s, i in speaker2id.items():
speaker2id_str += f"{sep}{s}->{i}"
id2speaker_str += f"{sep}{i}->{s}"
sep = ","

meta_data = {
"model_type": "kitten-tts",
"language": "English",
"has_espeak": 1,
"sample_rate": 24000,
"version": 1,
"voice": "en-us",
"style_dim": ",".join(map(str, style_shape)),
"n_speakers": len(speaker2id),
"speaker2id": speaker2id_str,
"id2speaker": id2speaker_str,
"speaker_names": ",".join(map(str, speaker2id.keys())),
"model_url": "https://huggingface.co/KittenML/kitten-tts-nano-0.1",
"see_also": "https://github.com/KittenML/KittenTTS",
"maintainer": "k2-fsa",
"comment": "This is kitten-tts-nano-0.1 and supports only English",
}

print(model.metadata_props)

while len(model.metadata_props):
model.metadata_props.pop()

for key, value in meta_data.items():
meta = model.metadata_props.add()
meta.key = key
meta.value = str(value)
print("--------------------")

print(model.metadata_props)

onnx.save(model, args.model)

print(f"Please see {args.model}")


if __name__ == "__main__":
main()
30 changes: 30 additions & 0 deletions scripts/kitten-tts/nano_v0_1/convert_opset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)

"""
Change the model so that it can be run in onnxruntime 1.17.1
"""

import onnx


def main():
model = onnx.load("kitten_tts_nano_v0_1.onnx")

# Print current opsets
for opset in model.opset_import:
print(f"Domain: '{opset.domain}', Version: {opset.version}")

# Modify the opset versions (be careful!)
for opset in model.opset_import:
if opset.domain == "": # ai.onnx domain
opset.version = 19 # change from 20 to 19
elif opset.domain == "ai.onnx.ml":
opset.version = 4 # change from 5 to 4

# Save the modified model
onnx.save(model, "model.fp16.onnx")

Comment on lines +11 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and make filenames configurable.

The script lacks error handling and uses hardcoded filenames, which reduces flexibility and robustness.

Consider this improvement:

+import sys
+import argparse
+
+def get_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--input", default="kitten_tts_nano_v0_1.onnx")
+    parser.add_argument("--output", default="model.fp16.onnx")
+    return parser.parse_args()

 def main():
-    model = onnx.load("kitten_tts_nano_v0_1.onnx")
+    args = get_args()
+    try:
+        model = onnx.load(args.input)
+    except Exception as e:
+        print(f"Error loading model {args.input}: {e}")
+        sys.exit(1)

     # Print current opsets
     for opset in model.opset_import:
         print(f"Domain: '{opset.domain}', Version: {opset.version}")

     # Modify the opset versions (be careful!)
+    modified = False
     for opset in model.opset_import:
         if opset.domain == "":  # ai.onnx domain
-            opset.version = 19  # change from 20 to 19
+            if opset.version == 20:
+                opset.version = 19
+                modified = True
         elif opset.domain == "ai.onnx.ml":
-            opset.version = 4  # change from 5 to 4
+            if opset.version == 5:
+                opset.version = 4
+                modified = True

+    if not modified:
+        print("Warning: No opset versions were modified")
+        
-    # Save the modified model
-    onnx.save(model, "model.fp16.onnx")
+    try:
+        onnx.save(model, args.output)
+        print(f"Model saved to {args.output}")
+    except Exception as e:
+        print(f"Error saving model: {e}")
+        sys.exit(1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def main():
model = onnx.load("kitten_tts_nano_v0_1.onnx")
# Print current opsets
for opset in model.opset_import:
print(f"Domain: '{opset.domain}', Version: {opset.version}")
# Modify the opset versions (be careful!)
for opset in model.opset_import:
if opset.domain == "": # ai.onnx domain
opset.version = 19 # change from 20 to 19
elif opset.domain == "ai.onnx.ml":
opset.version = 4 # change from 5 to 4
# Save the modified model
onnx.save(model, "model.fp16.onnx")
import sys
import argparse
import onnx
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument("--input", default="kitten_tts_nano_v0_1.onnx")
parser.add_argument("--output", default="model.fp16.onnx")
return parser.parse_args()
def main():
args = get_args()
try:
model = onnx.load(args.input)
except Exception as e:
print(f"Error loading model {args.input}: {e}")
sys.exit(1)
# Print current opsets
for opset in model.opset_import:
print(f"Domain: '{opset.domain}', Version: {opset.version}")
# Modify the opset versions (be careful!)
modified = False
for opset in model.opset_import:
if opset.domain == "": # ai.onnx domain
if opset.version == 20:
opset.version = 19
modified = True
elif opset.domain == "ai.onnx.ml":
if opset.version == 5:
opset.version = 4
modified = True
if not modified:
print("Warning: No opset versions were modified")
try:
onnx.save(model, args.output)
print(f"Model saved to {args.output}")
except Exception as e:
print(f"Error saving model: {e}")
sys.exit(1)
🤖 Prompt for AI Agents
In scripts/kitten-tts/nano_v0_1/convert_opset.py around lines 11 to 27, the
script lacks error handling and uses hardcoded filenames, limiting flexibility
and robustness. Modify the script to accept input and output filenames as
parameters or command-line arguments, and add try-except blocks around the model
loading, opset modification, and saving steps to catch and log potential errors
gracefully.


if __name__ == "__main__":
main()
27 changes: 27 additions & 0 deletions scripts/kitten-tts/nano_v0_1/generate_tokens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)


def get_vocab():
# https://github.com/KittenML/KittenTTS/blob/main/kittentts/onnx_model.py#L17
_pad = "$"
_punctuation = ';:,.!?¡¿—…"«»"" '
_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
_letters_ipa = "ɑɐɒæɓʙβɔɕçɗɖðʤəɘɚɛɜɝɞɟʄɡɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʊʋⱱʌɣɤʍχʎʏʑʐʒʔʡʕʢǀǁǂǃˈˌːˑʼʴʰʱʲʷˠˤ˞↓↑→↗↘'̩'ᵻ"

symbols = [_pad] + list(_punctuation) + list(_letters) + list(_letters_ipa)
dicts = {}
for i in range(len((symbols))):
dicts[symbols[i]] = i
return dicts


def main():
token2id = get_vocab()
with open("tokens.txt", "w", encoding="utf-8") as f:
for s, i in token2id.items():
f.write(f"{s} {i}\n")


if __name__ == "__main__":
main()
38 changes: 38 additions & 0 deletions scripts/kitten-tts/nano_v0_1/generate_voices_bin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)
from pathlib import Path

import numpy as np

speakers = [
"expr-voice-2-m",
"expr-voice-2-f",
"expr-voice-3-m",
"expr-voice-3-f",
"expr-voice-4-m",
"expr-voice-4-f",
"expr-voice-5-m",
"expr-voice-5-f",
]

id2speaker = {idx: speaker for idx, speaker in enumerate(speakers)}

speaker2id = {speaker: idx for idx, speaker in id2speaker.items()}

Comment on lines +7 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making speaker list data-driven and add validation.

The hardcoded speaker list could become out of sync with the actual contents of voices.npz. Consider loading speakers dynamically or validating they exist.

 def main():
     if Path("./voices.bin").is_file():
         print("./voices.bin exists - skip")
         return

-    voices = np.load("./voices.npz")
+    try:
+        voices = np.load("./voices.npz")
+    except Exception as e:
+        print(f"Error loading voices.npz: {e}")
+        return
+        
+    # Validate that all expected speakers exist
+    available_speakers = set(voices.keys())
+    expected_speakers = set(speakers)
+    missing_speakers = expected_speakers - available_speakers
+    if missing_speakers:
+        print(f"Warning: Missing speakers in voices.npz: {missing_speakers}")
+        print(f"Available speakers: {sorted(available_speakers)}")
+        return

     with open("voices.bin", "wb") as f:
         for speaker in speakers:
             v = voices[speaker]
-            # v.shape (1, 256)
+            if v.shape != (1, 256):
+                print(f"Warning: Speaker {speaker} has unexpected shape {v.shape}, expected (1, 256)")
             f.write(v.tobytes())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
speakers = [
"expr-voice-2-m",
"expr-voice-2-f",
"expr-voice-3-m",
"expr-voice-3-f",
"expr-voice-4-m",
"expr-voice-4-f",
"expr-voice-5-m",
"expr-voice-5-f",
]
id2speaker = {idx: speaker for idx, speaker in enumerate(speakers)}
speaker2id = {speaker: idx for idx, speaker in id2speaker.items()}
def main():
if Path("./voices.bin").is_file():
print("./voices.bin exists - skip")
return
try:
voices = np.load("./voices.npz")
except Exception as e:
print(f"Error loading voices.npz: {e}")
return
# Validate that all expected speakers exist
available_speakers = set(voices.keys())
expected_speakers = set(speakers)
missing_speakers = expected_speakers - available_speakers
if missing_speakers:
print(f"Warning: Missing speakers in voices.npz: {missing_speakers}")
print(f"Available speakers: {sorted(available_speakers)}")
return
with open("voices.bin", "wb") as f:
for speaker in speakers:
v = voices[speaker]
if v.shape != (1, 256):
print(f"Warning: Speaker {speaker} has unexpected shape {v.shape}, expected (1, 256)")
f.write(v.tobytes())
🤖 Prompt for AI Agents
In scripts/kitten-tts/nano_v0_1/generate_voices_bin.py around lines 7 to 21, the
speaker list is hardcoded which risks being out of sync with the actual
voices.npz data. Modify the code to load the speaker list dynamically from
voices.npz or add validation logic to check that each hardcoded speaker exists
in voices.npz. This ensures the speaker data is always accurate and consistent
with the source file.


def main():
if Path("./voices.bin").is_file():
print("./voices.bin exists - skip")
return

voices = np.load("./voices.npz")

with open("voices.bin", "wb") as f:
for speaker in speakers:
v = voices[speaker]
# v.shape (1, 256)
f.write(v.tobytes())


if __name__ == "__main__":
main()
20 changes: 20 additions & 0 deletions scripts/kitten-tts/nano_v0_1/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)

set -ex

if [ ! -f kitten_tts_nano_v0_1.onnx ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/kitten_tts_nano_v0_1.onnx
fi

if [ ! -f voices.npz ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/voices.npz
fi
Comment on lines +6 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add integrity verification for downloaded files.

Downloading files from external URLs without integrity verification poses security risks. Consider adding checksum validation.

 if [ ! -f kitten_tts_nano_v0_1.onnx ]; then
   curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/kitten_tts_nano_v0_1.onnx
+  # Verify file integrity (add expected SHA256 hash)
+  # echo "expected_hash kitten_tts_nano_v0_1.onnx" | sha256sum -c
 fi

 if [ ! -f voices.npz ]; then
   curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/voices.npz
+  # Verify file integrity (add expected SHA256 hash)
+  # echo "expected_hash voices.npz" | sha256sum -c
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ ! -f kitten_tts_nano_v0_1.onnx ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/kitten_tts_nano_v0_1.onnx
fi
if [ ! -f voices.npz ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/voices.npz
fi
if [ ! -f kitten_tts_nano_v0_1.onnx ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/kitten_tts_nano_v0_1.onnx
# Verify file integrity (add expected SHA256 hash)
# echo "expected_hash kitten_tts_nano_v0_1.onnx" | sha256sum -c
fi
if [ ! -f voices.npz ]; then
curl -SL -O https://huggingface.co/KittenML/kitten-tts-nano-0.1/resolve/main/voices.npz
# Verify file integrity (add expected SHA256 hash)
# echo "expected_hash voices.npz" | sha256sum -c
fi
🤖 Prompt for AI Agents
In scripts/kitten-tts/nano_v0_1/run.sh around lines 6 to 12, the script
downloads files without verifying their integrity. To fix this, add checksum
verification after each download by including known hash values for the files
and using a tool like sha256sum to validate the downloaded files. If the
checksum does not match, the script should report an error and exit to prevent
using corrupted or tampered files.


./generate_voices_bin.py
./generate_tokens.py
./convert_opset.py
./show.py
./add_meta_data.py --model ./model.fp16.onnx
# ./test.py --model ./model.fp16.onnx --tokens ./tokens.txt --voice ./voices.bin
ls -lh
45 changes: 45 additions & 0 deletions scripts/kitten-tts/nano_v0_1/show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# Copyright 2025 Xiaomi Corp. (authors: Fangjun Kuang)

import onnxruntime
import onnx

"""
[key: "onnx.infer"
value: "onnxruntime.quant"
, key: "onnx.quant.pre_process"
value: "onnxruntime.quant"
]
NodeArg(name='input_ids', type='tensor(int64)', shape=[1, 'sequence_length'])
NodeArg(name='style', type='tensor(float)', shape=[1, 256])
NodeArg(name='speed', type='tensor(float)', shape=[1])
-----
NodeArg(name='waveform', type='tensor(float)', shape=['num_samples'])
NodeArg(name='duration', type='tensor(int64)', shape=['Castduration_dim_0'])
"""


def show(filename):
model = onnx.load(filename)
print(model.metadata_props)

session_opts = onnxruntime.SessionOptions()
session_opts.log_severity_level = 3
sess = onnxruntime.InferenceSession(
filename, session_opts, providers=["CPUExecutionProvider"]
)
for i in sess.get_inputs():
print(i)

print("-----")

for i in sess.get_outputs():
print(i)


def main():
show("./model.fp16.onnx")
Comment on lines +22 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and make filename configurable.

The script should handle potential errors when loading models and allow flexible filename input.

+import sys
+import argparse

+def get_args():
+    parser = argparse.ArgumentParser(description="Inspect ONNX model")
+    parser.add_argument("--model", default="./model.fp16.onnx", help="Path to ONNX model file")
+    return parser.parse_args()

 def show(filename):
-    model = onnx.load(filename)
-    print(model.metadata_props)
+    try:
+        model = onnx.load(filename)
+        print(model.metadata_props)
+    except Exception as e:
+        print(f"Error loading model {filename}: {e}")
+        return False

     session_opts = onnxruntime.SessionOptions()
     session_opts.log_severity_level = 3
-    sess = onnxruntime.InferenceSession(
-        filename, session_opts, providers=["CPUExecutionProvider"]
-    )
-    for i in sess.get_inputs():
-        print(i)
+    try:
+        sess = onnxruntime.InferenceSession(
+            filename, session_opts, providers=["CPUExecutionProvider"]
+        )
+        for i in sess.get_inputs():
+            print(i)

-    print("-----")
+        print("-----")

-    for i in sess.get_outputs():
-        print(i)
+        for i in sess.get_outputs():
+            print(i)
+        return True
+    except Exception as e:
+        print(f"Error creating inference session: {e}")
+        return False

 def main():
-    show("./model.fp16.onnx")
+    args = get_args()
+    if not show(args.model):
+        sys.exit(1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def show(filename):
model = onnx.load(filename)
print(model.metadata_props)
session_opts = onnxruntime.SessionOptions()
session_opts.log_severity_level = 3
sess = onnxruntime.InferenceSession(
filename, session_opts, providers=["CPUExecutionProvider"]
)
for i in sess.get_inputs():
print(i)
print("-----")
for i in sess.get_outputs():
print(i)
def main():
show("./model.fp16.onnx")
import sys
import argparse
def get_args():
parser = argparse.ArgumentParser(description="Inspect ONNX model")
parser.add_argument("--model", default="./model.fp16.onnx", help="Path to ONNX model file")
return parser.parse_args()
def show(filename):
try:
model = onnx.load(filename)
print(model.metadata_props)
except Exception as e:
print(f"Error loading model {filename}: {e}")
return False
session_opts = onnxruntime.SessionOptions()
session_opts.log_severity_level = 3
try:
sess = onnxruntime.InferenceSession(
filename, session_opts, providers=["CPUExecutionProvider"]
)
for i in sess.get_inputs():
print(i)
print("-----")
for i in sess.get_outputs():
print(i)
return True
except Exception as e:
print(f"Error creating inference session: {e}")
return False
def main():
args = get_args()
if not show(args.model):
sys.exit(1)
🤖 Prompt for AI Agents
In scripts/kitten-tts/nano_v0_1/show.py around lines 22 to 41, add error
handling around the model loading and inference session creation to catch and
report exceptions gracefully. Also, modify the main function to accept the
filename as a parameter or from command-line arguments instead of hardcoding it,
enabling flexible filename input.



Comment on lines +41 to +43

Copilot AI Aug 7, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filename "./model.fp16.onnx" is hardcoded. Consider accepting the filename as a command-line argument for better reusability.

Suggested change
show("./model.fp16.onnx")
parser = argparse.ArgumentParser(description="Show ONNX model info")
parser.add_argument("filename", nargs="?", default="./model.fp16.onnx",
help="Path to the ONNX model file (default: ./model.fp16.onnx)")
args = parser.parse_args()
show(args.filename)

Copilot uses AI. Check for mistakes.
if __name__ == "__main__":
main()
Loading