adk_runner/
context.rs

1use adk_core::{
2    Agent, Artifacts, CallbackContext, Content, InvocationContext as InvocationContextTrait,
3    Memory, ReadonlyContext, RunConfig,
4};
5use adk_session::{Session as AdkSession, State as AdkState};
6use async_trait::async_trait;
7use std::collections::HashMap;
8use std::sync::{atomic::AtomicBool, Arc};
9
10// Adapter to bridge adk_session::State to adk_core::State
11#[allow(dead_code)] // Used via trait implementation
12struct StateAdapter<'a>(&'a dyn AdkState);
13
14impl<'a> adk_core::State for StateAdapter<'a> {
15    fn get(&self, key: &str) -> Option<serde_json::Value> {
16        self.0.get(key)
17    }
18
19    fn set(&mut self, _key: String, _value: serde_json::Value) {
20        // State updates should happen via EventActions, not direct mutation
21        // This is a read-only view of the state
22        panic!("Direct state mutation not supported in InvocationContext");
23    }
24
25    fn all(&self) -> HashMap<String, serde_json::Value> {
26        self.0.all()
27    }
28}
29
30// Adapter to bridge adk_session::Session to adk_core::Session
31struct SessionAdapter(Arc<dyn AdkSession>);
32
33impl adk_core::Session for SessionAdapter {
34    fn id(&self) -> &str {
35        self.0.id()
36    }
37
38    fn app_name(&self) -> &str {
39        self.0.app_name()
40    }
41
42    fn user_id(&self) -> &str {
43        self.0.user_id()
44    }
45
46    fn state(&self) -> &dyn adk_core::State {
47        // This is tricky because we need to return a reference to something that implements adk_core::State
48        // But StateAdapter wraps a reference.
49        // We can't easily return a reference to a temporary StateAdapter.
50        // For now, we might need to unsafe cast or rethink.
51        // Actually, since we can't return a reference to a temporary, we might need to implement State on the SessionAdapter itself?
52        // Or change adk_core::Session to return a Box or Arc?
53        // But we can't change adk_core easily.
54
55        // HACK: For now, we will panic if state is accessed, or we need a better solution.
56        // Wait, we can implement adk_core::State for SessionAdapter directly and return self?
57        // No, SessionAdapter implements Session.
58
59        // Let's implement adk_core::State for SessionAdapter (delegating to inner state)
60        // and return self.
61        unsafe { &*(self as *const Self as *const dyn adk_core::State) }
62    }
63
64    fn conversation_history(&self) -> Vec<adk_core::Content> {
65        // Convert session events to Content items for conversation history
66        let events = self.0.events();
67        let mut history = Vec::new();
68
69        for event in events.all() {
70            // Get content from the LlmResponse
71            if let Some(content) = event.llm_response.content {
72                // Map author to role
73                let role = match event.author.as_str() {
74                    "user" => "user".to_string(),
75                    _ => "model".to_string(),
76                };
77
78                // Create content with correct role
79                let mut mapped_content = content;
80                mapped_content.role = role;
81                history.push(mapped_content);
82            }
83        }
84
85        history
86    }
87}
88
89impl adk_core::State for SessionAdapter {
90    fn get(&self, key: &str) -> Option<serde_json::Value> {
91        self.0.state().get(key)
92    }
93
94    fn set(&mut self, _key: String, _value: serde_json::Value) {
95        panic!("Direct state mutation not supported");
96    }
97
98    fn all(&self) -> HashMap<String, serde_json::Value> {
99        self.0.state().all()
100    }
101}
102
103pub struct InvocationContext {
104    invocation_id: String,
105    agent: Arc<dyn Agent>,
106    user_id: String,
107    app_name: String,
108    session_id: String,
109    branch: String,
110    user_content: Content,
111    artifacts: Option<Arc<dyn Artifacts>>,
112    memory: Option<Arc<dyn Memory>>,
113    run_config: RunConfig,
114    ended: Arc<AtomicBool>,
115    session: Arc<SessionAdapter>,
116}
117
118impl InvocationContext {
119    pub fn new(
120        invocation_id: String,
121        agent: Arc<dyn Agent>,
122        user_id: String,
123        app_name: String,
124        session_id: String,
125        user_content: Content,
126        session: Arc<dyn AdkSession>,
127    ) -> Self {
128        Self {
129            invocation_id,
130            agent,
131            user_id,
132            app_name,
133            session_id,
134            branch: String::new(),
135            user_content,
136            artifacts: None,
137            memory: None,
138            run_config: RunConfig::default(),
139            ended: Arc::new(AtomicBool::new(false)),
140            session: Arc::new(SessionAdapter(session)),
141        }
142    }
143
144    pub fn with_branch(mut self, branch: String) -> Self {
145        self.branch = branch;
146        self
147    }
148
149    pub fn with_artifacts(mut self, artifacts: Arc<dyn Artifacts>) -> Self {
150        self.artifacts = Some(artifacts);
151        self
152    }
153
154    pub fn with_memory(mut self, memory: Arc<dyn Memory>) -> Self {
155        self.memory = Some(memory);
156        self
157    }
158
159    pub fn with_run_config(mut self, config: RunConfig) -> Self {
160        self.run_config = config;
161        self
162    }
163}
164
165#[async_trait]
166impl ReadonlyContext for InvocationContext {
167    fn invocation_id(&self) -> &str {
168        &self.invocation_id
169    }
170
171    fn agent_name(&self) -> &str {
172        self.agent.name()
173    }
174
175    fn user_id(&self) -> &str {
176        &self.user_id
177    }
178
179    fn app_name(&self) -> &str {
180        &self.app_name
181    }
182
183    fn session_id(&self) -> &str {
184        &self.session_id
185    }
186
187    fn branch(&self) -> &str {
188        &self.branch
189    }
190
191    fn user_content(&self) -> &Content {
192        &self.user_content
193    }
194}
195
196#[async_trait]
197impl CallbackContext for InvocationContext {
198    fn artifacts(&self) -> Option<Arc<dyn Artifacts>> {
199        self.artifacts.clone()
200    }
201}
202
203#[async_trait]
204impl InvocationContextTrait for InvocationContext {
205    fn agent(&self) -> Arc<dyn Agent> {
206        self.agent.clone()
207    }
208
209    fn memory(&self) -> Option<Arc<dyn Memory>> {
210        self.memory.clone()
211    }
212
213    fn session(&self) -> &dyn adk_core::Session {
214        self.session.as_ref()
215    }
216
217    fn run_config(&self) -> &RunConfig {
218        &self.run_config
219    }
220
221    fn end_invocation(&self) {
222        self.ended.store(true, std::sync::atomic::Ordering::SeqCst);
223    }
224
225    fn ended(&self) -> bool {
226        self.ended.load(std::sync::atomic::Ordering::SeqCst)
227    }
228}