Fangjun Kuang
Committed by GitHub

Add Dart API for homophone replacer (#2167)

@@ -4,6 +4,58 @@ set -ex @@ -4,6 +4,58 @@ set -ex
4 4
5 cd dart-api-examples 5 cd dart-api-examples
6 6
  7 +pushd non-streaming-asr
  8 +
  9 +echo '----------SenseVoice----------'
  10 +./run-sense-voice-with-hr.sh
  11 +./run-sense-voice.sh
  12 +rm -rf sherpa-onnx-*
  13 +
  14 +echo '----------FireRedAsr----------'
  15 +./run-fire-red-asr.sh
  16 +rm -rf sherpa-onnx-fire-red-asr-*
  17 +
  18 +echo '----------NeMo transducer----------'
  19 +./run-nemo-transducer.sh
  20 +rm -rf sherpa-onnx-*
  21 +
  22 +echo '----------Dolphin CTC----------'
  23 +./run-dolphin-ctc.sh
  24 +rm -rf sherpa-onnx-*
  25 +
  26 +echo '----------NeMo CTC----------'
  27 +./run-nemo-ctc.sh
  28 +rm -rf sherpa-onnx-*
  29 +
  30 +echo '----------TeleSpeech CTC----------'
  31 +./run-telespeech-ctc.sh
  32 +rm -rf sherpa-onnx-*
  33 +
  34 +echo '----------moonshine----------'
  35 +./run-moonshine.sh
  36 +rm -rf sherpa-onnx-*
  37 +
  38 +echo '----------whisper----------'
  39 +./run-whisper.sh
  40 +rm -rf sherpa-onnx-*
  41 +
  42 +echo '----------zipformer transducer----------'
  43 +./run-zipformer-transducer.sh
  44 +rm -rf sherpa-onnx-*
  45 +
  46 +echo '----------paraformer itn----------'
  47 +./run-paraformer-itn.sh
  48 +
  49 +echo '----------paraformer----------'
  50 +./run-paraformer.sh
  51 +rm -rf sherpa-onnx-*
  52 +
  53 +echo '----------VAD with paraformer----------'
  54 +./run-vad-with-paraformer.sh
  55 +rm -rf sherpa-onnx-*
  56 +
  57 +popd # non-streaming-asr
  58 +
7 pushd speech-enhancement-gtcrn 59 pushd speech-enhancement-gtcrn
8 echo "speech enhancement with gtcrn models" 60 echo "speech enhancement with gtcrn models"
9 ./run.sh 61 ./run.sh
@@ -101,57 +153,6 @@ pushd keyword-spotter @@ -101,57 +153,6 @@ pushd keyword-spotter
101 ./run-zh.sh 153 ./run-zh.sh
102 popd 154 popd
103 155
104 -pushd non-streaming-asr  
105 -  
106 -echo '----------FireRedAsr----------'  
107 -./run-fire-red-asr.sh  
108 -rm -rf sherpa-onnx-fire-red-asr-*  
109 -  
110 -echo '----------SenseVoice----------'  
111 -./run-sense-voice.sh  
112 -rm -rf sherpa-onnx-*  
113 -  
114 -echo '----------NeMo transducer----------'  
115 -./run-nemo-transducer.sh  
116 -rm -rf sherpa-onnx-*  
117 -  
118 -echo '----------Dolphin CTC----------'  
119 -./run-dolphin-ctc.sh  
120 -rm -rf sherpa-onnx-*  
121 -  
122 -echo '----------NeMo CTC----------'  
123 -./run-nemo-ctc.sh  
124 -rm -rf sherpa-onnx-*  
125 -  
126 -echo '----------TeleSpeech CTC----------'  
127 -./run-telespeech-ctc.sh  
128 -rm -rf sherpa-onnx-*  
129 -  
130 -echo '----------moonshine----------'  
131 -./run-moonshine.sh  
132 -rm -rf sherpa-onnx-*  
133 -  
134 -echo '----------whisper----------'  
135 -./run-whisper.sh  
136 -rm -rf sherpa-onnx-*  
137 -  
138 -echo '----------zipformer transducer----------'  
139 -./run-zipformer-transducer.sh  
140 -rm -rf sherpa-onnx-*  
141 -  
142 -echo '----------paraformer itn----------'  
143 -./run-paraformer-itn.sh  
144 -  
145 -echo '----------paraformer----------'  
146 -./run-paraformer.sh  
147 -rm -rf sherpa-onnx-*  
148 -  
149 -echo '----------VAD with paraformer----------'  
150 -./run-vad-with-paraformer.sh  
151 -rm -rf sherpa-onnx-*  
152 -  
153 -popd # non-streaming-asr  
154 -  
155 pushd streaming-asr 156 pushd streaming-asr
156 157
157 echo '----------streaming zipformer ctc HLG----------' 158 echo '----------streaming zipformer ctc HLG----------'
  1 +// Copyright (c) 2025 Xiaomi Corporation
  2 +import 'dart:io';
  3 +
  4 +import 'package:args/args.dart';
  5 +import 'package:sherpa_onnx/sherpa_onnx.dart' as sherpa_onnx;
  6 +
  7 +import './init.dart';
  8 +
  9 +void main(List<String> arguments) async {
  10 + await initSherpaOnnx();
  11 +
  12 + final parser = ArgParser()
  13 + ..addOption('model', help: 'Path to the SenseVoice model')
  14 + ..addOption('tokens', help: 'Path to tokens.txt')
  15 + ..addOption('language',
  16 + help: 'auto, zh, en, ja, ko, yue, or leave it empty to use auto',
  17 + defaultsTo: '')
  18 + ..addOption('use-itn',
  19 + help: 'true to use inverse text normalization', defaultsTo: 'false')
  20 + ..addOption('input-wav', help: 'Path to input.wav to transcribe')
  21 + ..addOption('hr-dict-dir',
  22 + help: 'Path to jieba dict for homophone replacer')
  23 + ..addOption('hr-lexicon',
  24 + help: 'Path to lexicon.txt for homophone replacer')
  25 + ..addOption('hr-rule-fsts',
  26 + help: 'Path to replace.fst for homophone replacer');
  27 +
  28 + final res = parser.parse(arguments);
  29 + if (res['model'] == null ||
  30 + res['tokens'] == null ||
  31 + res['hr-dict-dir'] == null ||
  32 + res['hr-lexicon'] == null ||
  33 + res['hr-rule-fsts'] == null ||
  34 + res['input-wav'] == null) {
  35 + print(parser.usage);
  36 + exit(1);
  37 + }
  38 +
  39 + final model = res['model'] as String;
  40 + final tokens = res['tokens'] as String;
  41 + final inputWav = res['input-wav'] as String;
  42 + final language = res['language'] as String;
  43 + final useItn = (res['use-itn'] as String).toLowerCase() == 'true';
  44 + final hrDictDir = res['hr-dict-dir'] as String;
  45 + final hrLexicon = res['hr-lexicon'] as String;
  46 + final hrRuleFsts = res['hr-rule-fsts'] as String;
  47 +
  48 + final senseVoice = sherpa_onnx.OfflineSenseVoiceModelConfig(
  49 + model: model, language: language, useInverseTextNormalization: useItn);
  50 +
  51 + final modelConfig = sherpa_onnx.OfflineModelConfig(
  52 + senseVoice: senseVoice,
  53 + tokens: tokens,
  54 + debug: true,
  55 + numThreads: 1,
  56 + );
  57 +
  58 + final hr = sherpa_onnx.HomophoneReplacerConfig(
  59 + dictDir: hrDictDir, lexicon: hrLexicon, ruleFsts: hrRuleFsts);
  60 +
  61 + final config =
  62 + sherpa_onnx.OfflineRecognizerConfig(model: modelConfig, hr: hr);
  63 +
  64 + final recognizer = sherpa_onnx.OfflineRecognizer(config);
  65 +
  66 + final waveData = sherpa_onnx.readWave(inputWav);
  67 + final stream = recognizer.createStream();
  68 +
  69 + stream.acceptWaveform(
  70 + samples: waveData.samples, sampleRate: waveData.sampleRate);
  71 + recognizer.decode(stream);
  72 +
  73 + final result = recognizer.getResult(stream);
  74 + print(result.text);
  75 +
  76 + stream.free();
  77 + recognizer.free();
  78 +}
  1 +#!/usr/bin/env bash
  2 +
  3 +set -ex
  4 +
  5 +dart pub get
  6 +
  7 +if [ ! -f ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt ]; then
  8 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
  9 + tar xvf sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
  10 + rm sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17.tar.bz2
  11 +fi
  12 +
  13 +if [ ! -d dict ]; then
  14 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/hr-files/dict.tar.bz2
  15 + tar xf dict.tar.bz2
  16 + rm dict.tar.bz2
  17 +
  18 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/hr-files/replace.fst
  19 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/hr-files/test-hr.wav
  20 + curl -SL -O https://github.com/k2-fsa/sherpa-onnx/releases/download/hr-files/lexicon.txt
  21 +fi
  22 +
  23 +dart run \
  24 + ./bin/sense-voice-with-hr.dart \
  25 + --model ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/model.int8.onnx \
  26 + --tokens ./sherpa-onnx-sense-voice-zh-en-ja-ko-yue-2024-07-17/tokens.txt \
  27 + --use-itn true \
  28 + --hr-dict-dir ./dict \
  29 + --hr-lexicon ./lexicon.txt \
  30 + --hr-rule-fsts ./replace.fst \
  31 + --input-wav ./test-hr.wav
@@ -4,6 +4,7 @@ import 'dart:ffi'; @@ -4,6 +4,7 @@ import 'dart:ffi';
4 4
5 export 'src/audio_tagging.dart'; 5 export 'src/audio_tagging.dart';
6 export 'src/feature_config.dart'; 6 export 'src/feature_config.dart';
  7 +export 'src/homophone_replacer_config.dart';
7 export 'src/keyword_spotter.dart'; 8 export 'src/keyword_spotter.dart';
8 export 'src/offline_punctuation.dart'; 9 export 'src/offline_punctuation.dart';
9 export 'src/offline_recognizer.dart'; 10 export 'src/offline_recognizer.dart';
  1 +// Copyright (c) 2025 Xiaomi Corporation
  2 +
  3 +class HomophoneReplacerConfig {
  4 + const HomophoneReplacerConfig(
  5 + {this.dictDir = '', this.lexicon = '', this.ruleFsts = ''});
  6 +
  7 + factory HomophoneReplacerConfig.fromJson(Map<String, dynamic> json) {
  8 + return HomophoneReplacerConfig(
  9 + dictDir: json['dictDir'] as String? ?? '',
  10 + lexicon: json['lexicon'] as String? ?? '',
  11 + ruleFsts: json['ruleFsts'] as String? ?? '',
  12 + );
  13 + }
  14 +
  15 + @override
  16 + String toString() {
  17 + return 'HomophoneReplacerConfig(dictDir: $dictDir, lexicon: $lexicon, ruleFsts: $ruleFsts)';
  18 + }
  19 +
  20 + Map<String, dynamic> toJson() => {
  21 + 'dictDir': dictDir,
  22 + 'lexicon': lexicon,
  23 + 'ruleFsts': ruleFsts,
  24 + };
  25 +
  26 + final String dictDir;
  27 + final String lexicon;
  28 + final String ruleFsts;
  29 +}
@@ -5,6 +5,7 @@ import 'dart:ffi'; @@ -5,6 +5,7 @@ import 'dart:ffi';
5 import 'package:ffi/ffi.dart'; 5 import 'package:ffi/ffi.dart';
6 6
7 import './feature_config.dart'; 7 import './feature_config.dart';
  8 +import './homophone_replacer_config.dart';
8 import './offline_stream.dart'; 9 import './offline_stream.dart';
9 import './sherpa_onnx_bindings.dart'; 10 import './sherpa_onnx_bindings.dart';
10 import './utils.dart'; 11 import './utils.dart';
@@ -403,6 +404,7 @@ class OfflineRecognizerConfig { @@ -403,6 +404,7 @@ class OfflineRecognizerConfig {
403 this.ruleFsts = '', 404 this.ruleFsts = '',
404 this.ruleFars = '', 405 this.ruleFars = '',
405 this.blankPenalty = 0.0, 406 this.blankPenalty = 0.0,
  407 + this.hr = const HomophoneReplacerConfig(),
406 }); 408 });
407 409
408 factory OfflineRecognizerConfig.fromJson(Map<String, dynamic> json) { 410 factory OfflineRecognizerConfig.fromJson(Map<String, dynamic> json) {
@@ -421,12 +423,13 @@ class OfflineRecognizerConfig { @@ -421,12 +423,13 @@ class OfflineRecognizerConfig {
421 ruleFsts: json['ruleFsts'] as String? ?? '', 423 ruleFsts: json['ruleFsts'] as String? ?? '',
422 ruleFars: json['ruleFars'] as String? ?? '', 424 ruleFars: json['ruleFars'] as String? ?? '',
423 blankPenalty: (json['blankPenalty'] as num?)?.toDouble() ?? 0.0, 425 blankPenalty: (json['blankPenalty'] as num?)?.toDouble() ?? 0.0,
  426 + hr: HomophoneReplacerConfig.fromJson(json['hr'] as Map<String, dynamic>),
424 ); 427 );
425 } 428 }
426 429
427 @override 430 @override
428 String toString() { 431 String toString() {
429 - return 'OfflineRecognizerConfig(feat: $feat, model: $model, lm: $lm, decodingMethod: $decodingMethod, maxActivePaths: $maxActivePaths, hotwordsFile: $hotwordsFile, hotwordsScore: $hotwordsScore, ruleFsts: $ruleFsts, ruleFars: $ruleFars, blankPenalty: $blankPenalty)'; 432 + return 'OfflineRecognizerConfig(feat: $feat, model: $model, lm: $lm, decodingMethod: $decodingMethod, maxActivePaths: $maxActivePaths, hotwordsFile: $hotwordsFile, hotwordsScore: $hotwordsScore, ruleFsts: $ruleFsts, ruleFars: $ruleFars, blankPenalty: $blankPenalty, hr: $hr)';
430 } 433 }
431 434
432 Map<String, dynamic> toJson() => { 435 Map<String, dynamic> toJson() => {
@@ -440,6 +443,7 @@ class OfflineRecognizerConfig { @@ -440,6 +443,7 @@ class OfflineRecognizerConfig {
440 'ruleFsts': ruleFsts, 443 'ruleFsts': ruleFsts,
441 'ruleFars': ruleFars, 444 'ruleFars': ruleFars,
442 'blankPenalty': blankPenalty, 445 'blankPenalty': blankPenalty,
  446 + 'hr': hr.toJson(),
443 }; 447 };
444 448
445 final FeatureConfig feat; 449 final FeatureConfig feat;
@@ -457,6 +461,7 @@ class OfflineRecognizerConfig { @@ -457,6 +461,7 @@ class OfflineRecognizerConfig {
457 final String ruleFars; 461 final String ruleFars;
458 462
459 final double blankPenalty; 463 final double blankPenalty;
  464 + final HomophoneReplacerConfig hr;
460 } 465 }
461 466
462 class OfflineRecognizerResult { 467 class OfflineRecognizerResult {
@@ -598,8 +603,15 @@ class OfflineRecognizer { @@ -598,8 +603,15 @@ class OfflineRecognizer {
598 603
599 c.ref.blankPenalty = config.blankPenalty; 604 c.ref.blankPenalty = config.blankPenalty;
600 605
  606 + c.ref.hr.dictDir = config.hr.dictDir.toNativeUtf8();
  607 + c.ref.hr.lexicon = config.hr.lexicon.toNativeUtf8();
  608 + c.ref.hr.ruleFsts = config.hr.ruleFsts.toNativeUtf8();
  609 +
601 final ptr = SherpaOnnxBindings.createOfflineRecognizer?.call(c) ?? nullptr; 610 final ptr = SherpaOnnxBindings.createOfflineRecognizer?.call(c) ?? nullptr;
602 611
  612 + calloc.free(c.ref.hr.dictDir);
  613 + calloc.free(c.ref.hr.lexicon);
  614 + calloc.free(c.ref.hr.ruleFsts);
603 calloc.free(c.ref.ruleFars); 615 calloc.free(c.ref.ruleFars);
604 calloc.free(c.ref.ruleFsts); 616 calloc.free(c.ref.ruleFsts);
605 calloc.free(c.ref.hotwordsFile); 617 calloc.free(c.ref.hotwordsFile);
@@ -5,6 +5,7 @@ import 'dart:ffi'; @@ -5,6 +5,7 @@ import 'dart:ffi';
5 import 'package:ffi/ffi.dart'; 5 import 'package:ffi/ffi.dart';
6 6
7 import './feature_config.dart'; 7 import './feature_config.dart';
  8 +import './homophone_replacer_config.dart';
8 import './online_stream.dart'; 9 import './online_stream.dart';
9 import './sherpa_onnx_bindings.dart'; 10 import './sherpa_onnx_bindings.dart';
10 import './utils.dart'; 11 import './utils.dart';
@@ -194,6 +195,7 @@ class OnlineRecognizerConfig { @@ -194,6 +195,7 @@ class OnlineRecognizerConfig {
194 this.ruleFsts = '', 195 this.ruleFsts = '',
195 this.ruleFars = '', 196 this.ruleFars = '',
196 this.blankPenalty = 0.0, 197 this.blankPenalty = 0.0,
  198 + this.hr = const HomophoneReplacerConfig(),
197 }); 199 });
198 200
199 factory OnlineRecognizerConfig.fromJson(Map<String, dynamic> json) { 201 factory OnlineRecognizerConfig.fromJson(Map<String, dynamic> json) {
@@ -217,12 +219,14 @@ class OnlineRecognizerConfig { @@ -217,12 +219,14 @@ class OnlineRecognizerConfig {
217 ruleFsts: json['ruleFsts'] as String? ?? '', 219 ruleFsts: json['ruleFsts'] as String? ?? '',
218 ruleFars: json['ruleFars'] as String? ?? '', 220 ruleFars: json['ruleFars'] as String? ?? '',
219 blankPenalty: (json['blankPenalty'] as num?)?.toDouble() ?? 0.0, 221 blankPenalty: (json['blankPenalty'] as num?)?.toDouble() ?? 0.0,
  222 + hr: HomophoneReplacerConfig.fromJson(
  223 + json['hr'] as Map<String, dynamic>? ?? const {}),
220 ); 224 );
221 } 225 }
222 226
223 @override 227 @override
224 String toString() { 228 String toString() {
225 - return 'OnlineRecognizerConfig(feat: $feat, model: $model, decodingMethod: $decodingMethod, maxActivePaths: $maxActivePaths, enableEndpoint: $enableEndpoint, rule1MinTrailingSilence: $rule1MinTrailingSilence, rule2MinTrailingSilence: $rule2MinTrailingSilence, rule3MinUtteranceLength: $rule3MinUtteranceLength, hotwordsFile: $hotwordsFile, hotwordsScore: $hotwordsScore, ctcFstDecoderConfig: $ctcFstDecoderConfig, ruleFsts: $ruleFsts, ruleFars: $ruleFars, blankPenalty: $blankPenalty)'; 229 + return 'OnlineRecognizerConfig(feat: $feat, model: $model, decodingMethod: $decodingMethod, maxActivePaths: $maxActivePaths, enableEndpoint: $enableEndpoint, rule1MinTrailingSilence: $rule1MinTrailingSilence, rule2MinTrailingSilence: $rule2MinTrailingSilence, rule3MinUtteranceLength: $rule3MinUtteranceLength, hotwordsFile: $hotwordsFile, hotwordsScore: $hotwordsScore, ctcFstDecoderConfig: $ctcFstDecoderConfig, ruleFsts: $ruleFsts, ruleFars: $ruleFars, blankPenalty: $blankPenalty, hr: $hr)';
226 } 230 }
227 231
228 Map<String, dynamic> toJson() => { 232 Map<String, dynamic> toJson() => {
@@ -240,6 +244,7 @@ class OnlineRecognizerConfig { @@ -240,6 +244,7 @@ class OnlineRecognizerConfig {
240 'ruleFsts': ruleFsts, 244 'ruleFsts': ruleFsts,
241 'ruleFars': ruleFars, 245 'ruleFars': ruleFars,
242 'blankPenalty': blankPenalty, 246 'blankPenalty': blankPenalty,
  247 + 'hr': hr.toJson(),
243 }; 248 };
244 249
245 final FeatureConfig feat; 250 final FeatureConfig feat;
@@ -265,6 +270,7 @@ class OnlineRecognizerConfig { @@ -265,6 +270,7 @@ class OnlineRecognizerConfig {
265 final String ruleFars; 270 final String ruleFars;
266 271
267 final double blankPenalty; 272 final double blankPenalty;
  273 + final HomophoneReplacerConfig hr;
268 } 274 }
269 275
270 class OnlineRecognizerResult { 276 class OnlineRecognizerResult {
@@ -352,8 +358,15 @@ class OnlineRecognizer { @@ -352,8 +358,15 @@ class OnlineRecognizer {
352 358
353 c.ref.blankPenalty = config.blankPenalty; 359 c.ref.blankPenalty = config.blankPenalty;
354 360
  361 + c.ref.hr.dictDir = config.hr.dictDir.toNativeUtf8();
  362 + c.ref.hr.lexicon = config.hr.lexicon.toNativeUtf8();
  363 + c.ref.hr.ruleFsts = config.hr.ruleFsts.toNativeUtf8();
  364 +
355 final ptr = SherpaOnnxBindings.createOnlineRecognizer?.call(c) ?? nullptr; 365 final ptr = SherpaOnnxBindings.createOnlineRecognizer?.call(c) ?? nullptr;
356 366
  367 + calloc.free(c.ref.hr.dictDir);
  368 + calloc.free(c.ref.hr.lexicon);
  369 + calloc.free(c.ref.hr.ruleFsts);
357 calloc.free(c.ref.ruleFars); 370 calloc.free(c.ref.ruleFars);
358 calloc.free(c.ref.ruleFsts); 371 calloc.free(c.ref.ruleFsts);
359 calloc.free(c.ref.ctcFstDecoderConfig.graph); 372 calloc.free(c.ref.ctcFstDecoderConfig.graph);
@@ -353,6 +353,7 @@ final class SherpaOnnxOfflineRecognizerConfig extends Struct { @@ -353,6 +353,7 @@ final class SherpaOnnxOfflineRecognizerConfig extends Struct {
353 353
354 @Float() 354 @Float()
355 external double blankPenalty; 355 external double blankPenalty;
  356 + external SherpaOnnxHomophoneReplacerConfig hr;
356 } 357 }
357 358
358 final class SherpaOnnxOnlineTransducerModelConfig extends Struct { 359 final class SherpaOnnxOnlineTransducerModelConfig extends Struct {
@@ -404,6 +405,12 @@ final class SherpaOnnxOnlineCtcFstDecoderConfig extends Struct { @@ -404,6 +405,12 @@ final class SherpaOnnxOnlineCtcFstDecoderConfig extends Struct {
404 external int maxActive; 405 external int maxActive;
405 } 406 }
406 407
  408 +final class SherpaOnnxHomophoneReplacerConfig extends Struct {
  409 + external Pointer<Utf8> dictDir;
  410 + external Pointer<Utf8> lexicon;
  411 + external Pointer<Utf8> ruleFsts;
  412 +}
  413 +
407 final class SherpaOnnxOnlineRecognizerConfig extends Struct { 414 final class SherpaOnnxOnlineRecognizerConfig extends Struct {
408 external SherpaOnnxFeatureConfig feat; 415 external SherpaOnnxFeatureConfig feat;
409 external SherpaOnnxOnlineModelConfig model; 416 external SherpaOnnxOnlineModelConfig model;
@@ -441,6 +448,7 @@ final class SherpaOnnxOnlineRecognizerConfig extends Struct { @@ -441,6 +448,7 @@ final class SherpaOnnxOnlineRecognizerConfig extends Struct {
441 448
442 @Int32() 449 @Int32()
443 external int hotwordsBufSize; 450 external int hotwordsBufSize;
  451 + external SherpaOnnxHomophoneReplacerConfig hr;
444 } 452 }
445 453
446 final class SherpaOnnxSileroVadModelConfig extends Struct { 454 final class SherpaOnnxSileroVadModelConfig extends Struct {