feat(local-asr): 模型状态透出目录元数据、新增中断下载清理命令与 Zipformer 目录项
LocalAsrModelStatus 补齐 display_name/family/mode/languages/size_bytes, 前端无需为基础元数据实时访问 HuggingFace;新增 local_asr_cleanup_incomplete 命令(LocalAsrApi 默认实现 + Service 实现 + 前端包装)清理 staging 目录; sherpa 目录新增 zipformer-bilingual-zh-en-streaming 流式中英模型并补齐八语文案。 修复测试里 LocalAsrTarget::parse 的 .into() 类型推断歧义。
This commit is contained in:
1 parent
bd5656b5fc
commit
786e4b4d07
13 files changed
+135
-1
No files matched your search
@@ -280,6 +280,12 @@ pub trait LocalAsrApi: Send + Sync {
|
||||
fn release(&self, runtime: LocalAsrRuntime) -> BoxFuture<'static, Result<(), BackendError>>;
|
||||
fn preload(&self, runtime: LocalAsrRuntime) -> BoxFuture<'static, Result<(), BackendError>>;
|
||||
fn delete_model(&self, target: LocalAsrTarget) -> BoxFuture<'static, Result<(), BackendError>>;
|
||||
fn cleanup_incomplete(
|
||||
&self,
|
||||
target: LocalAsrTarget,
|
||||
) -> BoxFuture<'static, Result<(), BackendError>> {
|
||||
unsupported("local ASR incomplete download cleanup")
|
||||
}
|
||||
fn model_dir(
|
||||
&self,
|
||||
target: LocalAsrTarget,
|
||||
|
||||
@@ -1254,6 +1254,14 @@ impl LocalAsrApi for LocalAsrService {
|
||||
})
|
||||
}
|
||||
|
||||
fn cleanup_incomplete(
|
||||
&self,
|
||||
target: LocalAsrTarget,
|
||||
) -> BoxFuture<'static, Result<(), BackendError>> {
|
||||
let store = Arc::clone(&self.model_store);
|
||||
Box::pin(async move { store.cleanup_incomplete(&target) })
|
||||
}
|
||||
|
||||
fn model_dir(
|
||||
&self,
|
||||
target: LocalAsrTarget,
|
||||
|
||||
@@ -76,12 +76,19 @@ impl From<openless_core::LocalAsrStorageSettings> for LocalAsrStorageSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// 与 Sherpa `SherpaCatalogWire` 对齐:透出展示名、家族、模式、语言与远端尺寸,
|
||||
/// 前端无需再为基础元数据实时访问 HuggingFace。
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LocalAsrModelStatus {
|
||||
pub id: String,
|
||||
pub hf_repo: String,
|
||||
pub display_name: String,
|
||||
pub family: String,
|
||||
pub mode: Option<String>,
|
||||
pub languages: Vec<String>,
|
||||
pub downloaded_bytes: u64,
|
||||
pub size_bytes: Option<u64>,
|
||||
pub is_downloaded: bool,
|
||||
}
|
||||
|
||||
@@ -89,8 +96,13 @@ impl From<openless_core::LocalAsrModel> for LocalAsrModelStatus {
|
||||
fn from(model: openless_core::LocalAsrModel) -> Self {
|
||||
Self {
|
||||
id: model.target.model_id().to_string(),
|
||||
hf_repo: model.repository.unwrap_or_default(),
|
||||
hf_repo: model.repository.clone().unwrap_or_default(),
|
||||
display_name: model.display_name,
|
||||
family: model.family,
|
||||
mode: model.mode,
|
||||
languages: model.languages,
|
||||
downloaded_bytes: model.downloaded_bytes,
|
||||
size_bytes: model.size_bytes,
|
||||
is_downloaded: model.installed,
|
||||
}
|
||||
}
|
||||
@@ -355,6 +367,20 @@ pub async fn local_asr_delete_model(
|
||||
.map_err(core_error)
|
||||
}
|
||||
|
||||
/// 清理指定模型中断下载遗留的 staging 目录,不触碰已安装模型。
|
||||
#[tauri::command]
|
||||
pub async fn local_asr_cleanup_incomplete(
|
||||
backend: CoreState<'_>,
|
||||
model_id: String,
|
||||
) -> Result<(), String> {
|
||||
backend
|
||||
.services()
|
||||
.local_asr
|
||||
.cleanup_incomplete(target(LocalAsrRuntime::Generic, model_id)?)
|
||||
.await
|
||||
.map_err(core_error)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn local_asr_model_dir(
|
||||
backend: CoreState<'_>,
|
||||
@@ -489,4 +515,40 @@ mod wire_contract_tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_model_status_keeps_the_legacy_fields_and_adds_catalog_metadata() {
|
||||
let core = openless_core::LocalAsrModel {
|
||||
target: openless_core::LocalAsrTarget::parse(
|
||||
openless_core::LocalAsrRuntime::Generic,
|
||||
"qwen3-asr-0.6b",
|
||||
)
|
||||
.unwrap(),
|
||||
display_name: "Qwen3 ASR 0.6B".into(),
|
||||
family: "qwen3_asr".into(),
|
||||
mode: None,
|
||||
repository: Some("Qwen/Qwen3-ASR-0.6B".into()),
|
||||
languages: vec!["zh".into(), "en".into()],
|
||||
installed: false,
|
||||
downloaded_bytes: 4096,
|
||||
size_bytes: Some(1_234_567),
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(LocalAsrModelStatus::from(core)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
value,
|
||||
serde_json::json!({
|
||||
"id": "qwen3-asr-0.6b",
|
||||
"hfRepo": "Qwen/Qwen3-ASR-0.6B",
|
||||
"displayName": "Qwen3 ASR 0.6B",
|
||||
"family": "qwen3_asr",
|
||||
"mode": null,
|
||||
"languages": ["zh", "en"],
|
||||
"downloadedBytes": 4096,
|
||||
"sizeBytes": 1234567,
|
||||
"isDownloaded": false,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -351,6 +351,7 @@ macro_rules! app_invoke_handler_desktop {
|
||||
commands::local_asr_download_model,
|
||||
commands::local_asr_cancel_download,
|
||||
commands::local_asr_delete_model,
|
||||
commands::local_asr_cleanup_incomplete,
|
||||
commands::local_asr_model_dir,
|
||||
commands::local_asr_reveal_model_dir,
|
||||
commands::local_asr_reveal_models_root,
|
||||
|
||||
@@ -2129,6 +2129,9 @@ export const de: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3 (mehrsprachig)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'Leistungsstärkste mehrsprachige Open-Source-Whisper-Variante: hohe Qualität, großer Download.',
|
||||
sherpaModelZipformer: 'Zipformer-Streaming (zh/en)',
|
||||
sherpaModelZipformerDesc:
|
||||
'Streaming-Modell für Chinesisch und Englisch mit der geringsten Latenz – Text erscheint beim Sprechen.',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'Konvertiertes sherpa-onnx-Qwen3-ASR-Modell mit mehrsprachiger Erkennung und besserer Verarbeitung längerer Kontexte.',
|
||||
|
||||
@@ -2063,6 +2063,9 @@ export const en: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3 (multilingual)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'The best open-source multilingual Whisper tier — high quality, large download.',
|
||||
sherpaModelZipformer: 'Zipformer Streaming (zh/en)',
|
||||
sherpaModelZipformerDesc:
|
||||
'Streaming Chinese-English model with the lowest latency — text appears as you speak.',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'Converted sherpa-onnx Qwen3-ASR model with multilingual recognition and stronger long-form context handling.',
|
||||
|
||||
@@ -2110,6 +2110,9 @@ export const es: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3 (multilingüe)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'La versión multilingüe de código abierto más avanzada de Whisper: gran calidad y descarga de gran tamaño.',
|
||||
sherpaModelZipformer: 'Zipformer en streaming (zh/en)',
|
||||
sherpaModelZipformerDesc:
|
||||
'Modelo en streaming de chino e inglés con la latencia más baja: el texto aparece mientras hablas.',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'Modelo Qwen3-ASR convertido para sherpa-onnx, con reconocimiento multilingüe y mejor tratamiento del contexto en textos largos.',
|
||||
|
||||
@@ -2139,6 +2139,9 @@ export const fr: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3 (multilingue)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'La version multilingue open source la plus avancée de Whisper : grande qualité et téléchargement volumineux.',
|
||||
sherpaModelZipformer: 'Zipformer streaming (zh/en)',
|
||||
sherpaModelZipformerDesc:
|
||||
'Modèle streaming chinois-anglais à la latence la plus faible — le texte apparaît pendant que vous parlez.',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'Modèle Qwen3-ASR converti pour sherpa-onnx, avec reconnaissance multilingue et meilleure gestion du contexte long.',
|
||||
|
||||
@@ -2041,6 +2041,9 @@ export const ja: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3(多言語)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'オープンソース多言語モデルの中で最高品質の Whisper 系。高品質だが大容量。',
|
||||
sherpaModelZipformer: 'Zipformer ストリーミング(中英)',
|
||||
sherpaModelZipformerDesc:
|
||||
'話しながら文字が出るストリーミング型の中英モデル。遅延が最も小さく、リアルタイム文字起こしに適します。',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'変換済み sherpa-onnx Qwen3-ASR モデル。多言語認識とより強い長文コンテキスト処理に対応。',
|
||||
|
||||
@@ -2025,6 +2025,9 @@ export const ko: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3 (다국어)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'오픈소스 다국어 모델 중 품질이 가장 좋은 Whisper 계열. 고품질이지만 용량이 큽니다.',
|
||||
sherpaModelZipformer: 'Zipformer 스트리밍(중·영)',
|
||||
sherpaModelZipformerDesc:
|
||||
'말하는 동안 텍스트가 나오는 스트리밍 중·영어 모델로, 지연이 가장 낮아 실시간 받아쓰기에 적합합니다.',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'변환된 sherpa-onnx Qwen3-ASR 모델로 다국어 인식과 더 강한 긴 문맥 처리를 지원합니다.',
|
||||
|
||||
@@ -1952,6 +1952,9 @@ export const zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3(多语)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'开源多语通用里效果最好的 Whisper 档,质量高、体积大,适合高质量转写。',
|
||||
sherpaModelZipformer: 'Zipformer 流式(中英)',
|
||||
sherpaModelZipformerDesc:
|
||||
'边说边出的流式中英模型,延迟最低,适合实时听写。',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'转换后的 sherpa-onnx Qwen3-ASR 模型,支持多语言识别与更强的长上下文能力。',
|
||||
|
||||
@@ -1938,6 +1938,9 @@ export const zhTW: typeof zhCN = {
|
||||
sherpaModelWhisperLargeV3: 'Whisper Large V3(多語)',
|
||||
sherpaModelWhisperLargeV3Desc:
|
||||
'開源多語通用中效果最好的 Whisper 檔,品質高、體積大,適合高品質轉寫。',
|
||||
sherpaModelZipformer: 'Zipformer 串流(中英)',
|
||||
sherpaModelZipformerDesc:
|
||||
'邊說邊出的串流中英模型,延遲最低,適合即時聽寫。',
|
||||
sherpaModelQwen3: 'Qwen3-ASR 0.6B INT8',
|
||||
sherpaModelQwen3Desc:
|
||||
'轉換後的 sherpa-onnx Qwen3-ASR 模型,支援多語言識別與更強的長上下文能力。',
|
||||
|
||||
@@ -37,7 +37,12 @@ export interface LocalAsrStorageSettings {
|
||||
export interface LocalAsrModelStatus {
|
||||
id: string;
|
||||
hfRepo: string;
|
||||
displayName: string;
|
||||
family: string;
|
||||
mode: string | null;
|
||||
languages: string[];
|
||||
downloadedBytes: number;
|
||||
sizeBytes: number | null;
|
||||
isDownloaded: boolean;
|
||||
}
|
||||
|
||||
@@ -212,13 +217,23 @@ const MOCK_MODELS: LocalAsrModelStatus[] = [
|
||||
{
|
||||
id: 'qwen3-asr-0.6b',
|
||||
hfRepo: 'Qwen/Qwen3-ASR-0.6B',
|
||||
displayName: 'Qwen3 ASR 0.6B',
|
||||
family: 'qwen3_asr',
|
||||
mode: null,
|
||||
languages: ['zh', 'en', 'ja', 'ko', 'yue'],
|
||||
downloadedBytes: 0,
|
||||
sizeBytes: null,
|
||||
isDownloaded: false,
|
||||
},
|
||||
{
|
||||
id: 'qwen3-asr-1.7b',
|
||||
hfRepo: 'Qwen/Qwen3-ASR-1.7B',
|
||||
displayName: 'Qwen3 ASR 1.7B',
|
||||
family: 'qwen3_asr',
|
||||
mode: null,
|
||||
languages: ['zh', 'en', 'ja', 'ko', 'yue'],
|
||||
downloadedBytes: 0,
|
||||
sizeBytes: null,
|
||||
isDownloaded: false,
|
||||
},
|
||||
];
|
||||
@@ -302,6 +317,11 @@ export function deleteLocalAsrModel(modelId: string): Promise<void> {
|
||||
return invokeOrMock('local_asr_delete_model', { modelId }, () => undefined);
|
||||
}
|
||||
|
||||
/** 清理中断下载遗留的 staging 目录,不触碰已安装模型。 */
|
||||
export function cleanupIncompleteLocalAsrModel(modelId: string): Promise<void> {
|
||||
return invokeOrMock('local_asr_cleanup_incomplete', { modelId }, () => undefined);
|
||||
}
|
||||
|
||||
export function getLocalAsrModelDir(modelId: string): Promise<string> {
|
||||
return invokeOrMock('local_asr_model_dir', { modelId }, () => '');
|
||||
}
|
||||
@@ -422,6 +442,7 @@ export type SherpaOnnxModelAlias =
|
||||
| 'paraformer-zh'
|
||||
| 'whisper-small-multi'
|
||||
| 'whisper-large-v3-multi'
|
||||
| 'zipformer-bilingual-zh-en-streaming'
|
||||
| 'qwen3-asr-0.6b-int8';
|
||||
|
||||
export type SherpaOnnxMirror = 'huggingface' | 'hf-mirror' | 'github-release';
|
||||
@@ -470,6 +491,11 @@ export const SHERPA_ONNX_ASR_MODELS: SherpaOnnxModelOption[] = [
|
||||
labelKey: 'localAsr.sherpaModelWhisperLargeV3',
|
||||
descKey: 'localAsr.sherpaModelWhisperLargeV3Desc',
|
||||
},
|
||||
{
|
||||
alias: 'zipformer-bilingual-zh-en-streaming',
|
||||
labelKey: 'localAsr.sherpaModelZipformer',
|
||||
descKey: 'localAsr.sherpaModelZipformerDesc',
|
||||
},
|
||||
{
|
||||
alias: 'qwen3-asr-0.6b-int8',
|
||||
labelKey: 'localAsr.sherpaModelQwen3',
|
||||
@@ -518,6 +544,13 @@ export function getSherpaOnnxAsrCatalog(): Promise<SherpaOnnxCatalogModel[]> {
|
||||
downloadedBytes: 0,
|
||||
fileSizeMb: 1700,
|
||||
},
|
||||
{
|
||||
alias: 'zipformer-bilingual-zh-en-streaming' as const,
|
||||
displayName: 'Zipformer Streaming (zh/en)',
|
||||
cached: false,
|
||||
downloadedBytes: 0,
|
||||
fileSizeMb: 260,
|
||||
},
|
||||
{
|
||||
alias: 'qwen3-asr-0.6b-int8' as const,
|
||||
displayName: 'Qwen3-ASR 0.6B INT8',
|
||||
|
||||
Reference in new issue
Block a user