use std::sync::Arc;
use serde_json::Value as JsonValue;
use toolkit::ClientHub;
use toolkit::client_hub::ClientScope;
use toolkit_macros::domain_model;
use tracing::warn;
use uuid::Uuid;
use chat_engine_sdk::plugin::ChatEngineBackendPlugin;
use crate::domain::HealthStatus;
use crate::domain::error::ChatEngineError;
use crate::domain::ports::PluginConfigRepo;
#[domain_model]
#[derive(Clone)]
pub struct PluginService {
client_hub: Arc<ClientHub>,
configs: Arc<dyn PluginConfigRepo>,
}
impl PluginService {
#[must_use]
pub fn new(client_hub: Arc<ClientHub>, configs: Arc<dyn PluginConfigRepo>) -> Self {
Self {
client_hub,
configs,
}
}
pub fn resolve(
&self,
plugin_instance_id: &str,
) -> Result<Arc<dyn ChatEngineBackendPlugin>, ChatEngineError> {
let scope = ClientScope::gts_id(plugin_instance_id);
self.client_hub
.try_get_scoped::<dyn ChatEngineBackendPlugin>(&scope)
.ok_or_else(|| ChatEngineError::not_found("plugin", plugin_instance_id))
}
pub async fn load_config(
&self,
plugin_instance_id: &str,
session_type_id: Uuid,
) -> Result<Option<JsonValue>, ChatEngineError> {
self.configs.find(plugin_instance_id, session_type_id).await
}
pub async fn save_config(
&self,
plugin_instance_id: &str,
session_type_id: Uuid,
config: JsonValue,
) -> Result<(), ChatEngineError> {
self.configs
.upsert(plugin_instance_id, session_type_id, config)
.await
}
pub async fn health_probe(
&self,
plugin_instance_id: &str,
) -> Result<HealthStatus, ChatEngineError> {
let plugin = self.resolve(plugin_instance_id)?;
match plugin.health_check().await {
Ok(HealthStatus::Healthy) => Ok(HealthStatus::Healthy),
Ok(HealthStatus::Degraded) => {
warn!(
plugin_instance_id = %plugin_instance_id,
status = "degraded",
"plugin health check reported degraded; routing remains enabled"
);
Ok(HealthStatus::Degraded)
}
Ok(HealthStatus::Unhealthy) => {
warn!(
plugin_instance_id = %plugin_instance_id,
status = "unhealthy",
"plugin health check reported unhealthy; routing remains enabled (advisory)"
);
Ok(HealthStatus::Unhealthy)
}
Err(err) => {
warn!(
plugin_instance_id = %plugin_instance_id,
status = "unhealthy",
error_status = err.suggested_status(),
"plugin health check returned an error; treating as unhealthy (advisory)"
);
Ok(HealthStatus::Unhealthy)
}
}
}
}
#[cfg(test)]
#[path = "plugin_service_tests.rs"]
mod plugin_service_tests;