use async_trait::async_trait; pub trait AiExecutionAttempt { fn execution_plan(&self) -> &aether_contracts::ExecutionPlan; fn report_kind(&self) -> Option; fn report_context(&self) -> Option; } #[derive(Debug)] pub enum AiAttemptLoopOutcome { Responded(Response), Exhausted(Exhaustion), NoPath, } #[async_trait] pub trait AiAttemptLoopPort: Send + Sync where Attempt: AiExecutionAttempt + Send + Sync + 'static, { type Response: Send; type Exhaustion: Send; type Error: Send; async fn execute_attempt( &self, attempt: &Attempt, ) -> Result, Self::Error>; async fn mark_unused_attempts(&self, attempts: Vec) -> Result<(), Self::Error>; async fn build_exhaustion( &self, last_plan: aether_contracts::ExecutionPlan, last_report_context: Option, ) -> Result; } pub async fn run_ai_attempt_loop( port: &Port, attempts: Vec, ) -> Result, Port::Error> where Port: AiAttemptLoopPort, Attempt: AiExecutionAttempt + Send + Sync + 'static, { let mut remaining = attempts.into_iter(); let mut last_attempted = None; while let Some(attempt) = remaining.next() { last_attempted = Some((attempt.execution_plan().clone(), attempt.report_context())); if let Some(response) = port.execute_attempt(&attempt).await? { port.mark_unused_attempts(remaining.collect()).await?; return Ok(AiAttemptLoopOutcome::Responded(response)); } } let Some((last_plan, last_report_context)) = last_attempted else { return Ok(AiAttemptLoopOutcome::NoPath); }; Ok(AiAttemptLoopOutcome::Exhausted( port.build_exhaustion(last_plan, last_report_context) .await?, )) } impl AiExecutionAttempt for crate::dto::AiSyncAttempt { fn execution_plan(&self) -> &aether_contracts::ExecutionPlan { &self.plan } fn report_kind(&self) -> Option { self.report_kind.clone() } fn report_context(&self) -> Option { self.report_context.clone() } } impl AiExecutionAttempt for crate::dto::AiStreamAttempt { fn execution_plan(&self) -> &aether_contracts::ExecutionPlan { &self.plan } fn report_kind(&self) -> Option { self.report_kind.clone() } fn report_context(&self) -> Option { self.report_context.clone() } }