@@ -296,6 +296,20 @@ async fn begin_session(inner: &Arc<Inner>) -> Result<(), String> {
296296 return Ok ( ( ) ) ;
297297 }
298298
299+ if let Err ( message) = ensure_asr_credentials ( ) {
300+ log:: warn!( "[coord] ASR credential gate failed: {message}" ) ;
301+ emit_capsule (
302+ inner,
303+ CapsuleState :: Error ,
304+ 0.0 ,
305+ 0 ,
306+ Some ( message. clone ( ) ) ,
307+ None ,
308+ ) ;
309+ inner. state . lock ( ) . phase = SessionPhase :: Idle ;
310+ return Err ( message) ;
311+ }
312+
299313 if let Err ( message) = ensure_microphone_permission ( inner) {
300314 log:: warn!( "[coord] microphone permission gate failed: {message}" ) ;
301315 emit_capsule (
@@ -460,6 +474,35 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
460474 } ,
461475 } ;
462476
477+ if raw. text . trim ( ) . is_empty ( ) {
478+ let session = DictationSession {
479+ id : Uuid :: new_v4 ( ) . to_string ( ) ,
480+ created_at : Utc :: now ( ) . to_rfc3339 ( ) ,
481+ raw_transcript : raw. text . clone ( ) ,
482+ final_text : String :: new ( ) ,
483+ mode : inner. prefs . get ( ) . default_mode ,
484+ app_bundle_id : None ,
485+ app_name : None ,
486+ insert_status : InsertStatus :: Failed ,
487+ error_code : Some ( "emptyTranscript" . to_string ( ) ) ,
488+ duration_ms : Some ( raw. duration_ms ) ,
489+ dictionary_entry_count : Some ( enabled_phrases ( inner) . len ( ) as u32 ) ,
490+ } ;
491+ if let Err ( e) = inner. history . append ( session) {
492+ log:: error!( "[coord] history append failed: {e}" ) ;
493+ }
494+ emit_capsule (
495+ inner,
496+ CapsuleState :: Error ,
497+ 0.0 ,
498+ elapsed,
499+ Some ( "ASR returned empty transcript" . to_string ( ) ) ,
500+ None ,
501+ ) ;
502+ inner. state . lock ( ) . phase = SessionPhase :: Idle ;
503+ return Err ( "ASR returned empty transcript" . to_string ( ) ) ;
504+ }
505+
463506 emit_capsule ( inner, CapsuleState :: Polishing , 0.0 , elapsed, None , None ) ;
464507
465508 let prefs = inner. prefs . get ( ) ;
@@ -569,6 +612,27 @@ fn ensure_microphone_permission(inner: &Arc<Inner>) -> Result<(), String> {
569612 }
570613}
571614
615+ fn ensure_asr_credentials ( ) -> Result < ( ) , String > {
616+ let active_asr = CredentialsVault :: get_active_asr ( ) ;
617+ if active_asr == "whisper" {
618+ let api_key = CredentialsVault :: get ( CredentialAccount :: AsrApiKey )
619+ . ok ( )
620+ . flatten ( )
621+ . unwrap_or_default ( ) ;
622+ if api_key. trim ( ) . is_empty ( ) {
623+ return Err ( "请先在设置中填写 Whisper ASR API Key" . to_string ( ) ) ;
624+ }
625+ return Ok ( ( ) ) ;
626+ }
627+
628+ let creds = read_volc_credentials ( ) ;
629+ if creds. app_id . trim ( ) . is_empty ( ) || creds. access_token . trim ( ) . is_empty ( ) {
630+ Err ( "请先在设置中填写火山引擎 ASR App Key 和 Access Key" . to_string ( ) )
631+ } else {
632+ Ok ( ( ) )
633+ }
634+ }
635+
572636async fn polish_or_passthrough (
573637 raw : & RawTranscript ,
574638 mode : PolishMode ,
0 commit comments