use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::seed::Seed;
use crate::{EvaluationResult, InterviewResult, ouroboros_engine::InterviewQuestionOutput};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Phase {
Interview,
Seed,
Execute,
Evaluate,
Evolve,
}
impl std::fmt::Display for Phase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Phase::Interview => write!(f, "interview"),
Phase::Seed => write!(f, "seed"),
Phase::Execute => write!(f, "execute"),
Phase::Evaluate => write!(f, "evaluate"),
Phase::Evolve => write!(f, "evolve"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallRecord {
pub tool: String,
pub input: String,
pub output: String,
pub duration_ms: u64,
#[serde(default)]
pub is_error: bool,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub tool_call_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionResult {
pub output: String,
pub steps_completed: usize,
pub success: bool,
#[serde(default)]
pub tool_calls: Vec<ToolCallRecord>,
#[serde(default)]
pub tokens_input: u64,
#[serde(default)]
pub tokens_output: u64,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub model_id: String,
}
#[async_trait]
pub trait OuroborosProtocol: Send + Sync {
async fn interview(&self, user_input: &str) -> Result<InterviewResult>;
async fn interview_structured(
&self,
user_input: &str,
) -> Result<Option<Vec<InterviewQuestionOutput>>>;
async fn generate_seed(&self, interview: &InterviewResult) -> Result<Seed>;
async fn execute(&self, seed: &Seed) -> Result<ExecutionResult>;
async fn evaluate(&self, seed: &Seed, execution: &ExecutionResult) -> Result<EvaluationResult>;
async fn evolve(&self, seed: &Seed, evaluation: &EvaluationResult) -> Result<Option<Seed>>;
#[inline]
fn set_persona_prompt(&self, _prompt: Option<String>) {}
}