use anyhow::Result;
use sacp::link::{AgentToClient, ConductorToProxy, ProxyToConductor};
use sacp::{Component, DynComponent};
use sacp_conductor::{Conductor, McpBridgeMode};
use std::path::PathBuf;
struct SymposiumConfig {
ferris: Option<symposium_ferris::Ferris>,
cargo: bool,
sparkle: bool,
trace_dir: Option<PathBuf>,
}
impl SymposiumConfig {
fn new() -> Self {
SymposiumConfig {
sparkle: true,
ferris: Some(symposium_ferris::Ferris::default()),
cargo: true,
trace_dir: None,
}
}
}
pub struct Symposium {
config: SymposiumConfig,
}
impl Symposium {
pub fn new() -> Self {
Symposium {
config: SymposiumConfig::new(),
}
}
pub fn sparkle(mut self, enable: bool) -> Self {
self.config.sparkle = enable;
self
}
pub fn ferris(mut self, config: Option<symposium_ferris::Ferris>) -> Self {
self.config.ferris = config;
self
}
pub fn cargo(mut self, enable: bool) -> Self {
self.config.cargo = enable;
self
}
pub fn trace_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.config.trace_dir = Some(dir.into());
self
}
pub fn with_agent(self, agent: impl Component<AgentToClient>) -> SymposiumAgent {
let Symposium { config } = self;
SymposiumAgent::new(config, agent)
}
}
impl Component<ProxyToConductor> for Symposium {
async fn serve(self, client: impl Component<ConductorToProxy>) -> Result<(), sacp::Error> {
tracing::debug!("Symposium::serve starting (proxy mode)");
let Self { config } = self;
let ferris = config.ferris;
let cargo = config.cargo;
let sparkle = config.sparkle;
let trace_dir = config.trace_dir;
tracing::debug!("Creating conductor (proxy mode)");
let mut conductor = Conductor::new_proxy(
"symposium",
move |init_req| async move {
tracing::info!("Building proxy chain based on capabilities");
let mut proxies: Vec<DynComponent<ProxyToConductor>> = vec![];
if let Some(ferris_config) = ferris {
proxies.push(DynComponent::new(symposium_ferris::FerrisComponent::new(
ferris_config,
)));
}
if cargo {
proxies.push(DynComponent::new(symposium_cargo::CargoProxy));
}
if sparkle {
proxies.push(DynComponent::new(sparkle::SparkleComponent::new()));
}
Ok((init_req, proxies))
},
McpBridgeMode::default(),
);
if let Some(dir) = trace_dir {
std::fs::create_dir_all(&dir).map_err(sacp::Error::into_internal_error)?;
let timestamp = chrono::Utc::now().format("%Y%m%d-%H%M%S");
let trace_path = dir.join(format!("{}.jsons", timestamp));
conductor = conductor
.trace_to_path(&trace_path)
.map_err(sacp::Error::into_internal_error)?;
tracing::info!("Tracing to {}", trace_path.display());
}
tracing::debug!("Starting conductor.run()");
conductor.run(client).await
}
}
pub struct SymposiumAgent {
config: SymposiumConfig,
agent: DynComponent<AgentToClient>,
}
impl SymposiumAgent {
fn new<C: Component<AgentToClient>>(config: SymposiumConfig, agent: C) -> Self {
SymposiumAgent {
config,
agent: DynComponent::new(agent),
}
}
}
impl Component<AgentToClient> for SymposiumAgent {
async fn serve(
self,
client: impl Component<sacp::link::ClientToAgent>,
) -> Result<(), sacp::Error> {
tracing::debug!("SymposiumAgent::serve starting (agent mode)");
let Self { config, agent } = self;
let ferris = config.ferris;
let cargo = config.cargo;
let sparkle = config.sparkle;
let trace_dir = config.trace_dir;
tracing::debug!("Creating conductor (agent mode)");
let mut conductor = Conductor::new_agent(
"symposium",
move |init_req| async move {
tracing::info!("Building proxy chain based on capabilities");
let mut proxies: Vec<DynComponent<ProxyToConductor>> = vec![];
if let Some(ferris_config) = ferris {
proxies.push(DynComponent::new(symposium_ferris::FerrisComponent::new(
ferris_config,
)));
}
if cargo {
proxies.push(DynComponent::new(symposium_cargo::CargoProxy));
}
if sparkle {
proxies.push(DynComponent::new(sparkle::SparkleComponent::new()));
}
Ok((init_req, proxies, agent))
},
McpBridgeMode::default(),
);
if let Some(dir) = trace_dir {
std::fs::create_dir_all(&dir).map_err(sacp::Error::into_internal_error)?;
let timestamp = chrono::Utc::now().format("%Y%m%d-%H%M%S");
let trace_path = dir.join(format!("{}.jsons", timestamp));
conductor = conductor
.trace_to_path(&trace_path)
.map_err(sacp::Error::into_internal_error)?;
tracing::info!("Tracing to {}", trace_path.display());
}
tracing::debug!("Starting conductor.run()");
conductor.run(client).await
}
}