mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-27 07:04:08 +00:00
chore: show plugin version
This commit is contained in:
parent
34a858e948
commit
af5c4bfe76
@ -23,7 +23,10 @@ class PluginStateIndicator extends StatelessWidget {
|
||||
unknown: () => const SizedBox.shrink(),
|
||||
readToRun: () => const _PrepareRunning(),
|
||||
initializingPlugin: () => const InitLocalAIIndicator(),
|
||||
running: (version) => _LocalAIRunning(version: version),
|
||||
running: (version) => _LocalAIRunning(
|
||||
key: ValueKey(version),
|
||||
version: version,
|
||||
),
|
||||
restartPlugin: () => const _RestartPluginButton(),
|
||||
lackOfResource: (desc) => _LackOfResource(desc: desc),
|
||||
);
|
||||
@ -84,7 +87,7 @@ class _RestartPluginButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _LocalAIRunning extends StatelessWidget {
|
||||
const _LocalAIRunning({required this.version});
|
||||
const _LocalAIRunning({required this.version, super.key});
|
||||
|
||||
final String version;
|
||||
|
||||
@ -111,13 +114,17 @@ class _LocalAIRunning extends StatelessWidget {
|
||||
color: Color(0xFF2E7D32),
|
||||
),
|
||||
const HSpace(6),
|
||||
if (version.isNotEmpty)
|
||||
Flexible(
|
||||
child: FlowyText(
|
||||
"($version) ",
|
||||
fontSize: 11,
|
||||
color: const Color(0xFF1E4620),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: FlowyText(
|
||||
LocaleKeys.settings_aiPage_keys_localAIRunning.tr(
|
||||
args: [
|
||||
version,
|
||||
],
|
||||
),
|
||||
LocaleKeys.settings_aiPage_keys_localAIRunning.tr(),
|
||||
fontSize: 11,
|
||||
color: const Color(0xFF1E4620),
|
||||
maxLines: 3,
|
||||
|
||||
@ -857,7 +857,7 @@
|
||||
"localAIStart": "بدأت الدردشة المحلية بالذكاء الاصطناعي...",
|
||||
"localAILoading": "جاري تحميل نموذج الدردشة المحلية للذكاء الاصطناعي...",
|
||||
"localAIStopped": "تم إيقاف الذكاء الاصطناعي المحلي",
|
||||
"localAIRunning": "الذكاء الاصطناعي المحلي قيد التشغيل. الإصدار: {}",
|
||||
"localAIRunning": "الذكاء الاصطناعي المحلي قيد التشغيل",
|
||||
"localAIInitializing": "يتم تهيئة الذكاء الاصطناعي المحلي وقد يستغرق الأمر بضع دقائق، حسب جهازك",
|
||||
"localAINotReadyTextFieldPrompt": "لا يمكنك التحرير أثناء تحميل الذكاء الاصطناعي المحلي",
|
||||
"failToLoadLocalAI": "فشل في بدء تشغيل الذكاء الاصطناعي المحلي",
|
||||
|
||||
@ -857,7 +857,7 @@
|
||||
"localAIStart": "Local AI is starting. If it’s slow, try toggling it off and on",
|
||||
"localAILoading": "Local AI Chat Model is loading...",
|
||||
"localAIStopped": "Local AI stopped",
|
||||
"localAIRunning": "Local AI is running. Version: {}",
|
||||
"localAIRunning": "Local AI is running",
|
||||
"localAIInitializing": "Local AI is loading and may take a few minutes, depending on your device",
|
||||
"localAINotReadyTextFieldPrompt": "You can not edit while Local AI is loading",
|
||||
"failToLoadLocalAI": "Failed to start local AI",
|
||||
|
||||
@ -851,7 +851,7 @@
|
||||
"localAIStart": "로컬 AI가 시작 중입니다. 느리다면 껐다가 다시 켜보세요",
|
||||
"localAILoading": "로컬 AI 채팅 모델이 로드 중입니다...",
|
||||
"localAIStopped": "로컬 AI가 중지되었습니다",
|
||||
"localAIRunning": "로컬 AI가 실행 중입니다. 버전: {}",
|
||||
"localAIRunning": "로컬 AI가 실행 중입니다",
|
||||
"localAIInitializing": "로컬 AI가 로드 중이며 장치에 따라 몇 분이 소요될 수 있습니다",
|
||||
"localAINotReadyTextFieldPrompt": "로컬 AI가 로드되는 동안 편집할 수 없습니다",
|
||||
"failToLoadLocalAI": "로컬 AI를 시작하지 못했습니다",
|
||||
|
||||
@ -77,62 +77,88 @@ impl LocalAIController {
|
||||
"[AI Plugin] init local ai controller, thread: {:?}",
|
||||
std::thread::current().id()
|
||||
);
|
||||
|
||||
// Create the core plugin and resource controller
|
||||
let local_ai = Arc::new(OllamaAIPlugin::new(plugin_manager));
|
||||
let res_impl = LLMResourceServiceImpl {
|
||||
user_service: user_service.clone(),
|
||||
cloud_service: cloud_service.clone(),
|
||||
store_preferences: store_preferences.clone(),
|
||||
};
|
||||
|
||||
let local_ai_resource = Arc::new(LocalAIResourceController::new(
|
||||
user_service.clone(),
|
||||
res_impl,
|
||||
));
|
||||
let current_chat_id = ArcSwapOption::default();
|
||||
// Subscribe to state changes
|
||||
let mut running_state_rx = local_ai.subscribe_running_state();
|
||||
let cloned_llm_res = local_ai_resource.clone();
|
||||
let cloned_store_preferences = store_preferences.clone();
|
||||
let cloned_user_service = user_service.clone();
|
||||
|
||||
let cloned_llm_res = Arc::clone(&local_ai_resource);
|
||||
let cloned_store_preferences = Arc::clone(&store_preferences);
|
||||
let cloned_local_ai = Arc::clone(&local_ai);
|
||||
let cloned_user_service = Arc::clone(&user_service);
|
||||
|
||||
// Spawn a background task to listen for plugin state changes
|
||||
tokio::spawn(async move {
|
||||
while let Some(state) = running_state_rx.next().await {
|
||||
if let Ok(workspace_id) = cloned_user_service.workspace_id() {
|
||||
let key = local_ai_enabled_key(&workspace_id);
|
||||
info!("[AI Plugin] state: {:?}", state);
|
||||
let mut plugin_downloaded = false;
|
||||
let mut lack_of_resource = None;
|
||||
let enabled = cloned_store_preferences.get_bool(&key).unwrap_or(true);
|
||||
if !matches!(state, RunningState::UnexpectedStop { .. }) && enabled {
|
||||
plugin_downloaded = is_plugin_ready();
|
||||
lack_of_resource = cloned_llm_res.get_lack_of_resource().await;
|
||||
}
|
||||
// Skip if we can’t get workspace_id
|
||||
let Ok(workspace_id) = cloned_user_service.workspace_id() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
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: None,
|
||||
})
|
||||
.send();
|
||||
}
|
||||
let key = local_ai_enabled_key(&workspace_id);
|
||||
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);
|
||||
|
||||
// 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)
|
||||
};
|
||||
|
||||
// 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 {
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
ai_plugin: local_ai,
|
||||
resource: local_ai_resource,
|
||||
current_chat_id,
|
||||
current_chat_id: ArcSwapOption::default(),
|
||||
store_preferences,
|
||||
user_service,
|
||||
cloud_service,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn observe_plugin_resource(&self) {
|
||||
debug!(
|
||||
@ -275,22 +301,48 @@ impl LocalAIController {
|
||||
pub async fn get_local_ai_state(&self) -> LocalAIPB {
|
||||
let start = std::time::Instant::now();
|
||||
let enabled = self.is_enabled();
|
||||
let mut plugin_downloaded = false;
|
||||
let mut state = RunningState::ReadyToConnect;
|
||||
let mut lack_of_resource = None;
|
||||
let mut plugin_version = None;
|
||||
if enabled {
|
||||
plugin_downloaded = is_plugin_ready();
|
||||
state = self.ai_plugin.get_plugin_running_state();
|
||||
plugin_version = self.ai_plugin.plugin_info().await.ok().map(|v| v.version);
|
||||
lack_of_resource = self.resource.get_lack_of_resource().await;
|
||||
|
||||
// If not enabled, return immediately.
|
||||
if !enabled {
|
||||
debug!(
|
||||
"[AI Plugin] get local ai state, elapsed: {:?}, thread: {:?}",
|
||||
start.elapsed(),
|
||||
std::thread::current().id()
|
||||
);
|
||||
return LocalAIPB {
|
||||
enabled: false,
|
||||
plugin_downloaded: false,
|
||||
state: RunningStatePB::from(RunningState::ReadyToConnect),
|
||||
lack_of_resource: None,
|
||||
plugin_version: None,
|
||||
};
|
||||
}
|
||||
|
||||
let plugin_downloaded = is_plugin_ready();
|
||||
let state = self.ai_plugin.get_plugin_running_state();
|
||||
|
||||
// If the plugin is running, run both requests in parallel.
|
||||
// Otherwise, only fetch the resource info.
|
||||
let (plugin_version, lack_of_resource) = if matches!(state, RunningState::Running { .. }) {
|
||||
// Launch both futures at once
|
||||
let plugin_info_fut = self.ai_plugin.plugin_info();
|
||||
let resource_fut = self.resource.get_lack_of_resource();
|
||||
|
||||
let (plugin_info_res, resource_res) = tokio::join!(plugin_info_fut, resource_fut);
|
||||
let plugin_version = plugin_info_res.ok().map(|info| info.version);
|
||||
(plugin_version, resource_res)
|
||||
} else {
|
||||
let resource_res = self.resource.get_lack_of_resource().await;
|
||||
(None, resource_res)
|
||||
};
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
debug!(
|
||||
"[AI Plugin] get local ai state, elapsed: {:?}, thread: {:?}",
|
||||
elapsed,
|
||||
std::thread::current().id()
|
||||
);
|
||||
|
||||
LocalAIPB {
|
||||
enabled,
|
||||
plugin_downloaded,
|
||||
@ -299,7 +351,6 @@ impl LocalAIController {
|
||||
plugin_version,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn restart_plugin(&self) {
|
||||
if let Err(err) = initialize_ai_plugin(&self.ai_plugin, &self.resource, None).await {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user