@@ -14,8 +14,7 @@ use crate::polish::{LLMError, OpenAICompatibleConfig, OpenAICompatibleLLMProvide
1414use crate :: types:: {
1515 ChineseScriptPreference , CredentialsStatus , DictationSession , DictionaryEntry ,
1616 HotkeyCapability , HotkeyStatus , OutputLanguagePreference , PolishMode , QaHotkeyBinding ,
17- UserPreferences ,
18- VocabPresetStore , WindowsImeStatus ,
17+ UserPreferences , VocabPresetStore , WindowsImeStatus ,
1918} ;
2019
2120type CoordinatorState < ' a > = State < ' a , Arc < Coordinator > > ;
@@ -100,16 +99,47 @@ pub fn get_windows_ime_status() -> WindowsImeStatus {
10099#[ tauri:: command]
101100pub fn get_credentials ( ) -> CredentialsStatus {
102101 let snap = CredentialsVault :: snapshot ( ) ;
102+ let active_asr_provider = CredentialsVault :: get_active_asr ( ) ;
103+ let active_llm_provider = CredentialsVault :: get_active_llm ( ) ;
104+ let volcengine_configured = volcengine_configured ( & snap) ;
105+ let asr_configured = asr_configured_for_provider ( & active_asr_provider, & snap) ;
106+ let llm_configured = llm_configured_for_snapshot ( & snap) ;
103107 CredentialsStatus {
104- volcengine_configured : configured ( & snap. volcengine_app_key )
105- && configured ( & snap. volcengine_access_key )
106- && configured ( & snap. volcengine_resource_id ) ,
107- ark_configured : configured ( & snap. ark_api_key ) ,
108+ active_asr_provider,
109+ active_llm_provider,
110+ asr_configured,
111+ llm_configured,
112+ volcengine_configured,
113+ ark_configured : llm_configured,
108114 }
109115}
110116
117+ fn volcengine_configured ( snap : & CredentialsSnapshot ) -> bool {
118+ configured ( & snap. volcengine_app_key )
119+ && configured ( & snap. volcengine_access_key )
120+ && configured ( & snap. volcengine_resource_id )
121+ }
122+
123+ fn asr_configured_for_provider ( provider : & str , snap : & CredentialsSnapshot ) -> bool {
124+ if provider == "volcengine" {
125+ return volcengine_configured ( snap) ;
126+ }
127+ if provider == crate :: asr:: local:: PROVIDER_ID {
128+ // 本地 ASR 不依赖云端凭据。
129+ return true ;
130+ }
131+ configured ( & snap. asr_endpoint ) && configured ( & snap. asr_model )
132+ }
133+
134+ fn llm_configured_for_snapshot ( snap : & CredentialsSnapshot ) -> bool {
135+ configured ( & snap. ark_endpoint ) && configured ( & snap. ark_model_id )
136+ }
137+
111138fn configured ( field : & Option < String > ) -> bool {
112- field. as_ref ( ) . map ( |s| !s. is_empty ( ) ) . unwrap_or ( false )
139+ field
140+ . as_ref ( )
141+ . map ( |s| !s. trim ( ) . is_empty ( ) )
142+ . unwrap_or ( false )
113143}
114144
115145#[ tauri:: command]
@@ -851,9 +881,11 @@ fn _ensure_snapshot_used(_: CredentialsSnapshot) {}
851881#[ cfg( test) ]
852882mod tests {
853883 use super :: {
854- asr_transcriptions_url, fetch_provider_models, models_url, parse_model_ids,
855- persist_settings, ProviderConfig , SettingsWriter ,
884+ asr_configured_for_provider, asr_transcriptions_url, fetch_provider_models,
885+ llm_configured_for_snapshot, models_url, parse_model_ids, persist_settings,
886+ ProviderConfig , SettingsWriter ,
856887 } ;
888+ use crate :: persistence:: CredentialsSnapshot ;
857889 use crate :: types:: {
858890 HotkeyBinding , HotkeyMode , HotkeyTrigger , QaHotkeyBinding , UserPreferences ,
859891 } ;
@@ -869,6 +901,65 @@ mod tests {
869901 qa_refreshes : Mutex < u32 > ,
870902 }
871903
904+ fn snapshot ( ) -> CredentialsSnapshot {
905+ CredentialsSnapshot :: default ( )
906+ }
907+
908+ #[ test]
909+ fn credentials_status_follows_active_asr_provider_requirements ( ) {
910+ let volcengine = CredentialsSnapshot {
911+ volcengine_app_key : Some ( "app" . into ( ) ) ,
912+ volcengine_access_key : Some ( "access" . into ( ) ) ,
913+ volcengine_resource_id : Some ( "resource" . into ( ) ) ,
914+ ..snapshot ( )
915+ } ;
916+ assert ! ( asr_configured_for_provider( "volcengine" , & volcengine) ) ;
917+
918+ let whisper_key_only = CredentialsSnapshot {
919+ asr_api_key : Some ( "key" . into ( ) ) ,
920+ ..snapshot ( )
921+ } ;
922+ assert ! ( !asr_configured_for_provider( "whisper" , & whisper_key_only) ) ;
923+
924+ let whisper_keyless_ready = CredentialsSnapshot {
925+ asr_endpoint : Some ( "https://api.openai.com/v1" . into ( ) ) ,
926+ asr_model : Some ( "whisper-1" . into ( ) ) ,
927+ ..snapshot ( )
928+ } ;
929+ assert ! ( asr_configured_for_provider(
930+ "whisper" ,
931+ & whisper_keyless_ready
932+ ) ) ;
933+
934+ assert ! ( asr_configured_for_provider(
935+ crate :: asr:: local:: PROVIDER_ID ,
936+ & snapshot( )
937+ ) ) ;
938+ }
939+
940+ #[ test]
941+ fn credentials_status_accepts_keyless_llm_with_endpoint_and_model ( ) {
942+ let keyless_ready = CredentialsSnapshot {
943+ ark_endpoint : Some ( "http://localhost:11434/v1" . into ( ) ) ,
944+ ark_model_id : Some ( "qwen" . into ( ) ) ,
945+ ..snapshot ( )
946+ } ;
947+ assert ! ( llm_configured_for_snapshot( & keyless_ready) ) ;
948+
949+ let key_without_endpoint = CredentialsSnapshot {
950+ ark_api_key : Some ( "key" . into ( ) ) ,
951+ ark_model_id : Some ( "qwen" . into ( ) ) ,
952+ ..snapshot ( )
953+ } ;
954+ assert ! ( !llm_configured_for_snapshot( & key_without_endpoint) ) ;
955+
956+ let endpoint_without_model = CredentialsSnapshot {
957+ ark_endpoint : Some ( "http://localhost:11434/v1" . into ( ) ) ,
958+ ..snapshot ( )
959+ } ;
960+ assert ! ( !llm_configured_for_snapshot( & endpoint_without_model) ) ;
961+ }
962+
872963 impl SettingsWriter for FakeSettingsWriter {
873964 fn write_settings ( & self , prefs : UserPreferences ) -> Result < ( ) , String > {
874965 * self . saved . lock ( ) . unwrap ( ) = Some ( prefs) ;
0 commit comments