adk_agent/workflow/
conditional_agent.rs1#[cfg(feature = "skills")]
30use crate::skill_shim::load_skill_index;
31use crate::skill_shim::{SelectionPolicy, SkillIndex};
32use adk_core::{
33 AfterAgentCallback, Agent, BeforeAgentCallback, CallbackContext, Event, EventStream,
34 InvocationContext, Result,
35};
36use async_stream::stream;
37use async_trait::async_trait;
38use futures::StreamExt;
39use std::sync::Arc;
40
41type ConditionFn = Arc<dyn Fn(&dyn InvocationContext) -> bool + Send + Sync>;
42
43pub struct ConditionalAgent {
59 name: String,
60 description: String,
61 condition: ConditionFn,
62 if_agent: Arc<dyn Agent>,
63 else_agent: Option<Arc<dyn Agent>>,
64 all_agents: Vec<Arc<dyn Agent>>,
66 skills_index: Option<Arc<SkillIndex>>,
67 skill_policy: SelectionPolicy,
68 max_skill_chars: usize,
69 before_callbacks: Arc<Vec<BeforeAgentCallback>>,
70 after_callbacks: Arc<Vec<AfterAgentCallback>>,
71}
72
73impl ConditionalAgent {
74 pub fn new<F>(name: impl Into<String>, condition: F, if_agent: Arc<dyn Agent>) -> Self
76 where
77 F: Fn(&dyn InvocationContext) -> bool + Send + Sync + 'static,
78 {
79 let all_agents = vec![if_agent.clone()];
80 Self {
81 name: name.into(),
82 description: String::new(),
83 condition: Arc::new(condition),
84 if_agent,
85 else_agent: None,
86 all_agents,
87 skills_index: None,
88 skill_policy: SelectionPolicy::default(),
89 max_skill_chars: 2000,
90 before_callbacks: Arc::new(Vec::new()),
91 after_callbacks: Arc::new(Vec::new()),
92 }
93 }
94
95 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
97 self.description = desc.into();
98 self
99 }
100
101 pub fn with_else(mut self, else_agent: Arc<dyn Agent>) -> Self {
103 self.all_agents.push(else_agent.clone());
104 self.else_agent = Some(else_agent);
105 self
106 }
107
108 pub fn before_callback(mut self, callback: BeforeAgentCallback) -> Self {
110 if let Some(callbacks) = Arc::get_mut(&mut self.before_callbacks) {
111 callbacks.push(callback);
112 }
113 self
114 }
115
116 pub fn after_callback(mut self, callback: AfterAgentCallback) -> Self {
118 if let Some(callbacks) = Arc::get_mut(&mut self.after_callbacks) {
119 callbacks.push(callback);
120 }
121 self
122 }
123
124 #[cfg(feature = "skills")]
126 pub fn with_skills(mut self, index: SkillIndex) -> Self {
127 self.skills_index = Some(Arc::new(index));
128 self
129 }
130
131 #[cfg(feature = "skills")]
133 pub fn with_auto_skills(self) -> Result<Self> {
134 self.with_skills_from_root(".")
135 }
136
137 #[cfg(feature = "skills")]
139 pub fn with_skills_from_root(mut self, root: impl AsRef<std::path::Path>) -> Result<Self> {
140 let index = load_skill_index(root).map_err(|e| adk_core::AdkError::agent(e.to_string()))?;
141 self.skills_index = Some(Arc::new(index));
142 Ok(self)
143 }
144
145 #[cfg(feature = "skills")]
147 pub fn with_skill_policy(mut self, policy: SelectionPolicy) -> Self {
148 self.skill_policy = policy;
149 self
150 }
151
152 #[cfg(feature = "skills")]
154 pub fn with_skill_budget(mut self, max_chars: usize) -> Self {
155 self.max_skill_chars = max_chars;
156 self
157 }
158}
159
160#[async_trait]
161impl Agent for ConditionalAgent {
162 fn name(&self) -> &str {
163 &self.name
164 }
165
166 fn description(&self) -> &str {
167 &self.description
168 }
169
170 fn sub_agents(&self) -> &[Arc<dyn Agent>] {
171 &self.all_agents
172 }
173
174 fn supports_agent_transfer(&self) -> bool {
175 false
179 }
180
181 async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream> {
182 let run_ctx = super::skill_context::with_skill_injected_context(
183 ctx,
184 self.skills_index.as_ref(),
185 &self.skill_policy,
186 self.max_skill_chars,
187 );
188 let before_callbacks = self.before_callbacks.clone();
189 let after_callbacks = self.after_callbacks.clone();
190 let if_agent = self.if_agent.clone();
191 let else_agent = self.else_agent.clone();
192 let agent_name = self.name.clone();
193 let invocation_id = run_ctx.invocation_id().to_string();
194 let condition = self.condition.clone();
195
196 let s = stream! {
197 for callback in before_callbacks.as_ref() {
198 match callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
199 Ok(Some(content)) => {
200 let mut early_event = Event::new(&invocation_id);
201 early_event.author = agent_name.clone();
202 early_event.llm_response.content = Some(content);
203 yield Ok(early_event);
204
205 for after_callback in after_callbacks.as_ref() {
206 match after_callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
207 Ok(Some(after_content)) => {
208 let mut after_event = Event::new(&invocation_id);
209 after_event.author = agent_name.clone();
210 after_event.llm_response.content = Some(after_content);
211 yield Ok(after_event);
212 return;
213 }
214 Ok(None) => continue,
215 Err(e) => {
216 yield Err(e);
217 return;
218 }
219 }
220 }
221 return;
222 }
223 Ok(None) => continue,
224 Err(e) => {
225 yield Err(e);
226 return;
227 }
228 }
229 }
230
231 let target_agent = if condition(run_ctx.as_ref()) {
232 Some(if_agent)
233 } else {
234 else_agent
235 };
236
237 if let Some(agent) = target_agent {
238 let mut stream = match agent.run(run_ctx.clone()).await {
239 Ok(stream) => stream,
240 Err(e) => {
241 yield Err(e);
242 return;
243 }
244 };
245
246 while let Some(result) = stream.next().await {
247 match result {
248 Ok(event) => yield Ok(event),
249 Err(e) => {
250 yield Err(e);
251 return;
252 }
253 }
254 }
255 }
256
257 for callback in after_callbacks.as_ref() {
258 match callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
259 Ok(Some(content)) => {
260 let mut after_event = Event::new(&invocation_id);
261 after_event.author = agent_name.clone();
262 after_event.llm_response.content = Some(content);
263 yield Ok(after_event);
264 break;
265 }
266 Ok(None) => continue,
267 Err(e) => {
268 yield Err(e);
269 return;
270 }
271 }
272 }
273 };
274
275 Ok(Box::pin(s))
276 }
277}