use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentSpec {
pub name: String,
pub system_prompt: String,
pub tools: Vec<String>,
pub max_turns: u32,
pub metadata: HashMap<String, Value>,
#[serde(default)]
pub cache_control: bool,
}
impl AgentSpec {
pub fn new(name: &str, system_prompt: &str) -> Self {
Self {
name: name.to_string(),
system_prompt: system_prompt.to_string(),
tools: Vec::new(),
max_turns: 10,
metadata: HashMap::new(),
cache_control: false,
}
}
pub fn with_tools(mut self, tools: Vec<String>) -> Self {
self.tools = tools;
self
}
pub fn with_max_turns(mut self, max_turns: u32) -> Self {
self.max_turns = max_turns;
self
}
pub fn with_metadata(mut self, key: &str, value: Value) -> Self {
self.metadata.insert(key.to_string(), value);
self
}
pub fn with_cache_control(mut self) -> Self {
self.cache_control = true;
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TokenAccounting {
#[serde(default)]
pub input_tokens: u64,
#[serde(default)]
pub output_tokens: u64,
#[serde(default)]
pub cost_usd: f64,
}
impl TokenAccounting {
pub fn new(input_tokens: u64, output_tokens: u64, cost_usd: f64) -> Self {
debug_assert!(cost_usd.is_finite(), "cost_usd must be finite");
debug_assert!(cost_usd >= 0.0, "cost_usd must be non-negative");
Self {
input_tokens,
output_tokens,
cost_usd,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentOutput {
pub name: String,
pub answer: String,
pub turns: u32,
pub tool_calls: u32,
pub duration_ms: f64,
pub error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub outcome: Option<car_ir::AgentOutcome>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tokens: Option<TokenAccounting>,
}
impl AgentOutput {
pub fn succeeded(&self) -> bool {
self.error.is_none() && !self.answer.is_empty()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub from: String,
pub to: String,
pub kind: MessageKind,
pub payload: Value,
pub timestamp: DateTime<Utc>,
}
impl Message {
pub fn new(from: &str, to: &str, kind: MessageKind, payload: Value) -> Self {
Self {
from: from.to_string(),
to: to.to_string(),
kind,
payload,
timestamp: Utc::now(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MessageKind {
TaskAssignment,
Result,
Feedback,
DelegateRequest,
DelegateResponse,
Custom,
}