use async_trait::async_trait;
use futures::stream::Stream;
use serde::{Deserialize, Serialize};
use std::pin::Pin;
use std::sync::Arc;
use crate::llm::StreamChunk;
use crate::messaging::AgentMessage;
use crate::state::AgentStateSnapshot;
#[async_trait]
pub trait PlannerHandle: Send + Sync + std::any::Any {
async fn plan(
&self,
context: PlannerContext,
state: Arc<AgentStateSnapshot>,
) -> anyhow::Result<PlannerDecision>;
fn as_any(&self) -> &dyn std::any::Any;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentDescriptor {
pub name: String,
pub version: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlannerDecision {
pub next_action: PlannerAction,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlannerAction {
CallTool {
tool_name: String,
payload: serde_json::Value,
},
Respond {
message: AgentMessage,
},
Terminate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlannerContext {
pub history: Vec<AgentMessage>,
pub system_prompt: String,
#[serde(default)]
pub tools: Vec<crate::tools::ToolSchema>,
}
pub type AgentStream = Pin<Box<dyn Stream<Item = anyhow::Result<StreamChunk>> + Send>>;
#[async_trait]
pub trait AgentHandle: Send + Sync {
async fn describe(&self) -> AgentDescriptor;
async fn handle_message(
&self,
input: AgentMessage,
state: Arc<AgentStateSnapshot>,
) -> anyhow::Result<AgentMessage>;
async fn handle_message_stream(
&self,
input: AgentMessage,
state: Arc<AgentStateSnapshot>,
) -> anyhow::Result<AgentStream> {
let response = self.handle_message(input, state).await?;
Ok(Box::pin(futures::stream::once(async move {
Ok(StreamChunk::Done { message: response })
})))
}
async fn current_interrupt(&self) -> anyhow::Result<Option<crate::hitl::AgentInterrupt>> {
Ok(None)
}
async fn resume_with_approval(
&self,
_action: crate::hitl::HitlAction,
) -> anyhow::Result<AgentMessage> {
anyhow::bail!("resume_with_approval not implemented for this agent")
}
}