use std::collections::BTreeSet;
use std::sync::Arc;
use af_agent::{
ChatModel, ContextContributor, Hook, MountedPlugins, PluginError, PromptAuthority,
PromptSection, ToolRegistry,
};
use crate::{
AgentRuntime, ApproximateTokenMeter, Compactor, ModelCompactor, RuntimeLimits, TokenMeter,
};
impl AgentRuntime {
pub fn new(
model: Arc<dyn ChatModel>,
model_name: impl Into<String>,
system_prompt: impl Into<String>,
) -> Self {
let mut prompts = af_agent::PromptRegistry::default();
prompts
.insert(PromptSection {
id: "product.base".into(),
order: 100,
authority: PromptAuthority::Product,
source: "product".into(),
version: env!("CARGO_PKG_VERSION").into(),
content: system_prompt.into(),
})
.expect("base prompt is validated by ProductRuntime");
Self {
compactor: Arc::new(ModelCompactor::new(Arc::clone(&model))),
model,
model_name: model_name.into(),
prompts,
contexts: Vec::new(),
tools: ToolRegistry::new(),
hooks: Vec::new(),
limits: RuntimeLimits::default(),
meter: Arc::new(ApproximateTokenMeter),
plugins: None,
reasoning_effort: None,
output_policy: None,
}
}
pub fn with_tools(mut self, tools: ToolRegistry) -> Self {
self.tools = tools;
self
}
pub fn with_hooks(mut self, hooks: Vec<Arc<dyn Hook>>) -> Self {
self.hooks = hooks;
self
}
pub fn with_limits(mut self, limits: RuntimeLimits) -> Self {
self.limits = limits;
self
}
pub fn with_token_meter(mut self, meter: Arc<dyn TokenMeter>) -> Self {
self.meter = meter;
self
}
pub fn with_compactor(mut self, compactor: Arc<dyn Compactor>) -> Self {
self.compactor = compactor;
self
}
pub fn with_reasoning_effort(mut self, effort: af_llm::ReasoningEffort) -> Self {
self.reasoning_effort = Some(effort);
self
}
pub fn with_output_policy(mut self, schema: serde_json::Value) -> Result<Self, PluginError> {
af_agent::validate_json_schema_definition(&schema).map_err(PluginError::Conflict)?;
self.output_policy = Some(schema);
Ok(self)
}
pub fn with_prompt_section(mut self, section: PromptSection) -> Result<Self, PluginError> {
self.prompts
.insert(section)
.map_err(PluginError::Conflict)?;
Ok(self)
}
pub fn with_context_contributor(mut self, context: Arc<dyn ContextContributor>) -> Self {
self.contexts.push(context);
self
}
pub fn with_plugins(mut self, mounted: MountedPlugins) -> Result<Self, PluginError> {
self.tools
.extend(mounted.tools())
.map_err(PluginError::Conflict)?;
self.hooks.extend(mounted.hooks().iter().cloned());
self.contexts.extend(mounted.contexts().iter().cloned());
for prompt in mounted.prompts() {
self.prompts
.insert(prompt.clone())
.map_err(PluginError::Conflict)?;
}
self.plugins = Some(mounted);
Ok(self)
}
pub fn with_allowed_tools(mut self, allowed: Option<&BTreeSet<String>>) -> Self {
if let Some(allowed) = allowed {
self.tools = self.tools.filtered(allowed.iter().map(String::as_str));
}
self
}
}