use std::any::Any;
use std::sync::Arc;
use everruns::ToolResponse;
use futures::future::BoxFuture;
use serde_json::Value;
use crate::connection::McpServer;
use crate::cx::Cx;
use crate::eval::EvalCx;
pub type ToolFuture = BoxFuture<'static, Result<ToolResponse, String>>;
pub struct AgentRegistration {
pub name: &'static str,
pub doc: &'static str,
pub sub: bool,
pub default: bool,
pub source: &'static str,
pub build: fn() -> crate::Agent,
}
#[derive(Clone, Copy)]
pub enum Approval {
Never,
Always,
When(fn(&Value) -> bool),
}
impl Approval {
pub(crate) fn label(&self) -> &'static str {
match self {
Approval::Never => "never",
Approval::Always => "always",
Approval::When(_) => "conditional",
}
}
}
pub struct ToolRegistration {
pub name: &'static str,
pub description: &'static str,
pub source: &'static str,
pub schema: fn() -> Value,
pub approval: Approval,
pub call: fn(Cx, Value) -> ToolFuture,
}
pub struct ChannelRegistration {
pub name: &'static str,
pub source: &'static str,
pub build: fn() -> Box<dyn crate::Channel>,
}
pub struct ScheduleRegistration {
pub name: &'static str,
pub cron: &'static str,
pub source: &'static str,
pub run: fn(Cx) -> BoxFuture<'static, crate::Result>,
}
pub struct ConnectionRegistration {
pub name: &'static str,
pub source: &'static str,
pub build: fn() -> crate::Result<ConnectionValue>,
}
pub struct EvalRegistration {
pub name: &'static str,
pub doc: &'static str,
pub source: &'static str,
pub run: for<'a> fn(&'a mut EvalCx) -> BoxFuture<'a, crate::Result>,
}
#[derive(Debug)]
pub struct AssetRegistration {
pub path: &'static str,
pub contents: &'static str,
pub disk: &'static str,
}
pub struct AppInfoRegistration {
pub name: &'static str,
pub version: &'static str,
}
inventory::collect!(AppInfoRegistration);
inventory::collect!(AgentRegistration);
inventory::collect!(ToolRegistration);
inventory::collect!(ChannelRegistration);
inventory::collect!(ScheduleRegistration);
inventory::collect!(ConnectionRegistration);
inventory::collect!(EvalRegistration);
inventory::collect!(AssetRegistration);
#[derive(Clone)]
pub struct ConnectionValue {
pub(crate) name: &'static str,
pub(crate) type_name: &'static str,
pub(crate) value: Arc<dyn Any + Send + Sync>,
pub(crate) mcp: Option<McpServer>,
}
impl ConnectionValue {
pub fn new<T: Any + Send + Sync>(name: &'static str, value: T) -> Self {
let mcp = (&value as &dyn Any).downcast_ref::<McpServer>().cloned();
Self {
name,
type_name: std::any::type_name::<T>(),
value: Arc::new(value),
mcp,
}
}
}
pub fn parse<T: serde::de::DeserializeOwned>(value: Value) -> Result<T, serde_json::Error> {
serde_json::from_value(value)
}
pub fn schema_for<T: schemars::JsonSchema>() -> Value {
serde_json::to_value(schemars::schema_for!(T)).unwrap_or(Value::Null)
}
pub fn invalid_arguments(tool: &str, err: serde_json::Error) -> Result<ToolResponse, String> {
Ok(ToolResponse::error(format!(
"invalid arguments for tool `{tool}`: {err}"
)))
}
pub fn tool_result<T: serde::Serialize, E: std::fmt::Display>(
out: Result<T, E>,
) -> Result<ToolResponse, String> {
match out {
Ok(value) => tool_value(&value),
Err(err) => Ok(ToolResponse::error(format!("{err:#}"))),
}
}
pub fn tool_value<T: serde::Serialize>(value: &T) -> Result<ToolResponse, String> {
serde_json::to_value(value)
.map(ToolResponse::json)
.map_err(|err| format!("tool result is not serializable: {err}"))
}