gemini_adk_rs/text/
dispatch.rs1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::Duration;
4
5use async_trait::async_trait;
6
7use super::TextAgent;
8use crate::context::AgentEvent;
9use crate::error::AgentError;
10use crate::middleware::MiddlewareChain;
11use crate::state::State;
12
13type TaskMap = HashMap<String, tokio::task::JoinHandle<Result<String, String>>>;
15
16#[derive(Clone, Default)]
18pub struct TaskRegistry {
19 pub(crate) inner: Arc<tokio::sync::Mutex<TaskMap>>,
20}
21
22impl TaskRegistry {
23 pub fn new() -> Self {
25 Self::default()
26 }
27}
28
29pub struct DispatchTextAgent {
34 name: String,
35 children: Vec<(String, Arc<dyn TextAgent>)>,
36 registry: TaskRegistry,
37 budget: Arc<tokio::sync::Semaphore>,
38 middleware: MiddlewareChain,
39}
40
41impl DispatchTextAgent {
42 pub fn new(
44 name: impl Into<String>,
45 children: Vec<(String, Arc<dyn TextAgent>)>,
46 registry: TaskRegistry,
47 budget: Arc<tokio::sync::Semaphore>,
48 ) -> Self {
49 Self {
50 name: name.into(),
51 children,
52 registry,
53 budget,
54 middleware: MiddlewareChain::new(),
55 }
56 }
57
58 pub fn with_middleware_chain(mut self, chain: MiddlewareChain) -> Self {
63 self.middleware = chain;
64 self
65 }
66}
67
68#[async_trait]
69impl TextAgent for DispatchTextAgent {
70 fn name(&self) -> &str {
71 &self.name
72 }
73
74 async fn run(&self, state: &State) -> Result<String, AgentError> {
75 let mut registry = self.registry.inner.lock().await;
76
77 for (task_name, agent) in &self.children {
78 let agent = agent.clone();
79 let state = state.clone();
80 let budget = self.budget.clone();
81 let task_name_owned = task_name.clone();
82
83 let handle = tokio::spawn(async move {
84 let _permit = budget
85 .acquire()
86 .await
87 .map_err(|e| format!("Semaphore closed: {e}"))?;
88 agent
89 .run(&state)
90 .await
91 .map_err(|e| format!("Task '{task_name_owned}' failed: {e}"))
92 });
93
94 registry.insert(task_name.clone(), handle);
95 }
96
97 let _ = state.set(
98 "_dispatch_status",
99 self.children
100 .iter()
101 .map(|(name, _)| (name.clone(), "running".to_string()))
102 .collect::<HashMap<String, String>>(),
103 );
104
105 Ok(String::new())
106 }
107}
108
109pub struct JoinTextAgent {
113 name: String,
114 registry: TaskRegistry,
115 target_names: Option<Vec<String>>,
116 timeout: Option<Duration>,
117 middleware: MiddlewareChain,
118}
119
120impl JoinTextAgent {
121 pub fn new(name: impl Into<String>, registry: TaskRegistry) -> Self {
123 Self {
124 name: name.into(),
125 registry,
126 target_names: None,
127 timeout: None,
128 middleware: MiddlewareChain::new(),
129 }
130 }
131
132 pub fn with_middleware_chain(mut self, chain: MiddlewareChain) -> Self {
136 self.middleware = chain;
137 self
138 }
139
140 pub fn targets(mut self, names: Vec<String>) -> Self {
142 self.target_names = Some(names);
143 self
144 }
145
146 pub fn timeout(mut self, timeout: Duration) -> Self {
148 self.timeout = Some(timeout);
149 self
150 }
151}
152
153#[async_trait]
154impl TextAgent for JoinTextAgent {
155 fn name(&self) -> &str {
156 &self.name
157 }
158
159 async fn run(&self, state: &State) -> Result<String, AgentError> {
160 let mut registry = self.registry.inner.lock().await;
161
162 let tasks: HashMap<String, _> = if let Some(targets) = &self.target_names {
164 targets
165 .iter()
166 .filter_map(|name| registry.remove(name).map(|h| (name.clone(), h)))
167 .collect()
168 } else {
169 std::mem::take(&mut *registry)
170 };
171 drop(registry);
172
173 let mut results = Vec::new();
174
175 for (task_name, handle) in tasks {
176 let result = if let Some(timeout) = self.timeout {
177 match tokio::time::timeout(timeout, handle).await {
178 Ok(Ok(Ok(text))) => {
179 let _ = state.set(format!("_result_{task_name}"), &text);
180 Ok(text)
181 }
182 Ok(Ok(Err(e))) => Err(AgentError::Other(e)),
183 Ok(Err(e)) => Err(AgentError::Other(format!("Join error: {e}"))),
184 Err(_) => {
185 let _ = self.middleware.run_on_event(&AgentEvent::Timeout).await;
186 Err(AgentError::Timeout)
187 }
188 }
189 } else {
190 match handle.await {
191 Ok(Ok(text)) => {
192 let _ = state.set(format!("_result_{task_name}"), &text);
193 Ok(text)
194 }
195 Ok(Err(e)) => Err(AgentError::Other(e)),
196 Err(e) => Err(AgentError::Other(format!("Join error: {e}"))),
197 }
198 };
199
200 results.push(result?);
201 }
202
203 let combined = results.join("\n");
204 let _ = state.set("output", &combined);
205 Ok(combined)
206 }
207}