use crate::{content::Content, duration::DurationMs, effect::Effect, error::OperatorError, id::*};
use async_trait::async_trait;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum TriggerType {
User,
Task,
Signal,
Schedule,
SystemEvent,
Custom(String),
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperatorInput {
pub message: Content,
pub trigger: TriggerType,
pub session: Option<SessionId>,
pub config: Option<OperatorConfig>,
#[serde(default)]
pub metadata: serde_json::Value,
}
#[non_exhaustive]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct OperatorConfig {
pub max_turns: Option<u32>,
pub max_cost: Option<Decimal>,
pub max_duration: Option<DurationMs>,
pub model: Option<String>,
pub allowed_tools: Option<Vec<String>>,
pub system_addendum: Option<String>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ExitReason {
Complete,
MaxTurns,
BudgetExhausted,
CircuitBreaker,
Timeout,
ObserverHalt {
reason: String,
},
Error,
Custom(String),
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperatorOutput {
pub message: Content,
pub exit_reason: ExitReason,
pub metadata: OperatorMetadata,
#[serde(default)]
pub effects: Vec<Effect>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperatorMetadata {
pub tokens_in: u64,
pub tokens_out: u64,
pub cost: Decimal,
pub turns_used: u32,
pub tools_called: Vec<ToolCallRecord>,
pub duration: DurationMs,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallRecord {
pub name: String,
pub duration: DurationMs,
pub success: bool,
}
impl Default for OperatorMetadata {
fn default() -> Self {
Self {
tokens_in: 0,
tokens_out: 0,
cost: Decimal::ZERO,
turns_used: 0,
tools_called: vec![],
duration: DurationMs::ZERO,
}
}
}
impl OperatorInput {
pub fn new(message: Content, trigger: TriggerType) -> Self {
Self {
message,
trigger,
session: None,
config: None,
metadata: serde_json::Value::Null,
}
}
}
impl OperatorOutput {
pub fn new(message: Content, exit_reason: ExitReason) -> Self {
Self {
message,
exit_reason,
metadata: OperatorMetadata::default(),
effects: vec![],
}
}
}
impl ToolCallRecord {
pub fn new(name: impl Into<String>, duration: DurationMs, success: bool) -> Self {
Self {
name: name.into(),
duration,
success,
}
}
}
#[async_trait]
pub trait Operator: Send + Sync {
async fn execute(&self, input: OperatorInput) -> Result<OperatorOutput, OperatorError>;
}