mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
97 lines
2.6 KiB
Rust
97 lines
2.6 KiB
Rust
|
|
use async_trait::async_trait;
|
||
|
|
|
||
|
|
pub trait AiExecutionAttempt {
|
||
|
|
fn execution_plan(&self) -> &aether_contracts::ExecutionPlan;
|
||
|
|
|
||
|
|
fn report_kind(&self) -> Option<String>;
|
||
|
|
|
||
|
|
fn report_context(&self) -> Option<serde_json::Value>;
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug)]
|
||
|
|
pub enum AiAttemptLoopOutcome<Response, Exhaustion> {
|
||
|
|
Responded(Response),
|
||
|
|
Exhausted(Exhaustion),
|
||
|
|
NoPath,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[async_trait]
|
||
|
|
pub trait AiAttemptLoopPort<Attempt>: 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<Option<Self::Response>, Self::Error>;
|
||
|
|
|
||
|
|
async fn mark_unused_attempts(&self, attempts: Vec<Attempt>) -> Result<(), Self::Error>;
|
||
|
|
|
||
|
|
async fn build_exhaustion(
|
||
|
|
&self,
|
||
|
|
last_plan: aether_contracts::ExecutionPlan,
|
||
|
|
last_report_context: Option<serde_json::Value>,
|
||
|
|
) -> Result<Self::Exhaustion, Self::Error>;
|
||
|
|
}
|
||
|
|
|
||
|
|
pub async fn run_ai_attempt_loop<Port, Attempt>(
|
||
|
|
port: &Port,
|
||
|
|
attempts: Vec<Attempt>,
|
||
|
|
) -> Result<AiAttemptLoopOutcome<Port::Response, Port::Exhaustion>, Port::Error>
|
||
|
|
where
|
||
|
|
Port: AiAttemptLoopPort<Attempt>,
|
||
|
|
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<String> {
|
||
|
|
self.report_kind.clone()
|
||
|
|
}
|
||
|
|
|
||
|
|
fn report_context(&self) -> Option<serde_json::Value> {
|
||
|
|
self.report_context.clone()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl AiExecutionAttempt for crate::dto::AiStreamAttempt {
|
||
|
|
fn execution_plan(&self) -> &aether_contracts::ExecutionPlan {
|
||
|
|
&self.plan
|
||
|
|
}
|
||
|
|
|
||
|
|
fn report_kind(&self) -> Option<String> {
|
||
|
|
self.report_kind.clone()
|
||
|
|
}
|
||
|
|
|
||
|
|
fn report_context(&self) -> Option<serde_json::Value> {
|
||
|
|
self.report_context.clone()
|
||
|
|
}
|
||
|
|
}
|