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#[derive(Clone)]
29pub struct ResolvedContext {
30 pub system_instruction: String,
32 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}