use everruns_host::HostComposition;
use std::sync::Arc;
use everruns_host::{
InProcessRuntime, InProcessRuntimeBuilder, RealDiskSessionFileSystemFactory,
SessionFileSystemFactory, SessionFileSystemFactoryContext,
};
use everruns_provider::error::Result;
use everruns_provider::model_spec::ModelSpec;
use super::backends::LocalBackends;
use super::profile::LocalProfile;
pub fn local_capability_registry() -> everruns_core::CapabilityRegistry {
let registry = everruns_platform::capabilities::hosted_capability_registry();
everruns_host::compose_runtime_capability_registry(registry)
}
pub struct LocalRuntimeBuilder {
profile: LocalProfile,
inner: InProcessRuntimeBuilder,
file_system_factory: Option<Arc<dyn SessionFileSystemFactory>>,
host_composition: Option<HostComposition>,
}
impl LocalRuntimeBuilder {
pub fn new(profile: LocalProfile) -> Self {
Self {
profile,
inner: InProcessRuntimeBuilder::new(),
file_system_factory: None,
host_composition: None,
}
}
pub fn host_composition(mut self, host_composition: HostComposition) -> Self {
self.host_composition = Some(host_composition);
self
}
pub fn provider_with_default_model(
mut self,
provider: everruns_provider::runtime_provider::Provider,
model_id: impl Into<String>,
) -> Self {
self.inner = self.inner.provider_with_default_model(provider, model_id);
self
}
pub fn default_model(mut self, model: ModelSpec) -> Self {
self.inner = self.inner.default_model(model);
self
}
pub fn harness(mut self, harness: everruns_host::SeededHarness) -> Self {
self.inner = self.inner.harness(harness);
self
}
pub fn agent(mut self, agent: everruns_core::AgentDefinition) -> Self {
self.inner = self.inner.agent(agent);
self
}
pub fn session(mut self, session: everruns_core::ExecutionSession) -> Self {
self.inner = self.inner.session(session);
self
}
pub fn session_file_system_factory(
mut self,
factory: Arc<dyn SessionFileSystemFactory>,
) -> Self {
self.file_system_factory = Some(factory);
self
}
pub fn inner_mut(&mut self) -> &mut InProcessRuntimeBuilder {
&mut self.inner
}
pub async fn build(self) -> Result<(InProcessRuntime, LocalBackends)> {
self.profile
.ensure_dirs()
.map_err(|e| everruns_provider::error::AgentLoopError::config(e.to_string()))?;
let local = LocalBackends::new(
self.profile.clone(),
everruns_host::HostBackends::in_memory(),
)?;
let host_composition = match self.host_composition {
Some(pd) => pd,
None => {
let factory = self.file_system_factory.unwrap_or_else(|| {
Arc::new(RealDiskSessionFileSystemFactory::new(
self.profile.workspace_root.clone(),
))
});
HostComposition::builder()
.capability_registry(local_capability_registry())
.driver_registry(everruns_provider::DriverRegistry::new())
.egress_service(everruns_host::runtime_egress_service())
.session_file_system_factory(factory)
.build()
}
};
let runtime = self
.inner
.host_composition(host_composition)
.session_file_system_factory_context(SessionFileSystemFactoryContext::new())
.backends(local.runtime_backends.clone())
.build()
.await?;
Ok((runtime, local))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_local_registry_keeps_core_and_full_portable_catalogs() {
let registry = local_capability_registry();
for capability_id in [
"session_schedule",
"current_time",
"compaction",
"tool_call_repair",
"usage_limit_auto_continue",
] {
assert!(
registry.has(capability_id),
"local registry is missing `{capability_id}`"
);
}
}
}