Skip to main content

adk_core/
agent.rs

1use crate::{InvocationContext, Result, event::Event};
2use async_trait::async_trait;
3use futures::stream::Stream;
4use std::pin::Pin;
5use std::sync::Arc;
6
7pub type EventStream = Pin<Box<dyn Stream<Item = Result<Event>> + Send>>;
8
9#[async_trait]
10pub trait Agent: Send + Sync {
11    fn name(&self) -> &str;
12    fn description(&self) -> &str;
13    fn sub_agents(&self) -> &[Arc<dyn Agent>];
14
15    async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream>;
16}
17
18/// A validated context containing engineered instructions and resolved tool instances.
19///
20/// This structure serves as the "Atomic Unit of Capability" for an agent. It guarantees
21/// that the agent's cognitive frame (the instructions telling it what it can do) is
22/// perfectly aligned with its physical capabilities (the binary tool instances bound
23/// to the session).
24///
25/// By using `ResolvedContext`, the framework eliminates "Phantom Tool" hallucinations,
26/// where an agent tries to call a tool that was mentioned in its prompt but never
27/// actually registered in the runtime.
28#[derive(Clone)]
29pub struct ResolvedContext {
30    /// The engineered system instruction.
31    pub system_instruction: String,
32    /// The resolved, executable tools.
33    pub active_tools: Vec<Arc<dyn crate::Tool>>,
34}
35
36impl std::fmt::Debug for ResolvedContext {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        f.debug_struct("ResolvedContext")
39            .field("system_instruction_len", &self.system_instruction.len())
40            .field("active_tools_count", &self.active_tools.len())
41            .finish()
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::{Content, ReadonlyContext, RunConfig};
49    use async_stream::stream;
50
51    struct TestAgent {
52        name: String,
53    }
54
55    use crate::{CallbackContext, Session, State};
56    use std::collections::HashMap;
57
58    struct MockState;
59    impl State for MockState {
60        fn get(&self, _key: &str) -> Option<serde_json::Value> {
61            None
62        }
63        fn set(&mut self, _key: String, _value: serde_json::Value) {}
64        fn all(&self) -> HashMap<String, serde_json::Value> {
65            HashMap::new()
66        }
67    }
68
69    struct MockSession;
70    impl Session for MockSession {
71        fn id(&self) -> &str {
72            "session"
73        }
74        fn app_name(&self) -> &str {
75            "app"
76        }
77        fn user_id(&self) -> &str {
78            "user"
79        }
80        fn state(&self) -> &dyn State {
81            &MockState
82        }
83        fn conversation_history(&self) -> Vec<Content> {
84            Vec::new()
85        }
86    }
87
88    #[allow(dead_code)]
89    struct TestContext {
90        content: Content,
91        config: RunConfig,
92        session: MockSession,
93    }
94
95    #[allow(dead_code)]
96    impl TestContext {
97        fn new() -> Self {
98            Self {
99                content: Content::new("user"),
100                config: RunConfig::default(),
101                session: MockSession,
102            }
103        }
104    }
105
106    #[async_trait]
107    impl ReadonlyContext for TestContext {
108        fn invocation_id(&self) -> &str {
109            "test"
110        }
111        fn agent_name(&self) -> &str {
112            "test"
113        }
114        fn user_id(&self) -> &str {
115            "user"
116        }
117        fn app_name(&self) -> &str {
118            "app"
119        }
120        fn session_id(&self) -> &str {
121            "session"
122        }
123        fn branch(&self) -> &str {
124            ""
125        }
126        fn user_content(&self) -> &Content {
127            &self.content
128        }
129    }
130
131    #[async_trait]
132    impl CallbackContext for TestContext {
133        fn artifacts(&self) -> Option<Arc<dyn crate::Artifacts>> {
134            None
135        }
136    }
137
138    #[async_trait]
139    impl InvocationContext for TestContext {
140        fn agent(&self) -> Arc<dyn Agent> {
141            unimplemented!()
142        }
143        fn memory(&self) -> Option<Arc<dyn crate::Memory>> {
144            None
145        }
146        fn session(&self) -> &dyn Session {
147            &self.session
148        }
149        fn run_config(&self) -> &RunConfig {
150            &self.config
151        }
152        fn end_invocation(&self) {}
153        fn ended(&self) -> bool {
154            false
155        }
156    }
157
158    #[async_trait]
159    impl Agent for TestAgent {
160        fn name(&self) -> &str {
161            &self.name
162        }
163
164        fn description(&self) -> &str {
165            "test agent"
166        }
167
168        fn sub_agents(&self) -> &[Arc<dyn Agent>] {
169            &[]
170        }
171
172        async fn run(&self, _ctx: Arc<dyn InvocationContext>) -> Result<EventStream> {
173            let s = stream! {
174                yield Ok(Event::new("test"));
175            };
176            Ok(Box::pin(s))
177        }
178    }
179
180    #[test]
181    fn test_agent_trait() {
182        let agent = TestAgent { name: "test".to_string() };
183        assert_eq!(agent.name(), "test");
184        assert_eq!(agent.description(), "test agent");
185    }
186}