hzeng412 Claude Fable 5 commited on
Commit
2e4bd5d
·
1 Parent(s): 21dbad4

Bundle Qwen3-ASR weights in packaged app; slim PyInstaller assets

Browse files

- qwen.py resolves model from env var > bundled assets dir > HF download
- PyInstaller hook: include assets/models/asr/qwen3-asr-1.7b, exclude
legacy funasr/whisper models and superseded TTS .bin weights
(weights are not committed; copy to assets/models/asr/qwen3-asr-1.7b
before packaging)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

build/pyinstaller/hooks/hook-voice_dialogue.py CHANGED
@@ -24,8 +24,29 @@ ASSETS_ROOT = PROJECT_ROOT / "assets"
24
  # 收集主模块的所有子模块
25
  hiddenimports = collect_submodules('voice_dialogue')
26
  datas = collect_data_files('moyoyo_tts', include_py_files=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  # 收集系统资源文件
28
- datas += collect_system_data_files(ASSETS_ROOT.as_posix(), "assets")
 
 
 
 
29
 
30
  # ============================================================================
31
  # 第三方依赖配置
@@ -39,6 +60,7 @@ ML_DEPENDENCIES = [
39
  "pytorch_lightning",
40
  "huggingface_hub",
41
  "einops",
 
42
  ]
43
 
44
  # 语音处理相关依赖
@@ -117,6 +139,7 @@ DATA_PACKAGES = [
117
  ("spacy", {"include_py_files": True}),
118
  ("misaki", {}),
119
  ("silero_vad", {}),
 
120
  ]
121
 
122
  # 收集数据文件
 
24
  # 收集主模块的所有子模块
25
  hiddenimports = collect_submodules('voice_dialogue')
26
  datas = collect_data_files('moyoyo_tts', include_py_files=True)
27
+
28
+ # 不打包的资源:
29
+ # - 旧版 FunASR/Whisper 模型(默认引擎为内置的 Qwen3-ASR)
30
+ # - TTS 预训练权重的 .bin(已内置等价的 model.safetensors)
31
+ EXCLUDED_ASSET_PATTERNS = [
32
+ "assets/models/asr/funasr/",
33
+ "assets/models/asr/whisper/",
34
+ "chinese-roberta-wwm-ext-large/pytorch_model.bin",
35
+ "chinese-hubert-base/pytorch_model.bin",
36
+ ]
37
+
38
+
39
+ def _is_excluded(source_path: str) -> bool:
40
+ normalized = source_path.replace("\\", "/")
41
+ return any(pattern in normalized for pattern in EXCLUDED_ASSET_PATTERNS)
42
+
43
+
44
  # 收集系统资源文件
45
+ datas += [
46
+ (source, dest)
47
+ for source, dest in collect_system_data_files(ASSETS_ROOT.as_posix(), "assets")
48
+ if not _is_excluded(source)
49
+ ]
50
 
51
  # ============================================================================
52
  # 第三方依赖配置
 
60
  "pytorch_lightning",
61
  "huggingface_hub",
62
  "einops",
63
+ "qwen_asr",
64
  ]
65
 
66
  # 语音处理相关依赖
 
139
  ("spacy", {"include_py_files": True}),
140
  ("misaki", {}),
141
  ("silero_vad", {}),
142
+ ("qwen_asr", {}),
143
  ]
144
 
145
  # 收集数据文件
src/voice_dialogue/asr/models/qwen.py CHANGED
@@ -8,13 +8,25 @@ from qwen_asr import Qwen3ASRModel
8
  from voice_dialogue.asr.manager import asr_tables
9
  from voice_dialogue.asr.models.base import ASRInterface
10
  from voice_dialogue.asr.utils import ensure_minimum_audio_duration
 
11
  from voice_dialogue.utils.logger import logger
12
 
13
- DEFAULT_MODEL = os.environ.get('QWEN_ASR_MODEL', 'Qwen/Qwen3-ASR-1.7B')
 
14
 
15
  TARGET_SAMPLE_RATE = 16000
16
 
17
 
 
 
 
 
 
 
 
 
 
 
18
  @asr_tables.register('asr_classes', 'qwen')
19
  class QwenASRClient(ASRInterface):
20
  """Qwen3-ASR 客户端(transformers 后端,macOS 上使用 MPS 加速)"""
@@ -25,7 +37,7 @@ class QwenASRClient(ASRInterface):
25
  self.model: typing.Optional[Qwen3ASRModel] = None
26
 
27
  def setup(self, **kwargs) -> None:
28
- model_name = kwargs.get('model', DEFAULT_MODEL)
29
 
30
  if torch.backends.mps.is_available():
31
  device_map, dtype = 'mps', torch.bfloat16
 
8
  from voice_dialogue.asr.manager import asr_tables
9
  from voice_dialogue.asr.models.base import ASRInterface
10
  from voice_dialogue.asr.utils import ensure_minimum_audio_duration
11
+ from voice_dialogue.config import paths
12
  from voice_dialogue.utils.logger import logger
13
 
14
+ # 内置模型目录(打包分发时随应用携带,存在则离线加载)
15
+ BUILTIN_QWEN_ASR_MODEL_PATH = paths.ASR_MODELS_PATH / 'qwen3-asr-1.7b'
16
 
17
  TARGET_SAMPLE_RATE = 16000
18
 
19
 
20
+ def resolve_model_path() -> str:
21
+ """模型来源优先级:环境变量 > 内置目录 > HuggingFace 自动下载。"""
22
+ env_model = os.environ.get('QWEN_ASR_MODEL')
23
+ if env_model:
24
+ return env_model
25
+ if (BUILTIN_QWEN_ASR_MODEL_PATH / 'config.json').exists():
26
+ return BUILTIN_QWEN_ASR_MODEL_PATH.as_posix()
27
+ return 'Qwen/Qwen3-ASR-1.7B'
28
+
29
+
30
  @asr_tables.register('asr_classes', 'qwen')
31
  class QwenASRClient(ASRInterface):
32
  """Qwen3-ASR 客户端(transformers 后端,macOS 上使用 MPS 加速)"""
 
37
  self.model: typing.Optional[Qwen3ASRModel] = None
38
 
39
  def setup(self, **kwargs) -> None:
40
+ model_name = kwargs.get('model') or resolve_model_path()
41
 
42
  if torch.backends.mps.is_available():
43
  device_map, dtype = 'mps', torch.bfloat16