use std::fmt;
use std::future::Future;
use std::sync::Arc;
use ferrin_spec::BoxFuture;
use ferrin_spec::JsonValue;
use ferrin_spec::LanguageModelRef;
use ferrin_spec::ToolChoice;
use ferrin_spec::ToolName;
use ferrin_tool::ToolCallers;
use ferrin_tool::ToolSet;
use secrecy::SecretBox;
use super::AgentInput;
use crate::error::Error;
use crate::generate_text::ApprovalPolicy;
use crate::generate_text::Include;
use crate::generate_text::PrepareStep;
use crate::generate_text::RefineToolInputs;
use crate::generate_text::StopCondition;
use crate::generate_text::ToolCallRepair;
use crate::prompt::CallSettings;
use crate::prompt::DownloadFn;
use crate::prompt::Instructions;
use crate::retry::RetryPolicy;
use crate::telemetry::TelemetryOptions;
use crate::timeout::Timeout;
#[derive(Clone)]
pub struct PreparedCall {
pub input: AgentInput,
pub instructions: Option<Instructions>,
pub allow_system_in_messages: bool,
pub model: LanguageModelRef,
pub tools: ToolSet,
pub tool_choice: Option<ToolChoice>,
pub active_tools: Option<Vec<ToolName>>,
pub tool_order: Vec<ToolName>,
pub tools_context: Option<JsonValue>,
pub runtime_context: Option<JsonValue>,
pub tool_approval: Option<Arc<dyn ApprovalPolicy>>,
pub tool_approval_secret: Option<Arc<SecretBox<[u8]>>>,
pub tool_callers: ToolCallers,
pub prepare_step: Option<Arc<dyn PrepareStep>>,
pub repair_tool_call: Option<Arc<dyn ToolCallRepair>>,
pub refine_tool_inputs: RefineToolInputs,
pub download: Option<Arc<dyn DownloadFn>>,
pub settings: CallSettings,
pub stop_conditions: Vec<Arc<dyn StopCondition>>,
pub timeout: Timeout,
pub retry_policy: RetryPolicy,
pub include: Include,
pub max_tool_concurrency: Option<usize>,
pub telemetry: TelemetryOptions,
}
impl fmt::Debug for PreparedCall {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PreparedCall")
.field("input", &self.input)
.field("instructions", &self.instructions)
.field("allow_system_in_messages", &self.allow_system_in_messages)
.field("model", &self.model)
.field("tools", &self.tools.names().collect::<Vec<_>>())
.field("tool_choice", &self.tool_choice)
.field("active_tools", &self.active_tools)
.field("tool_order", &self.tool_order)
.field("tools_context", &self.tools_context)
.field("runtime_context", &self.runtime_context.is_some())
.field("tool_approval", &self.tool_approval.is_some())
.field("tool_approval_secret", &self.tool_approval_secret.is_some())
.field("tool_callers", &self.tool_callers)
.field("prepare_step", &self.prepare_step.is_some())
.field("repair_tool_call", &self.repair_tool_call.is_some())
.field("refine_tool_inputs", &self.refine_tool_inputs)
.field("download", &self.download.is_some())
.field("settings", &self.settings)
.field("stop_conditions", &self.stop_conditions.len())
.field("timeout", &self.timeout)
.field("retry_policy", &self.retry_policy)
.field("include", &self.include)
.field("max_tool_concurrency", &self.max_tool_concurrency)
.finish_non_exhaustive()
}
}
pub struct PrepareCallInput<Opt> {
pub options: Opt,
pub defaults: PreparedCall,
#[cfg(feature = "sandbox")]
pub sandbox: Option<Arc<dyn ferrin_tool::Sandbox>>,
}
impl<Opt: fmt::Debug> fmt::Debug for PrepareCallInput<Opt> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut debug = f.debug_struct("PrepareCallInput");
debug
.field("options", &self.options)
.field("defaults", &self.defaults);
#[cfg(feature = "sandbox")]
debug.field("has_sandbox", &self.sandbox.is_some());
debug.finish()
}
}
pub trait PrepareCall<Opt>: Send + Sync {
fn prepare_call(
&self,
input: PrepareCallInput<Opt>,
) -> BoxFuture<'_, Result<PreparedCall, Error>>;
}
impl<Opt, F, Fut> PrepareCall<Opt> for F
where
F: Fn(PrepareCallInput<Opt>) -> Fut + Send + Sync,
Fut: Future<Output = Result<PreparedCall, Error>> + Send + 'static,
{
fn prepare_call(
&self,
input: PrepareCallInput<Opt>,
) -> BoxFuture<'_, Result<PreparedCall, Error>> {
Box::pin(self(input))
}
}