@@ -23,6 +23,8 @@ use flowy_ai_pub::persistence::{
2323use flowy_ai_pub:: user_service:: AIUserService ;
2424use futures_util:: SinkExt ;
2525use lib_infra:: util:: get_operating_system;
26+ use ollama_rs:: generation:: embeddings:: request:: { EmbeddingsInput , GenerateEmbeddingsRequest } ;
27+ use ollama_rs:: Ollama ;
2628use serde:: { Deserialize , Serialize } ;
2729use std:: ops:: Deref ;
2830use std:: path:: PathBuf ;
@@ -32,11 +34,6 @@ use tokio_stream::StreamExt;
3234use tracing:: { debug, error, info, instrument, warn} ;
3335use uuid:: Uuid ;
3436
35- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
36- use ollama_rs:: generation:: embeddings:: request:: { EmbeddingsInput , GenerateEmbeddingsRequest } ;
37- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
38- use ollama_rs:: Ollama ;
39-
4037#[ derive( Clone , Debug , Serialize , Deserialize ) ]
4138pub struct LocalAISetting {
4239 pub ollama_server_url : String ,
@@ -62,7 +59,6 @@ pub struct LocalAIController {
6259 current_chat_id : ArcSwapOption < Uuid > ,
6360 store_preferences : Weak < KVStorePreferences > ,
6461 user_service : Arc < dyn AIUserService > ,
65- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
6662 ollama : ArcSwapOption < Ollama > ,
6763}
6864
@@ -95,105 +91,87 @@ impl LocalAIController {
9591 res_impl,
9692 ) ) ;
9793
98- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
99- let ollama = {
100- let mut ollama = ArcSwapOption :: default ( ) ;
101- let sys = get_operating_system ( ) ;
102- if sys. is_desktop ( ) {
103- let setting = local_ai_resource. get_llm_setting ( ) ;
104- ollama. store (
105- Ollama :: try_new ( & setting. ollama_server_url )
106- . map ( Arc :: new)
107- . ok ( ) ,
108- ) ;
109- }
110- ollama
111- } ;
112-
113- // Subscribe to state changes
114- let mut running_state_rx = local_ai. subscribe_running_state ( ) ;
115- let cloned_llm_res = Arc :: clone ( & local_ai_resource) ;
116- let cloned_store_preferences = store_preferences. clone ( ) ;
117- let cloned_local_ai = Arc :: clone ( & local_ai) ;
118- let cloned_user_service = Arc :: clone ( & user_service) ;
94+ let ollama = ArcSwapOption :: default ( ) ;
95+ let sys = get_operating_system ( ) ;
96+ if sys. is_desktop ( ) {
97+ let setting = local_ai_resource. get_llm_setting ( ) ;
98+ ollama. store (
99+ Ollama :: try_new ( & setting. ollama_server_url )
100+ . map ( Arc :: new)
101+ . ok ( ) ,
102+ ) ;
119103
120- // Spawn a background task to listen for plugin state changes
121- tokio:: spawn ( async move {
122- while let Some ( state) = running_state_rx. next ( ) . await {
123- // Skip if we can't get workspace_id
124- let Ok ( workspace_id) = cloned_user_service. workspace_id ( ) else {
125- continue ;
126- } ;
104+ // Subscribe to state changes
105+ let mut running_state_rx = local_ai. subscribe_running_state ( ) ;
106+ let cloned_llm_res = Arc :: clone ( & local_ai_resource) ;
107+ let cloned_store_preferences = store_preferences. clone ( ) ;
108+ let cloned_local_ai = Arc :: clone ( & local_ai) ;
109+ let cloned_user_service = Arc :: clone ( & user_service) ;
110+
111+ // Spawn a background task to listen for plugin state changes
112+ tokio:: spawn ( async move {
113+ while let Some ( state) = running_state_rx. next ( ) . await {
114+ // Skip if we can't get workspace_id
115+ let Ok ( workspace_id) = cloned_user_service. workspace_id ( ) else {
116+ continue ;
117+ } ;
127118
128- let key = crate :: local_ai:: controller:: local_ai_enabled_key ( & workspace_id. to_string ( ) ) ;
129- info ! ( "[AI Plugin] state: {:?}" , state) ;
130-
131- // Read whether plugin is enabled from store; default to true
132- if let Some ( store_preferences) = cloned_store_preferences. upgrade ( ) {
133- let enabled = store_preferences. get_bool ( & key) . unwrap_or ( true ) ;
134- // Only check resource status if the plugin isn't in "UnexpectedStop" and is enabled
135- let ( plugin_downloaded, lack_of_resource) =
136- if !matches ! ( state, RunningState :: UnexpectedStop { .. } ) && enabled {
137- // Possibly check plugin readiness and resource concurrency in parallel,
138- // but here we do it sequentially for clarity.
139- let downloaded = is_plugin_ready ( ) ;
140- let resource_lack = cloned_llm_res. get_lack_of_resource ( ) . await ;
141- ( downloaded, resource_lack)
119+ let key = crate :: local_ai:: controller:: local_ai_enabled_key ( & workspace_id. to_string ( ) ) ;
120+ info ! ( "[AI Plugin] state: {:?}" , state) ;
121+
122+ // Read whether plugin is enabled from store; default to true
123+ if let Some ( store_preferences) = cloned_store_preferences. upgrade ( ) {
124+ let enabled = store_preferences. get_bool ( & key) . unwrap_or ( true ) ;
125+ // Only check resource status if the plugin isn't in "UnexpectedStop" and is enabled
126+ let ( plugin_downloaded, lack_of_resource) =
127+ if !matches ! ( state, RunningState :: UnexpectedStop { .. } ) && enabled {
128+ // Possibly check plugin readiness and resource concurrency in parallel,
129+ // but here we do it sequentially for clarity.
130+ let downloaded = is_plugin_ready ( ) ;
131+ let resource_lack = cloned_llm_res. get_lack_of_resource ( ) . await ;
132+ ( downloaded, resource_lack)
133+ } else {
134+ ( false , None )
135+ } ;
136+
137+ // If plugin is running, retrieve version
138+ let plugin_version = if matches ! ( state, RunningState :: Running { .. } ) {
139+ match cloned_local_ai. plugin_info ( ) . await {
140+ Ok ( info) => Some ( info. version ) ,
141+ Err ( _) => None ,
142+ }
142143 } else {
143- ( false , None )
144+ None
144145 } ;
145146
146- // If plugin is running, retrieve version
147- let plugin_version = if matches ! ( state, RunningState :: Running { .. } ) {
148- match cloned_local_ai. plugin_info ( ) . await {
149- Ok ( info) => Some ( info. version ) ,
150- Err ( _) => None ,
151- }
147+ // Broadcast the new local AI state
148+ let new_state = RunningStatePB :: from ( state) ;
149+ chat_notification_builder (
150+ APPFLOWY_AI_NOTIFICATION_KEY ,
151+ ChatNotification :: UpdateLocalAIState ,
152+ )
153+ . payload ( LocalAIPB {
154+ enabled,
155+ plugin_downloaded,
156+ lack_of_resource,
157+ state : new_state,
158+ plugin_version,
159+ } )
160+ . send ( ) ;
152161 } else {
153- None
154- } ;
155-
156- // Broadcast the new local AI state
157- let new_state = RunningStatePB :: from ( state) ;
158- chat_notification_builder (
159- APPFLOWY_AI_NOTIFICATION_KEY ,
160- ChatNotification :: UpdateLocalAIState ,
161- )
162- . payload ( LocalAIPB {
163- enabled,
164- plugin_downloaded,
165- lack_of_resource,
166- state : new_state,
167- plugin_version,
168- } )
169- . send ( ) ;
170- } else {
171- warn ! ( "[AI Plugin] store preferences is dropped" ) ;
162+ warn ! ( "[AI Plugin] store preferences is dropped" ) ;
163+ }
172164 }
173- }
174- } ) ;
175-
176- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
177- {
178- Self {
179- ai_plugin : local_ai,
180- resource : local_ai_resource,
181- current_chat_id : ArcSwapOption :: default ( ) ,
182- store_preferences,
183- user_service,
184- ollama,
185- }
165+ } ) ;
186166 }
187167
188- #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
189- {
190- Self {
191- ai_plugin : local_ai,
192- resource : local_ai_resource,
193- current_chat_id : ArcSwapOption :: default ( ) ,
194- store_preferences,
195- user_service,
196- }
168+ Self {
169+ ai_plugin : local_ai,
170+ resource : local_ai_resource,
171+ current_chat_id : ArcSwapOption :: default ( ) ,
172+ store_preferences,
173+ user_service,
174+ ollama,
197175 }
198176 }
199177 #[ instrument( level = "debug" , skip_all) ]
@@ -329,35 +307,18 @@ impl LocalAIController {
329307 }
330308
331309 pub async fn get_all_chat_local_models ( & self ) -> Vec < AIModel > {
332- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
333- {
334- self
335- . get_filtered_local_models ( |name| !name. contains ( "embed" ) )
336- . await
337- }
338-
339- #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
340- {
341- vec ! [ ]
342- }
310+ self
311+ . get_filtered_local_models ( |name| !name. contains ( "embed" ) )
312+ . await
343313 }
344314
345315 pub async fn get_all_embedded_local_models ( & self ) -> Vec < AIModel > {
346- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
347- {
348- self
349- . get_filtered_local_models ( |name| name. contains ( "embed" ) )
350- . await
351- }
352-
353- #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
354- {
355- vec ! [ ]
356- }
316+ self
317+ . get_filtered_local_models ( |name| name. contains ( "embed" ) )
318+ . await
357319 }
358320
359321 // Helper function to avoid code duplication in model retrieval
360- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
361322 async fn get_filtered_local_models < F > ( & self , filter_fn : F ) -> Vec < AIModel >
362323 where
363324 F : Fn ( & str ) -> bool ,
@@ -383,43 +344,35 @@ impl LocalAIController {
383344 let mut conn = self . user_service . sqlite_connection ( uid) ?;
384345 match select_local_ai_model ( & mut conn, model_name) {
385346 None => {
386- #[ cfg( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ]
387- {
388- let ollama = self
389- . ollama
390- . load_full ( )
391- . ok_or_else ( || FlowyError :: local_ai ( ) . with_context ( "ollama is not initialized" ) ) ?;
392-
393- let request = GenerateEmbeddingsRequest :: new (
394- model_name. to_string ( ) ,
395- EmbeddingsInput :: Single ( "Hello" . to_string ( ) ) ,
396- ) ;
397-
398- let model_type = match ollama. generate_embeddings ( request) . await {
399- Ok ( value) => {
400- if value. embeddings . is_empty ( ) {
401- ModelType :: Chat
402- } else {
403- ModelType :: Embedding
404- }
405- } ,
406- Err ( _) => ModelType :: Chat ,
407- } ;
347+ let ollama = self
348+ . ollama
349+ . load_full ( )
350+ . ok_or_else ( || FlowyError :: local_ai ( ) . with_context ( "ollama is not initialized" ) ) ?;
351+
352+ let request = GenerateEmbeddingsRequest :: new (
353+ model_name. to_string ( ) ,
354+ EmbeddingsInput :: Single ( "Hello" . to_string ( ) ) ,
355+ ) ;
408356
409- upsert_local_ai_model (
410- & mut conn,
411- & LocalAIModelTable {
412- name : model_name. to_string ( ) ,
413- model_type : model_type as i16 ,
414- } ,
415- ) ?;
416- Ok ( model_type)
417- }
357+ let model_type = match ollama. generate_embeddings ( request) . await {
358+ Ok ( value) => {
359+ if value. embeddings . is_empty ( ) {
360+ ModelType :: Chat
361+ } else {
362+ ModelType :: Embedding
363+ }
364+ } ,
365+ Err ( _) => ModelType :: Chat ,
366+ } ;
418367
419- #[ cfg( not( any( target_os = "macos" , target_os = "linux" , target_os = "windows" ) ) ) ]
420- {
421- Ok ( ModelType :: Chat )
422- }
368+ upsert_local_ai_model (
369+ & mut conn,
370+ & LocalAIModelTable {
371+ name : model_name. to_string ( ) ,
372+ model_type : model_type as i16 ,
373+ } ,
374+ ) ?;
375+ Ok ( model_type)
423376 } ,
424377 Some ( r) => Ok ( ModelType :: from ( r. model_type ) ) ,
425378 }
0 commit comments