@@ -61,16 +61,19 @@ pub struct LanguageModels {
6161 model_list : acp_thread:: AgentModelList ,
6262 refresh_models_rx : watch:: Receiver < ( ) > ,
6363 refresh_models_tx : watch:: Sender < ( ) > ,
64+ _authenticate_all_providers_task : Task < ( ) > ,
6465}
6566
6667impl LanguageModels {
67- fn new ( cx : & App ) -> Self {
68+ fn new ( cx : & mut App ) -> Self {
6869 let ( refresh_models_tx, refresh_models_rx) = watch:: channel ( ( ) ) ;
70+
6971 let mut this = Self {
7072 models : HashMap :: default ( ) ,
7173 model_list : acp_thread:: AgentModelList :: Grouped ( IndexMap :: default ( ) ) ,
7274 refresh_models_rx,
7375 refresh_models_tx,
76+ _authenticate_all_providers_task : Self :: authenticate_all_language_model_providers ( cx) ,
7477 } ;
7578 this. refresh_list ( cx) ;
7679 this
@@ -150,6 +153,52 @@ impl LanguageModels {
150153 fn model_id ( model : & Arc < dyn LanguageModel > ) -> acp_thread:: AgentModelId {
151154 acp_thread:: AgentModelId ( format ! ( "{}/{}" , model. provider_id( ) . 0 , model. id( ) . 0 ) . into ( ) )
152155 }
156+
157+ fn authenticate_all_language_model_providers ( cx : & mut App ) -> Task < ( ) > {
158+ let authenticate_all_providers = LanguageModelRegistry :: global ( cx)
159+ . read ( cx)
160+ . providers ( )
161+ . iter ( )
162+ . map ( |provider| ( provider. id ( ) , provider. name ( ) , provider. authenticate ( cx) ) )
163+ . collect :: < Vec < _ > > ( ) ;
164+
165+ cx. background_spawn ( async move {
166+ for ( provider_id, provider_name, authenticate_task) in authenticate_all_providers {
167+ if let Err ( err) = authenticate_task. await {
168+ if matches ! ( err, language_model:: AuthenticateError :: CredentialsNotFound ) {
169+ // Since we're authenticating these providers in the
170+ // background for the purposes of populating the
171+ // language selector, we don't care about providers
172+ // where the credentials are not found.
173+ } else {
174+ // Some providers have noisy failure states that we
175+ // don't want to spam the logs with every time the
176+ // language model selector is initialized.
177+ //
178+ // Ideally these should have more clear failure modes
179+ // that we know are safe to ignore here, like what we do
180+ // with `CredentialsNotFound` above.
181+ match provider_id. 0 . as_ref ( ) {
182+ "lmstudio" | "ollama" => {
183+ // LM Studio and Ollama both make fetch requests to the local APIs to determine if they are "authenticated".
184+ //
185+ // These fail noisily, so we don't log them.
186+ }
187+ "copilot_chat" => {
188+ // Copilot Chat returns an error if Copilot is not enabled, so we don't log those errors.
189+ }
190+ _ => {
191+ log:: error!(
192+ "Failed to authenticate provider: {}: {err}" ,
193+ provider_name. 0
194+ ) ;
195+ }
196+ }
197+ }
198+ }
199+ }
200+ } )
201+ }
153202}
154203
155204pub struct NativeAgent {
0 commit comments