adk_agent/workflow/
parallel_agent.rs1#[cfg(feature = "skills")]
2use crate::skill_shim::load_skill_index;
3use crate::skill_shim::{SelectionPolicy, SkillIndex};
4use adk_core::{
5 AfterAgentCallback, Agent, BeforeAgentCallback, CallbackContext, Event, EventStream,
6 InvocationContext, Result, SharedState,
7};
8use async_stream::stream;
9use async_trait::async_trait;
10use std::sync::Arc;
11
12use super::branch_context::{BranchContext, derive_sub_branch};
13use super::shared_state_context::SharedStateContext;
14
15pub struct ParallelAgent {
17 name: String,
18 description: String,
19 sub_agents: Vec<Arc<dyn Agent>>,
20 skills_index: Option<Arc<SkillIndex>>,
21 skill_policy: SelectionPolicy,
22 max_skill_chars: usize,
23 before_callbacks: Arc<Vec<BeforeAgentCallback>>,
24 after_callbacks: Arc<Vec<AfterAgentCallback>>,
25 shared_state_enabled: bool,
26}
27
28impl ParallelAgent {
29 pub fn new(name: impl Into<String>, sub_agents: Vec<Arc<dyn Agent>>) -> Self {
31 Self {
32 name: name.into(),
33 description: String::new(),
34 sub_agents,
35 skills_index: None,
36 skill_policy: SelectionPolicy::default(),
37 max_skill_chars: 2000,
38 before_callbacks: Arc::new(Vec::new()),
39 after_callbacks: Arc::new(Vec::new()),
40 shared_state_enabled: false,
41 }
42 }
43
44 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
46 self.description = desc.into();
47 self
48 }
49
50 pub fn before_callback(mut self, callback: BeforeAgentCallback) -> Self {
52 if let Some(callbacks) = Arc::get_mut(&mut self.before_callbacks) {
53 callbacks.push(callback);
54 }
55 self
56 }
57
58 pub fn after_callback(mut self, callback: AfterAgentCallback) -> Self {
60 if let Some(callbacks) = Arc::get_mut(&mut self.after_callbacks) {
61 callbacks.push(callback);
62 }
63 self
64 }
65
66 #[cfg(feature = "skills")]
68 pub fn with_skills(mut self, index: SkillIndex) -> Self {
69 self.skills_index = Some(Arc::new(index));
70 self
71 }
72
73 #[cfg(feature = "skills")]
75 pub fn with_auto_skills(self) -> Result<Self> {
76 self.with_skills_from_root(".")
77 }
78
79 #[cfg(feature = "skills")]
81 pub fn with_skills_from_root(mut self, root: impl AsRef<std::path::Path>) -> Result<Self> {
82 let index = load_skill_index(root).map_err(|e| adk_core::AdkError::agent(e.to_string()))?;
83 self.skills_index = Some(Arc::new(index));
84 Ok(self)
85 }
86
87 #[cfg(feature = "skills")]
89 pub fn with_skill_policy(mut self, policy: SelectionPolicy) -> Self {
90 self.skill_policy = policy;
91 self
92 }
93
94 #[cfg(feature = "skills")]
96 pub fn with_skill_budget(mut self, max_chars: usize) -> Self {
97 self.max_skill_chars = max_chars;
98 self
99 }
100
101 pub fn with_shared_state(mut self) -> Self {
107 self.shared_state_enabled = true;
108 self
109 }
110}
111
112#[async_trait]
113impl Agent for ParallelAgent {
114 fn name(&self) -> &str {
115 &self.name
116 }
117
118 fn description(&self) -> &str {
119 &self.description
120 }
121
122 fn sub_agents(&self) -> &[Arc<dyn Agent>] {
123 &self.sub_agents
124 }
125
126 fn supports_agent_transfer(&self) -> bool {
127 false
131 }
132
133 async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream> {
134 let sub_agents = self.sub_agents.clone();
135 let run_ctx = super::skill_context::with_skill_injected_context(
136 ctx,
137 self.skills_index.as_ref(),
138 &self.skill_policy,
139 self.max_skill_chars,
140 );
141 let before_callbacks = self.before_callbacks.clone();
142 let after_callbacks = self.after_callbacks.clone();
143 let agent_name = self.name.clone();
144 let invocation_id = run_ctx.invocation_id().to_string();
145 let shared_state_enabled = self.shared_state_enabled;
146
147 let s = stream! {
148 use futures::stream::{StreamExt, select_all};
149
150 for callback in before_callbacks.as_ref() {
151 match callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
152 Ok(Some(content)) => {
153 let mut early_event = Event::new(&invocation_id);
154 early_event.author = agent_name.clone();
155 early_event.llm_response.content = Some(content);
156 yield Ok(early_event);
157
158 for after_callback in after_callbacks.as_ref() {
159 match after_callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
160 Ok(Some(after_content)) => {
161 let mut after_event = Event::new(&invocation_id);
162 after_event.author = agent_name.clone();
163 after_event.llm_response.content = Some(after_content);
164 yield Ok(after_event);
165 return;
166 }
167 Ok(None) => continue,
168 Err(e) => {
169 yield Err(e);
170 return;
171 }
172 }
173 }
174 return;
175 }
176 Ok(None) => continue,
177 Err(e) => {
178 yield Err(e);
179 return;
180 }
181 }
182 }
183
184
185 let shared = if shared_state_enabled {
187 Some(Arc::new(SharedState::new()))
188 } else {
189 None
190 };
191
192 let mut merged = {
210 type BranchStream =
213 std::pin::Pin<Box<dyn futures::Stream<Item = (usize, Result<Event>)> + Send>>;
214 let mut per_agent: Vec<BranchStream> = Vec::with_capacity(sub_agents.len());
215
216 for (index, agent) in sub_agents.into_iter().enumerate() {
217 let base: Arc<dyn InvocationContext> = if let Some(ref shared) = shared {
218 Arc::new(SharedStateContext::new(run_ctx.clone(), shared.clone()))
219 } else {
220 run_ctx.clone()
221 };
222
223 let branch = derive_sub_branch(
229 base.branch(),
230 &format!("{agent_name}.{}", agent.name()),
231 );
232 let ctx: Arc<dyn InvocationContext> =
233 Arc::new(BranchContext::new(base, branch.clone()));
234
235 per_agent.push(Box::pin(stream! {
236 match agent.run(ctx).await {
237 Ok(mut events) => {
238 while let Some(event_result) = events.next().await {
239 let failed = event_result.is_err();
240 let event_result = event_result.map(|mut event| {
245 if event.branch.is_empty() {
246 event.branch = branch.clone();
247 }
248 event
249 });
250 yield (index, event_result);
251 if failed {
252 break;
254 }
255 }
256 }
257 Err(e) => yield (index, Err(e)),
258 }
259 }));
260 }
261
262 select_all(per_agent)
263 };
264
265 let mut failures: Vec<(usize, adk_core::AdkError)> = Vec::new();
270
271 while let Some((index, event_result)) = merged.next().await {
272 match event_result {
273 Ok(event) => yield Ok(event),
274 Err(e) => failures.push((index, e)),
275 }
276 }
277
278 if let Some((_, e)) = failures.into_iter().min_by_key(|(index, _)| *index) {
280 yield Err(e);
281 return;
282 }
283
284 for callback in after_callbacks.as_ref() {
285 match callback(run_ctx.clone() as Arc<dyn CallbackContext>).await {
286 Ok(Some(content)) => {
287 let mut after_event = Event::new(&invocation_id);
288 after_event.author = agent_name.clone();
289 after_event.llm_response.content = Some(content);
290 yield Ok(after_event);
291 break;
292 }
293 Ok(None) => continue,
294 Err(e) => {
295 yield Err(e);
296 return;
297 }
298 }
299 }
300 };
301
302 Ok(Box::pin(s))
303 }
304}