use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub type Result<T> = std::result::Result<T, AgentError>;
#[derive(Debug, thiserror::Error)]
pub enum AgentError {
#[error("Tool execution failed: {tool_name}: {message}")]
ToolError { tool_name: String, message: String },
#[error("Model error: {0}")]
ModelError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Max iterations exceeded: {0}")]
MaxIterations(usize),
#[error("MCP error: {0}")]
McpError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentOutput {
pub output: String,
pub data: Option<serde_json::Value>,
pub tool_calls: Vec<ToolCall>,
pub usage: Usage,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: serde_json::Value,
pub result: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Usage {
pub input_tokens: u64,
pub output_tokens: u64,
pub total_tokens: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
pub model: String,
pub api_base: Option<String>,
pub api_key: Option<String>,
pub max_iterations: usize,
pub temperature: Option<f32>,
pub max_tokens: Option<u32>,
pub settings: HashMap<String, serde_json::Value>,
}
impl Default for AgentConfig {
fn default() -> Self {
Self {
model: "gpt-4".to_string(),
api_base: None,
api_key: None,
max_iterations: 10,
temperature: None,
max_tokens: None,
settings: HashMap::new(),
}
}
}
impl AgentConfig {
pub fn new(model: impl Into<String>) -> Self {
Self {
model: model.into(),
..Default::default()
}
}
pub fn with_api_key(mut self, key: impl Into<String>) -> Self {
self.api_key = Some(key.into());
self
}
pub fn with_api_base(mut self, base: impl Into<String>) -> Self {
self.api_base = Some(base.into());
self
}
pub fn with_max_iterations(mut self, max: usize) -> Self {
self.max_iterations = max;
self
}
pub fn with_temperature(mut self, temp: f32) -> Self {
self.temperature = Some(temp);
self
}
}
#[async_trait]
pub trait SpecializedAgent: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn system_prompt(&self) -> &str;
fn tools(&self) -> Vec<ToolDefinition>;
async fn run(&self, input: &str, config: &AgentConfig) -> Result<AgentOutput>;
async fn run_streaming(
&self,
input: &str,
config: &AgentConfig,
) -> Result<Box<dyn futures::Stream<Item = Result<String>> + Send + Unpin>>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
pub requires_confirmation: bool,
}
impl ToolDefinition {
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
name: name.into(),
description: description.into(),
parameters: serde_json::json!({
"type": "object",
"properties": {},
"required": []
}),
requires_confirmation: false,
}
}
pub fn with_parameters(mut self, schema: serde_json::Value) -> Self {
self.parameters = schema;
self
}
pub fn with_confirmation(mut self, requires: bool) -> Self {
self.requires_confirmation = requires;
self
}
}