adk_agent/team/
lifecycle.rs1use std::sync::Arc;
2
3use async_trait::async_trait;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use super::{RelationshipKind, TeamRuntimeError};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
11#[serde(rename_all = "camelCase")]
12pub enum TeamLifecyclePhase {
13 Team,
15 Member,
17 Relationship,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
23#[serde(rename_all = "camelCase")]
24pub struct TeamLifecycleContext {
25 pub team: String,
27 pub invocation_id: String,
29 pub phase: TeamLifecyclePhase,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub member: Option<String>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub edge_id: Option<String>,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub from: Option<String>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub to: Option<String>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub kind: Option<RelationshipKind>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub attempt: Option<u32>,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
53#[serde(rename_all = "camelCase", tag = "status")]
54pub enum TeamLifecycleOutcome {
55 Succeeded,
57 Failed {
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 code: Option<String>,
62 message: String,
64 },
65 Terminated {
67 reason: String,
69 },
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
74#[serde(rename_all = "camelCase", tag = "decision")]
75pub enum TeamLifecycleDecision {
76 Continue,
78 Terminate {
80 reason: String,
82 },
83}
84
85#[async_trait]
90pub trait TeamLifecycleHook: Send + Sync {
91 fn name(&self) -> &str;
93
94 fn priority(&self) -> i32 {
96 0
97 }
98
99 async fn before(
101 &self,
102 _context: &TeamLifecycleContext,
103 ) -> adk_core::Result<TeamLifecycleDecision> {
104 Ok(TeamLifecycleDecision::Continue)
105 }
106
107 async fn after(
109 &self,
110 _context: &TeamLifecycleContext,
111 _outcome: &TeamLifecycleOutcome,
112 ) -> adk_core::Result<()> {
113 Ok(())
114 }
115}
116
117pub(crate) struct TeamLifecycleManager {
118 hooks: Vec<Arc<dyn TeamLifecycleHook>>,
119}
120
121impl TeamLifecycleManager {
122 pub(crate) fn new(mut hooks: Vec<Arc<dyn TeamLifecycleHook>>) -> Self {
123 hooks.sort_by_key(|hook| hook.priority());
124 Self { hooks }
125 }
126
127 pub(crate) async fn before(
128 &self,
129 context: &TeamLifecycleContext,
130 ) -> adk_core::Result<TeamLifecycleDecision> {
131 for hook in &self.hooks {
132 match hook.before(context).await.map_err(|error| TeamRuntimeError::LifecycleHook {
133 hook: hook.name().to_string(),
134 phase: context.phase,
135 message: error.to_string(),
136 })? {
137 TeamLifecycleDecision::Continue => {}
138 decision @ TeamLifecycleDecision::Terminate { .. } => return Ok(decision),
139 }
140 }
141 Ok(TeamLifecycleDecision::Continue)
142 }
143
144 pub(crate) async fn after(
145 &self,
146 context: &TeamLifecycleContext,
147 outcome: &TeamLifecycleOutcome,
148 ) -> adk_core::Result<()> {
149 for hook in self.hooks.iter().rev() {
150 hook.after(context, outcome).await.map_err(|error| {
151 TeamRuntimeError::LifecycleHook {
152 hook: hook.name().to_string(),
153 phase: context.phase,
154 message: error.to_string(),
155 }
156 })?;
157 }
158 Ok(())
159 }
160}