use crate::audio_recorder::AudioRecorderPlugin;
use crate::call::CallPlugin;
use crate::camera::CameraPlugin;
use crate::chat::ChatPlugin;
use crate::cognitive_output_audio::AudioOutputPlugin;
use crate::document::{DocumentProviderPlugin, DocumentsPlugin};
use crate::gui::GuiPlugin;
use crate::interaction::{InteractionObserver, RetrospectiveConsolidationPlugin};
use crate::peer_input_audio::AudioInputPlugin;
use crate::rollout::RolloutController;
use crate::speech_to_text::{DiarizationPlugin, STTPlugin, TTSPlugin};
use async_trait::async_trait;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub struct PluginInitContext<'a> {
llm_executor: crate::llm::LlmExecutor,
plugin_config: &'a serde_json::Value,
storage: crate::storage::StorageHandle,
plugin_namespace: &'a str,
credentials: crate::credentials::CredentialsHandle,
}
impl<'a> PluginInitContext<'a> {
#[doc = " Internal constructor used by the Core AI engine."]
#[doc(hidden)]
pub fn new(
llm_executor: crate::llm::LlmExecutor,
plugin_config: &'a serde_json::Value,
storage: crate::storage::StorageHandle,
plugin_namespace: &'a str,
credentials: crate::credentials::CredentialsHandle,
) -> Self {
Self {
llm_executor,
plugin_config,
storage,
plugin_namespace,
credentials,
}
}
pub fn credentials(&self) -> crate::credentials::CredentialsHandle {
self.credentials.clone()
}
#[doc = " Deserializes the raw JSON configuration into the plugin's requested config struct."]
#[doc = ""]
#[doc = " **Note on Serde Configuration Defaults:**"]
#[doc = " This performs strict structural deserialization. If the JSON object provided by the"]
#[doc = " `ConfigProvider` is missing a field that your Rust struct (which must derive `Deserialize`) expects, `serde` will"]
#[doc = " return a `missing field` error — even if your struct implements `Default`."]
#[doc = ""]
#[doc = " To make a configuration field optional, use the `#[serde(default)]`"]
#[doc = " attribute on the struct field. This instructs `serde` to fall back to `Default::default()`"]
#[doc = " when the key is omitted."]
pub fn config<C: serde::de::DeserializeOwned>(&self) -> Result<C, String> {
serde_json::from_value(self.plugin_config.clone())
.map_err(|e| format!("Failed to parse plugin config: {}", e))
}
#[doc = " Extracts the configuration if present, or returns `None` if the configuration is completely empty or null."]
#[doc = " This allows plugins to have a mandatory configuration schema when provided, but remain optional overall."]
pub fn optional_config<C: serde::de::DeserializeOwned>(&self) -> Result<Option<C>, String> {
if self.plugin_config.is_null()
|| self.plugin_config.as_object().is_some_and(|m| m.is_empty())
{
return Ok(None);
}
self.config().map(Some)
}
#[doc = " Initializes and returns a shared database connection scoped strictly to this plugin's namespace."]
pub async fn store<S: crate::storage::StorageConnection>(
&self,
) -> Result<std::sync::Arc<S>, String> {
self.storage.connect_store::<S>(self.plugin_namespace).await
}
pub fn llm_executor(&self) -> crate::llm::LlmExecutor {
self.llm_executor.clone()
}
}
pub trait PluginRegistry {
fn register_gui<P: GuiPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_audio_input<P: AudioInputPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_audio_output<P: AudioOutputPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_stt<P: STTPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_tts<P: TTSPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_diarization<P: DiarizationPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_chat<P: ChatPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_documents<P: DocumentsPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_document_provider<P: DocumentProviderPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_interaction_observer<P: InteractionObserver>(&mut self, plugin: std::sync::Arc<P>);
fn register_rollout_controller<P: RolloutController>(&mut self, plugin: std::sync::Arc<P>);
fn register_retrospective_consolidation<P: RetrospectiveConsolidationPlugin>(
&mut self,
plugin: std::sync::Arc<P>,
);
fn register_camera<P: CameraPlugin>(&mut self, plugin: std::sync::Arc<P>);
fn register_context_provider<P: crate::context::IntoContextProvider>(&mut self, provider: P);
fn register_command<C: crate::command::Command>(&mut self, command: C);
#[doc = " Registers a static tool implementation."]
fn register_tool<T: crate::tool::Tool>(&mut self, tool: T);
#[doc = " Registers a type-erased dynamic tool handle (e.g. dynamically discovered at runtime)."]
fn register_erased_tool(&mut self, tool: crate::tool::ToolHandle);
fn register_call<P: CallPlugin>(
&mut self,
plugin: std::sync::Arc<P>,
capability: Option<&'static str>,
);
fn register_recorder<P: AudioRecorderPlugin>(&mut self, plugin: std::sync::Arc<P>);
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EmptyPluginConfig {}
#[async_trait]
pub trait Plugin: Send + Sync + 'static {
#[doc = " Compile-time semantic description of this plugin's capability for the LLM."]
const CAPABILITY: Option<&'static str> = None;
#[doc = " This is the method for instantiating plugins, allowing them to await"]
#[doc = " their database connections (via `context.store::<S>().await`) before returning."]
#[doc = ""]
#[doc = " **Note on Configuration:** Configuration in Synapto plugins is entirely optional."]
#[doc = " If your plugin requires no configuration, simply do not define a config struct"]
#[doc = " and do not call `context.config()?`."]
#[doc = ""]
#[doc = " If you do define a config struct: when calling `context.config()?` to extract your configuration,"]
#[doc = " ensure any optional fields in your struct are marked with `#[serde(default)]`. Otherwise,"]
#[doc = " omitted fields in the config file will cause strict deserialization errors."]
#[doc = " Alternatively, use `context.optional_config()?` to allow the entire config block to be safely omitted."]
async fn create(context: &crate::plugin::PluginInitContext<'_>) -> Result<Self, String>
where
Self: Sized;
fn register<R: PluginRegistry + ?Sized>(self: std::sync::Arc<Self>, registry: &mut R)
where
Self: Sized;
}
#[doc = " An opaque channel identifier used to route messages within the system."]
#[derive(Serialize, Deserialize, JsonSchema, PartialEq, Eq, Debug, Clone)]
pub struct MessageChannel {
#[doc = " Opaque JSON context provided by plugins or core modules."]
pub context: serde_json::Value,
}