1use adk_core::{
2 AdkIdentity, Agent, AppName, Artifacts, CallbackContext, Content, Event, ExecutionIdentity,
3 InvocationContext as InvocationContextTrait, InvocationId, Memory, ReadonlyContext,
4 RequestContext, RunConfig, SecretService, SessionId, UserId,
5};
6use adk_session::Session as AdkSession;
7use async_trait::async_trait;
8use std::collections::HashMap;
9use std::sync::{Arc, RwLock, atomic::AtomicBool};
10
11pub struct MutableSession {
18 inner: Arc<dyn AdkSession>,
20 state: Arc<RwLock<HashMap<String, serde_json::Value>>>,
23 events: Arc<RwLock<Vec<Event>>>,
25}
26
27impl MutableSession {
28 pub fn new(session: Arc<dyn AdkSession>) -> Self {
31 let initial_state = session.state().all();
33 let initial_events = session.events().all();
35
36 Self {
37 inner: session,
38 state: Arc::new(RwLock::new(initial_state)),
39 events: Arc::new(RwLock::new(initial_events)),
40 }
41 }
42
43 pub fn apply_state_delta(&self, delta: &HashMap<String, serde_json::Value>) {
46 if delta.is_empty() {
47 return;
48 }
49
50 let Ok(mut state) = self.state.write() else {
51 tracing::error!("state RwLock poisoned in apply_state_delta — skipping delta");
52 return;
53 };
54 for (key, value) in delta {
55 if !key.starts_with("temp:") {
57 state.insert(key.clone(), value.clone());
58 }
59 }
60 }
61
62 pub fn append_event(&self, event: Event) {
65 let Ok(mut events) = self.events.write() else {
66 tracing::error!("events RwLock poisoned in append_event — event dropped");
67 return;
68 };
69 events.push(event);
70 }
71
72 pub fn events_snapshot(&self) -> Vec<Event> {
75 let Ok(events) = self.events.read() else {
76 tracing::error!("events RwLock poisoned in events_snapshot — returning empty");
77 return Vec::new();
78 };
79 events.clone()
80 }
81
82 pub fn replace_events(&self, new_events: Vec<Event>) {
84 let Ok(mut events) = self.events.write() else {
85 tracing::error!("events RwLock poisoned in replace_events — events unchanged");
86 return;
87 };
88 *events = new_events;
89 }
90
91 pub fn events_len(&self) -> usize {
93 let Ok(events) = self.events.read() else {
94 tracing::error!("events RwLock poisoned in events_len — returning 0");
95 return 0;
96 };
97 events.len()
98 }
99
100 pub fn conversation_history_for_agent_impl(
115 &self,
116 agent_name: Option<&str>,
117 branch: &str,
118 ) -> Vec<adk_core::Content> {
119 let Ok(events) = self.events.read() else {
120 tracing::error!("events RwLock poisoned in conversation_history — returning empty");
121 return Vec::new();
122 };
123 let mut history = Vec::new();
124
125 let mut compaction_boundary = None;
129 for event in events.iter().rev() {
130 if let Some(ref compaction) = event.actions.compaction {
131 history.push(compaction.compacted_content.clone());
132 compaction_boundary = Some(compaction.end_timestamp);
133 break;
134 }
135 }
136
137 for event in events.iter() {
138 if event.actions.compaction.is_some() {
140 continue;
141 }
142
143 if let Some(boundary) = compaction_boundary
145 && event.timestamp <= boundary
146 {
147 continue;
148 }
149
150 if !adk_core::event_belongs_to_branch(branch, &event.branch) {
154 continue;
155 }
156
157 if let Some(name) = agent_name
163 && event.author != "user"
164 && event.author != name
165 {
166 continue;
167 }
168
169 if let Some(content) = &event.llm_response.content {
170 let mut mapped_content = content.clone();
171 mapped_content.role = match (event.author.as_str(), content.role.as_str()) {
172 ("user", _) => "user",
173 (_, "function" | "tool") => content.role.as_str(),
174 _ => "model",
175 }
176 .to_string();
177 history.push(mapped_content);
178 }
179 }
180
181 history
182 }
183}
184
185impl adk_core::Session for MutableSession {
186 fn id(&self) -> &str {
187 self.inner.id()
188 }
189
190 fn app_name(&self) -> &str {
191 self.inner.app_name()
192 }
193
194 fn user_id(&self) -> &str {
195 self.inner.user_id()
196 }
197
198 fn state(&self) -> &dyn adk_core::State {
199 self
200 }
201
202 fn conversation_history(&self) -> Vec<adk_core::Content> {
203 self.conversation_history_for_agent_impl(None, "")
204 }
205
206 fn conversation_history_for_agent(&self, agent_name: &str) -> Vec<adk_core::Content> {
207 self.conversation_history_for_agent_impl(Some(agent_name), "")
208 }
209
210 fn conversation_history_scoped(
211 &self,
212 agent_name: Option<&str>,
213 branch: &str,
214 ) -> Vec<adk_core::Content> {
215 self.conversation_history_for_agent_impl(agent_name, branch)
216 }
217}
218
219impl adk_core::State for MutableSession {
220 fn get(&self, key: &str) -> Option<serde_json::Value> {
221 let Ok(state) = self.state.read() else {
222 tracing::error!("state RwLock poisoned in State::get — returning None");
223 return None;
224 };
225 state.get(key).cloned()
226 }
227
228 fn set(&mut self, key: String, value: serde_json::Value) {
229 if let Err(msg) = adk_core::validate_state_key(&key) {
230 tracing::warn!(key = %key, "rejecting invalid state key: {msg}");
231 return;
232 }
233 let Ok(mut state) = self.state.write() else {
234 tracing::error!("state RwLock poisoned in State::set — value dropped");
235 return;
236 };
237 state.insert(key, value);
238 }
239
240 fn all(&self) -> HashMap<String, serde_json::Value> {
241 let Ok(state) = self.state.read() else {
242 tracing::error!("state RwLock poisoned in State::all — returning empty");
243 return HashMap::new();
244 };
245 state.clone()
246 }
247}
248
249pub struct InvocationContext {
255 identity: ExecutionIdentity,
256 agent: Arc<dyn Agent>,
257 user_content: Content,
258 artifacts: Option<Arc<dyn Artifacts>>,
259 memory: Option<Arc<dyn Memory>>,
260 run_config: RunConfig,
261 ended: Arc<AtomicBool>,
262 session: Arc<MutableSession>,
266 request_context: Option<RequestContext>,
270 shared_state: Option<Arc<adk_core::SharedState>>,
272 secret_service: Option<Arc<dyn SecretService>>,
275 cancellation_token: Option<tokio_util::sync::CancellationToken>,
281 orchestration_root_invocation_id: String,
283}
284
285impl InvocationContext {
286 pub fn new_typed(
288 invocation_id: String,
289 agent: Arc<dyn Agent>,
290 user_id: UserId,
291 app_name: AppName,
292 session_id: SessionId,
293 user_content: Content,
294 session: Arc<dyn AdkSession>,
295 ) -> adk_core::Result<Self> {
296 let orchestration_root_invocation_id = invocation_id.clone();
297 let identity = ExecutionIdentity {
298 adk: AdkIdentity { app_name, user_id, session_id },
299 invocation_id: InvocationId::try_from(invocation_id)?,
300 branch: String::new(),
301 agent_name: agent.name().to_string(),
302 };
303 Ok(Self {
304 identity,
305 agent,
306 user_content,
307 artifacts: None,
308 memory: None,
309 run_config: RunConfig::default(),
310 ended: Arc::new(AtomicBool::new(false)),
311 session: Arc::new(MutableSession::new(session)),
312 request_context: None,
313 shared_state: None,
314 secret_service: None,
315 cancellation_token: None,
316 orchestration_root_invocation_id,
317 })
318 }
319
320 pub fn new(
325 invocation_id: String,
326 agent: Arc<dyn Agent>,
327 user_id: String,
328 app_name: String,
329 session_id: String,
330 user_content: Content,
331 session: Arc<dyn AdkSession>,
332 ) -> adk_core::Result<Self> {
333 Self::new_typed(
334 invocation_id,
335 agent,
336 UserId::try_from(user_id)?,
337 AppName::try_from(app_name)?,
338 SessionId::try_from(session_id)?,
339 user_content,
340 session,
341 )
342 }
343
344 pub fn with_mutable_session_typed(
347 invocation_id: String,
348 agent: Arc<dyn Agent>,
349 user_id: UserId,
350 app_name: AppName,
351 session_id: SessionId,
352 user_content: Content,
353 session: Arc<MutableSession>,
354 ) -> adk_core::Result<Self> {
355 let orchestration_root_invocation_id = invocation_id.clone();
356 let identity = ExecutionIdentity {
357 adk: AdkIdentity { app_name, user_id, session_id },
358 invocation_id: InvocationId::try_from(invocation_id)?,
359 branch: String::new(),
360 agent_name: agent.name().to_string(),
361 };
362 Ok(Self {
363 identity,
364 agent,
365 user_content,
366 artifacts: None,
367 memory: None,
368 run_config: RunConfig::default(),
369 ended: Arc::new(AtomicBool::new(false)),
370 session,
371 request_context: None,
372 shared_state: None,
373 secret_service: None,
374 cancellation_token: None,
375 orchestration_root_invocation_id,
376 })
377 }
378
379 pub fn with_mutable_session(
383 invocation_id: String,
384 agent: Arc<dyn Agent>,
385 user_id: String,
386 app_name: String,
387 session_id: String,
388 user_content: Content,
389 session: Arc<MutableSession>,
390 ) -> adk_core::Result<Self> {
391 Self::with_mutable_session_typed(
392 invocation_id,
393 agent,
394 UserId::try_from(user_id)?,
395 AppName::try_from(app_name)?,
396 SessionId::try_from(session_id)?,
397 user_content,
398 session,
399 )
400 }
401
402 pub fn with_branch(mut self, branch: String) -> Self {
404 self.identity.branch = branch;
405 self
406 }
407
408 pub fn with_artifacts(mut self, artifacts: Arc<dyn Artifacts>) -> Self {
410 self.artifacts = Some(artifacts);
411 self
412 }
413
414 pub fn with_memory(mut self, memory: Arc<dyn Memory>) -> Self {
416 self.memory = Some(memory);
417 self
418 }
419
420 pub fn with_run_config(mut self, config: RunConfig) -> Self {
422 self.run_config = config;
423 self
424 }
425
426 pub fn with_request_context(mut self, ctx: RequestContext) -> Self {
434 self.request_context = Some(ctx);
435 self
436 }
437
438 pub fn with_shared_state(mut self, shared: Arc<adk_core::SharedState>) -> Self {
440 self.shared_state = Some(shared);
441 self
442 }
443
444 pub fn with_secret_service(mut self, service: Arc<dyn SecretService>) -> Self {
450 self.secret_service = Some(service);
451 self
452 }
453
454 pub fn with_cancellation_token(mut self, token: tokio_util::sync::CancellationToken) -> Self {
460 self.cancellation_token = Some(token);
461 self
462 }
463
464 pub fn with_orchestration_root_invocation_id(mut self, invocation_id: String) -> Self {
466 self.orchestration_root_invocation_id = invocation_id;
467 self
468 }
469
470 pub fn mutable_session(&self) -> &Arc<MutableSession> {
473 &self.session
474 }
475}
476
477#[async_trait]
478impl ReadonlyContext for InvocationContext {
479 fn invocation_id(&self) -> &str {
480 self.identity.invocation_id.as_ref()
481 }
482
483 fn agent_name(&self) -> &str {
484 self.agent.name()
485 }
486
487 fn user_id(&self) -> &str {
488 self.request_context.as_ref().map_or(self.identity.adk.user_id.as_ref(), |rc| &rc.user_id)
494 }
495
496 fn app_name(&self) -> &str {
497 self.identity.adk.app_name.as_ref()
498 }
499
500 fn session_id(&self) -> &str {
501 self.identity.adk.session_id.as_ref()
502 }
503
504 fn branch(&self) -> &str {
505 &self.identity.branch
506 }
507
508 fn user_content(&self) -> &Content {
509 &self.user_content
510 }
511}
512
513#[async_trait]
514impl CallbackContext for InvocationContext {
515 fn artifacts(&self) -> Option<Arc<dyn Artifacts>> {
516 self.artifacts.clone()
517 }
518
519 fn shared_state(&self) -> Option<Arc<adk_core::SharedState>> {
520 self.shared_state.clone()
521 }
522}
523
524#[async_trait]
525impl InvocationContextTrait for InvocationContext {
526 fn agent(&self) -> Arc<dyn Agent> {
527 self.agent.clone()
528 }
529
530 fn memory(&self) -> Option<Arc<dyn Memory>> {
531 self.memory.clone()
532 }
533
534 fn session(&self) -> &dyn adk_core::Session {
535 self.session.as_ref()
536 }
537
538 fn run_config(&self) -> &RunConfig {
539 &self.run_config
540 }
541
542 fn end_invocation(&self) {
543 self.ended.store(true, std::sync::atomic::Ordering::SeqCst);
544 }
545
546 fn ended(&self) -> bool {
547 self.ended.load(std::sync::atomic::Ordering::SeqCst)
548 }
549
550 fn is_cancelled(&self) -> bool {
551 self.cancellation_token
552 .as_ref()
553 .is_some_and(tokio_util::sync::CancellationToken::is_cancelled)
554 }
555
556 fn user_scopes(&self) -> Vec<String> {
557 self.request_context.as_ref().map_or_else(Vec::new, |rc| rc.scopes.clone())
558 }
559
560 fn request_metadata(&self) -> HashMap<String, serde_json::Value> {
561 self.request_context.as_ref().map_or_else(HashMap::new, |rc| {
562 rc.metadata
563 .iter()
564 .map(|(k, v)| (k.clone(), serde_json::Value::String(v.clone())))
565 .collect()
566 })
567 }
568
569 fn orchestration_root_invocation_id(&self) -> &str {
570 &self.orchestration_root_invocation_id
571 }
572
573 async fn get_secret(&self, name: &str) -> adk_core::Result<Option<String>> {
574 let request = adk_core::SecretRequest::new(name)
575 .with_identity(self.app_name(), self.user_id(), self.session_id())
576 .with_invocation_id(self.invocation_id());
577 self.get_secret_for(&request).await
578 }
579
580 async fn get_secret_for(
581 &self,
582 request: &adk_core::SecretRequest,
583 ) -> adk_core::Result<Option<String>> {
584 match &self.secret_service {
585 Some(service) => service.get_secret_for(request).await.map(Some),
586 None => Ok(None),
587 }
588 }
589}