mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-18 20:05:05 -04:00
chore: local ai initialize
This commit is contained in:
parent
c89f33e2f8
commit
77fbf0f8a3
8 changed files with 98 additions and 60 deletions
2
frontend/rust-lib/Cargo.lock
generated
2
frontend/rust-lib/Cargo.lock
generated
|
@ -2609,6 +2609,7 @@ name = "flowy-core"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"af-local-ai",
|
||||
"af-plugin",
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"base64 0.21.5",
|
||||
|
@ -2966,6 +2967,7 @@ dependencies = [
|
|||
"collab-plugins",
|
||||
"collab-user",
|
||||
"dotenv",
|
||||
"flowy-ai",
|
||||
"flowy-ai-pub",
|
||||
"flowy-database-pub",
|
||||
"flowy-document-pub",
|
||||
|
|
|
@ -99,6 +99,8 @@ zip = "2.2.0"
|
|||
dashmap = "6.0.1"
|
||||
derive_builder = "0.20.2"
|
||||
tantivy = { version = "0.24.0" }
|
||||
af-plugin = { version = "0.1" }
|
||||
af-local-ai = { version = "0.1" }
|
||||
|
||||
# Please using the following command to update the revision id
|
||||
# Current directory: frontend
|
||||
|
|
|
@ -35,8 +35,8 @@ serde_json = { workspace = true }
|
|||
anyhow = "1.0.86"
|
||||
tokio-stream = "0.1.15"
|
||||
tokio-util = { workspace = true, features = ["full"] }
|
||||
af-local-ai = { version = "0.1.0" }
|
||||
af-plugin = { version = "0.1.0" }
|
||||
af-local-ai = { workspace = true }
|
||||
af-plugin = { workspace = true }
|
||||
reqwest = { version = "0.11.27", features = ["json"] }
|
||||
sha2 = "0.10.7"
|
||||
base64 = "0.21.5"
|
||||
|
|
|
@ -5,7 +5,7 @@ pub mod ai_manager;
|
|||
mod chat;
|
||||
mod completion;
|
||||
pub mod entities;
|
||||
mod local_ai;
|
||||
pub mod local_ai;
|
||||
|
||||
// #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
|
||||
// pub mod mcp;
|
||||
|
|
|
@ -24,10 +24,10 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::json;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::select;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{debug, error, info, instrument};
|
||||
use tracing::{debug, error, info, instrument, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -53,7 +53,7 @@ pub struct LocalAIController {
|
|||
ai_plugin: Arc<OllamaAIPlugin>,
|
||||
resource: Arc<LocalAIResourceController>,
|
||||
current_chat_id: ArcSwapOption<Uuid>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
store_preferences: Weak<KVStorePreferences>,
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
#[allow(dead_code)]
|
||||
cloud_service: Arc<dyn ChatCloudService>,
|
||||
|
@ -70,7 +70,7 @@ impl Deref for LocalAIController {
|
|||
impl LocalAIController {
|
||||
pub fn new(
|
||||
plugin_manager: Arc<PluginManager>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
store_preferences: Weak<KVStorePreferences>,
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
cloud_service: Arc<dyn ChatCloudService>,
|
||||
) -> Self {
|
||||
|
@ -94,7 +94,7 @@ impl LocalAIController {
|
|||
let mut running_state_rx = local_ai.subscribe_running_state();
|
||||
|
||||
let cloned_llm_res = Arc::clone(&local_ai_resource);
|
||||
let cloned_store_preferences = Arc::clone(&store_preferences);
|
||||
let cloned_store_preferences = store_preferences.clone();
|
||||
let cloned_local_ai = Arc::clone(&local_ai);
|
||||
let cloned_user_service = Arc::clone(&user_service);
|
||||
|
||||
|
@ -110,44 +110,47 @@ impl LocalAIController {
|
|||
info!("[AI Plugin] state: {:?}", state);
|
||||
|
||||
// Read whether plugin is enabled from store; default to true
|
||||
let enabled = cloned_store_preferences.get_bool(&key).unwrap_or(true);
|
||||
if let Some(store_preferences) = cloned_store_preferences.upgrade() {
|
||||
let enabled = store_preferences.get_bool(&key).unwrap_or(true);
|
||||
// Only check resource status if the plugin isn’t in "UnexpectedStop" and is enabled
|
||||
let (plugin_downloaded, lack_of_resource) =
|
||||
if !matches!(state, RunningState::UnexpectedStop { .. }) && enabled {
|
||||
// Possibly check plugin readiness and resource concurrency in parallel,
|
||||
// but here we do it sequentially for clarity.
|
||||
let downloaded = is_plugin_ready();
|
||||
let resource_lack = cloned_llm_res.get_lack_of_resource().await;
|
||||
(downloaded, resource_lack)
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
|
||||
// Only check resource status if the plugin isn’t in "UnexpectedStop" and is enabled
|
||||
let (plugin_downloaded, lack_of_resource) =
|
||||
if !matches!(state, RunningState::UnexpectedStop { .. }) && enabled {
|
||||
// Possibly check plugin readiness and resource concurrency in parallel,
|
||||
// but here we do it sequentially for clarity.
|
||||
let downloaded = is_plugin_ready();
|
||||
let resource_lack = cloned_llm_res.get_lack_of_resource().await;
|
||||
(downloaded, resource_lack)
|
||||
// If plugin is running, retrieve version
|
||||
let plugin_version = if matches!(state, RunningState::Running { .. }) {
|
||||
match cloned_local_ai.plugin_info().await {
|
||||
Ok(info) => Some(info.version),
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
(false, None)
|
||||
None
|
||||
};
|
||||
|
||||
// If plugin is running, retrieve version
|
||||
let plugin_version = if matches!(state, RunningState::Running { .. }) {
|
||||
match cloned_local_ai.plugin_info().await {
|
||||
Ok(info) => Some(info.version),
|
||||
Err(_) => None,
|
||||
}
|
||||
// Broadcast the new local AI state
|
||||
let new_state = RunningStatePB::from(state);
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateLocalAIState,
|
||||
)
|
||||
.payload(LocalAIPB {
|
||||
enabled,
|
||||
plugin_downloaded,
|
||||
lack_of_resource,
|
||||
state: new_state,
|
||||
plugin_version,
|
||||
})
|
||||
.send();
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Broadcast the new local AI state
|
||||
let new_state = RunningStatePB::from(state);
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateLocalAIState,
|
||||
)
|
||||
.payload(LocalAIPB {
|
||||
enabled,
|
||||
plugin_downloaded,
|
||||
lack_of_resource,
|
||||
state: new_state,
|
||||
plugin_version,
|
||||
})
|
||||
.send();
|
||||
warn!("[AI Plugin] store preferences is dropped");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -207,6 +210,13 @@ impl LocalAIController {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn upgrade_store_preferences(&self) -> FlowyResult<Arc<KVStorePreferences>> {
|
||||
self
|
||||
.store_preferences
|
||||
.upgrade()
|
||||
.ok_or_else(|| FlowyError::internal().with_context("Store preferences is dropped"))
|
||||
}
|
||||
|
||||
/// Indicate whether the local AI plugin is running.
|
||||
pub fn is_running(&self) -> bool {
|
||||
if !self.is_enabled() {
|
||||
|
@ -228,7 +238,10 @@ impl LocalAIController {
|
|||
.workspace_id()
|
||||
.map(|workspace_id| local_ai_enabled_key(&workspace_id))
|
||||
{
|
||||
self.store_preferences.get_bool(&key).unwrap_or(false)
|
||||
match self.upgrade_store_preferences() {
|
||||
Ok(store) => store.get_bool(&key).unwrap_or(false),
|
||||
Err(_) => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -373,8 +386,9 @@ impl LocalAIController {
|
|||
pub async fn toggle_local_ai(&self) -> FlowyResult<bool> {
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let key = local_ai_enabled_key(&workspace_id);
|
||||
let enabled = !self.store_preferences.get_bool(&key).unwrap_or(true);
|
||||
self.store_preferences.set_bool(&key, enabled)?;
|
||||
let store_preferences = self.upgrade_store_preferences()?;
|
||||
let enabled = !store_preferences.get_bool(&key).unwrap_or(true);
|
||||
store_preferences.set_bool(&key, enabled)?;
|
||||
self.toggle_plugin(enabled).await?;
|
||||
Ok(enabled)
|
||||
}
|
||||
|
@ -591,7 +605,16 @@ async fn initialize_ai_plugin(
|
|||
pub struct LLMResourceServiceImpl {
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
cloud_service: Arc<dyn ChatCloudService>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
store_preferences: Weak<KVStorePreferences>,
|
||||
}
|
||||
|
||||
impl LLMResourceServiceImpl {
|
||||
fn upgrade_store_preferences(&self) -> FlowyResult<Arc<KVStorePreferences>> {
|
||||
self
|
||||
.store_preferences
|
||||
.upgrade()
|
||||
.ok_or_else(|| FlowyError::internal().with_context("Store preferences is dropped"))
|
||||
}
|
||||
}
|
||||
#[async_trait]
|
||||
impl LLMResourceService for LLMResourceServiceImpl {
|
||||
|
@ -605,16 +628,14 @@ impl LLMResourceService for LLMResourceServiceImpl {
|
|||
}
|
||||
|
||||
fn store_setting(&self, setting: LocalAISetting) -> Result<(), Error> {
|
||||
self
|
||||
.store_preferences
|
||||
.set_object(LOCAL_AI_SETTING_KEY, &setting)?;
|
||||
let store_preferences = self.upgrade_store_preferences()?;
|
||||
store_preferences.set_object(LOCAL_AI_SETTING_KEY, &setting)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn retrieve_setting(&self) -> Option<LocalAISetting> {
|
||||
self
|
||||
.store_preferences
|
||||
.get_object::<LocalAISetting>(LOCAL_AI_SETTING_KEY)
|
||||
let store_preferences = self.upgrade_store_preferences().ok()?;
|
||||
store_preferences.get_object::<LocalAISetting>(LOCAL_AI_SETTING_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,8 @@ flowy-storage-pub = { workspace = true }
|
|||
client-api.workspace = true
|
||||
flowy-ai = { workspace = true }
|
||||
flowy-ai-pub = { workspace = true }
|
||||
af-local-ai = { version = "0.1.0" }
|
||||
af-local-ai = { workspace = true }
|
||||
af-plugin = { workspace = true }
|
||||
|
||||
|
||||
tracing.workspace = true
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use crate::AppFlowyCoreConfig;
|
||||
use af_plugin::manager::PluginManager;
|
||||
use arc_swap::ArcSwapOption;
|
||||
use dashmap::DashMap;
|
||||
use diesel::Connection;
|
||||
use serde_repr::*;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use flowy_ai::ai_manager::AIUserService;
|
||||
use flowy_ai::local_ai::controller::LocalAIController;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_server::af_cloud::define::ServerUser;
|
||||
use flowy_server::af_cloud::AppFlowyCloudServer;
|
||||
|
@ -14,8 +12,10 @@ use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl};
|
|||
use flowy_server_pub::AuthenticatorType;
|
||||
use flowy_sqlite::kv::KVStorePreferences;
|
||||
use flowy_user_pub::entities::*;
|
||||
|
||||
use crate::AppFlowyCoreConfig;
|
||||
use serde_repr::*;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
|
@ -66,10 +66,21 @@ impl ServerProvider {
|
|||
config: AppFlowyCoreConfig,
|
||||
server: Server,
|
||||
store_preferences: Weak<KVStorePreferences>,
|
||||
user_service: impl AIUserService,
|
||||
server_user: impl ServerUser + 'static,
|
||||
) -> Self {
|
||||
let user = Arc::new(server_user);
|
||||
let encryption = EncryptionImpl::new(None);
|
||||
|
||||
let user_service = Arc::new(user_service);
|
||||
let plugin_manager = Arc::new(PluginManager::new());
|
||||
let local_ai = Arc::new(LocalAIController::new(
|
||||
plugin_manager.clone(),
|
||||
store_preferences.clone(),
|
||||
user_service.clone(),
|
||||
chat_cloud_service.clone(),
|
||||
));
|
||||
|
||||
Self {
|
||||
config,
|
||||
providers: DashMap::new(),
|
||||
|
|
|
@ -44,6 +44,7 @@ tokio-stream = { workspace = true, features = ["sync"] }
|
|||
rand = "0.8.5"
|
||||
semver = "1.0.23"
|
||||
flowy-sqlite = { workspace = true }
|
||||
flowy-ai = { workspace = true }
|
||||
|
||||
[dependencies.client-api]
|
||||
workspace = true
|
||||
|
|
Loading…
Add table
Reference in a new issue