mod config;
mod events;
mod react;
#[cfg(feature = "structured")]
mod structured;
mod sub_agent;
pub use config::AgentConfig;
pub use events::ReActEvent;
pub use react::{
ReActAgent, ReActAgentBuilder, SerialToolRoundExecutor, ToolCallOutcome, ToolRoundCtx,
ToolRoundExecutor,
};
#[cfg(feature = "structured")]
pub use structured::{
StructuredOutcome, StructuredValidator, structured_retry_message, validate_structured,
};
pub use sub_agent::{PoolError, SubAgentPool, SubAgentTool};
use crate::memory::MemoryError;
use crate::observability::AgentEventRecord;
use crate::provider::ProviderError;
#[cfg(feature = "structured")]
use crate::run::TypedRunOutput;
use crate::run::{RunContext, RunOutput, RunRequest};
use futures::stream::BoxStream;
use std::fmt;
pub use crate::run::RunSummary;
pub use molo_core::agent::{AgentAction, ModelObservation, ModelRequest, Observation};
#[async_trait::async_trait]
pub trait AgentKernel: Send {
async fn start(
&mut self,
request: RunRequest,
context: &RunContext,
) -> Result<AgentAction, AgentError>;
async fn observe(
&mut self,
observation: Observation,
context: &RunContext,
) -> Result<AgentAction, AgentError>;
}
#[async_trait::async_trait]
pub trait Agent {
async fn run_request_with_context(
&mut self,
request: RunRequest,
context: RunContext,
) -> Result<RunOutput, AgentError>;
async fn run_request(&mut self, request: RunRequest) -> Result<RunOutput, AgentError> {
self.run_request_with_context(request, RunContext::generated())
.await
}
async fn run(&mut self, input: &str) -> Result<String, AgentError> {
Ok(self.run_request(RunRequest::text(input)).await?.answer)
}
async fn run_stream_request_with_context<'a>(
&'a mut self,
request: RunRequest,
context: RunContext,
) -> Result<BoxStream<'a, Result<MessageChunk, AgentError>>, AgentError> {
let output = self.run_request_with_context(request, context).await?;
Ok(Box::pin(futures::stream::iter([
Ok(MessageChunk::Delta(output.answer)),
Ok(MessageChunk::Done(output.summary)),
])))
}
async fn run_stream_request<'a>(
&'a mut self,
request: RunRequest,
) -> Result<BoxStream<'a, Result<MessageChunk, AgentError>>, AgentError> {
self.run_stream_request_with_context(request, RunContext::generated())
.await
}
async fn run_stream<'a>(
&'a mut self,
input: &'a str,
) -> Result<BoxStream<'a, Result<MessageChunk, AgentError>>, AgentError> {
self.run_stream_request(RunRequest::text(input)).await
}
}
#[async_trait::async_trait]
#[cfg(feature = "structured")]
pub trait TypedAgent: Agent {
async fn run_typed_request_with_context<U>(
&mut self,
request: RunRequest,
context: RunContext,
) -> Result<TypedRunOutput<U>, AgentError>
where
U: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync;
async fn run_typed_request<U>(
&mut self,
request: RunRequest,
) -> Result<TypedRunOutput<U>, AgentError>
where
U: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync,
{
self.run_typed_request_with_context(request, RunContext::generated())
.await
}
async fn run_typed<U>(&mut self, input: &str) -> Result<U, AgentError>
where
U: serde::de::DeserializeOwned + schemars::JsonSchema + Send + Sync,
{
Ok(self
.run_typed_request::<U>(RunRequest::text(input))
.await?
.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum MessageChunk {
Delta(String),
ToolCall {
id: String,
name: String,
arguments: String,
},
ToolResult {
id: String,
name: String,
content: String,
},
Done(RunSummary),
Cancelled,
}
pub trait AgentEvent: std::any::Any + Send + Sync + fmt::Debug {
fn name(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn to_record(&self) -> Option<AgentEventRecord> {
None
}
}
impl dyn AgentEvent {
pub fn as_any(&self) -> &dyn std::any::Any {
self as &dyn std::any::Any
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum AgentError {
#[error("memory error: {0}")]
Memory(#[from] MemoryError),
#[error("provider error: {0}")]
Provider(#[from] ProviderError),
#[error(
"model requested tools for more than {0} rounds; increase AgentConfig::max_tool_rounds (via with_config) if intended"
)]
TooManyToolRounds(usize),
#[error("run cancelled")]
Cancelled,
#[error("structured output failed to deserialize: {0}")]
StructuredParse(String),
#[error(
"structured output failed validation for more than {0} attempts; increase AgentConfig::max_structured_retries (via with_config) if intended"
)]
StructuredRetriesExhausted(usize),
#[error("run deadline exceeded")]
DeadlineExceeded,
#[error("effect requires harness: {0}")]
EffectRequiresHarness(String),
#[error("invalid agent step: {0}")]
InvalidStep(String),
}