1use std::{collections::BTreeMap, future::Future, pin::Pin, sync::Arc, time::Instant};
2
3use futures_util::{
4 StreamExt,
5 future::{Either, select},
6};
7use runifold_core::{
8 BudgetEvent, CapabilitySet, DomainEvent, EffectId, EffectKind, EffectRequest, EventId,
9 InvocationId, LifecycleEvent, RetrySafety, RunContext, RunError, RunErrorKind, RunEventKind,
10 Usage,
11};
12use runifold_effect::{
13 EffectExecutionContext, EffectExecutor, EffectExecutorErrorKind, EffectFuture, EffectHandler,
14 EffectRecoveryPolicy, InMemoryEffectStore,
15};
16use runifold_model::{
17 ContentPart, FeaturePolicy, Message, Model, ModelCallContext, ModelError, ModelErrorKind,
18 ModelRef, ModelRequest, ModelResponse, ModelStreamAccumulator, OutputFormat, Role, ToolCall,
19 ToolResult,
20};
21use runifold_tool::{ToolError, ToolErrorKind, ToolOutput, ToolRegistry};
22use schemars::JsonSchema;
23use serde::{Deserialize, Serialize};
24
25use crate::checkpoint::CheckpointCursor;
26use crate::stream::{AgentObserver, BufferedObserver, NoopObserver, emit_agent_event};
27use crate::{AgentCheckpoint, AgentCheckpointPhase, AgentCheckpointState, ResumePolicy};
28use crate::{
29 AgentError, AgentEventStream, AgentGateway, AgentOutcome, AgentStreamEvent, CallableKind,
30 GatewayError, GatewayErrorKind, StructuredAgent,
31};
32
33mod callable;
34mod checkpointing;
35mod execution;
36mod observability;
37
38pub type AgentFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
40
41#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
43#[non_exhaustive]
44pub enum ToolErrorPolicy {
45 #[default]
47 ReturnToModel,
48 FailFast,
50}
51
52#[derive(Clone, Debug, Eq, PartialEq)]
54pub struct AgentConfig {
55 pub max_turns: u32,
57 pub tool_error_policy: ToolErrorPolicy,
59 pub feature_policy: FeaturePolicy,
61}
62
63impl Default for AgentConfig {
64 fn default() -> Self {
65 Self {
66 max_turns: 16,
67 tool_error_policy: ToolErrorPolicy::ReturnToModel,
68 feature_policy: FeaturePolicy::Strict,
69 }
70 }
71}
72
73#[derive(Clone)]
75pub struct Agent {
76 pub(crate) name: String,
77 pub(crate) model: Arc<dyn Model>,
78 pub(crate) model_ref: ModelRef,
79 pub(crate) instructions: Vec<Message>,
80 pub(crate) tools: ToolRegistry,
81 pub(crate) agents: AgentGateway,
82 pub(crate) effects: EffectExecutor,
83 pub(crate) effect_recovery: EffectRecoveryPolicy,
84 pub(crate) config: AgentConfig,
85 pub(crate) output_format: OutputFormat,
86}
87
88impl Agent {
89 pub fn builder(
91 name: impl Into<String>,
92 model: Arc<dyn Model>,
93 model_ref: ModelRef,
94 ) -> crate::AgentBuilder {
95 crate::AgentBuilder::new(name, model, model_ref)
96 }
97
98 pub fn new(name: impl Into<String>, model: Arc<dyn Model>, model_ref: ModelRef) -> Self {
100 Self {
101 name: name.into(),
102 model,
103 model_ref,
104 instructions: Vec::new(),
105 tools: ToolRegistry::new(),
106 agents: AgentGateway::new(),
107 effects: EffectExecutor::new(Arc::new(InMemoryEffectStore::new())),
108 effect_recovery: EffectRecoveryPolicy::RejectAmbiguous,
109 config: AgentConfig::default(),
110 output_format: OutputFormat::Text,
111 }
112 }
113
114 #[must_use]
116 pub fn system(mut self, instruction: impl Into<String>) -> Self {
117 self.instructions.push(Message::system(instruction));
118 self
119 }
120
121 #[must_use]
123 pub fn tools(mut self, tools: ToolRegistry) -> Self {
124 self.tools = tools;
125 self
126 }
127
128 #[must_use]
130 pub fn agents(mut self, agents: AgentGateway) -> Self {
131 self.agents = agents;
132 self
133 }
134
135 #[must_use]
137 pub fn effect_executor(mut self, effects: EffectExecutor) -> Self {
138 self.effects = effects;
139 self
140 }
141
142 #[must_use]
144 pub const fn effect_recovery_policy(mut self, policy: EffectRecoveryPolicy) -> Self {
145 self.effect_recovery = policy;
146 self
147 }
148
149 #[must_use]
151 pub const fn with_config(mut self, config: AgentConfig) -> Self {
152 self.config = config;
153 self
154 }
155
156 #[must_use]
158 pub fn output_format(mut self, output_format: OutputFormat) -> Self {
159 self.output_format = output_format;
160 self
161 }
162
163 #[must_use]
165 pub fn structured_output<T>(self, name: impl Into<String>) -> Self
166 where
167 T: JsonSchema,
168 {
169 self.output_format(OutputFormat::typed::<T>(name))
170 }
171
172 pub fn into_structured<T>(self, name: impl Into<String>) -> StructuredAgent<T>
174 where
175 T: JsonSchema,
176 {
177 StructuredAgent::new(self.structured_output::<T>(name))
178 }
179
180 pub fn name(&self) -> &str {
182 &self.name
183 }
184
185 pub const fn model_ref(&self) -> &ModelRef {
187 &self.model_ref
188 }
189
190 pub fn callable_capabilities(&self) -> CapabilitySet {
196 let mut capabilities = CapabilitySet::new();
197 for spec in self.tools.model_specs() {
198 if let Some(descriptor) = self.tools.descriptor(&spec.name) {
199 capabilities.grant(descriptor.capability());
200 }
201 }
202 for spec in self.agents.model_specs() {
203 if let Some(descriptor) = self.agents.descriptor(&spec.name) {
204 capabilities.grant(descriptor.capability());
205 }
206 }
207 capabilities
208 }
209}
210
211impl std::fmt::Debug for Agent {
212 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 formatter
214 .debug_struct("Agent")
215 .field("name", &self.name)
216 .field("model_ref", &self.model_ref)
217 .field("instructions", &self.instructions)
218 .field("tools", &self.tools)
219 .field("agents", &self.agents)
220 .field("effects", &self.effects)
221 .field("effect_recovery", &self.effect_recovery)
222 .field("config", &self.config)
223 .field("output_format", &self.output_format)
224 .finish_non_exhaustive()
225 }
226}
227
228#[cfg(test)]
229mod tests;