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}
282
283impl InvocationContext {
284 pub fn new_typed(
286 invocation_id: String,
287 agent: Arc<dyn Agent>,
288 user_id: UserId,
289 app_name: AppName,
290 session_id: SessionId,
291 user_content: Content,
292 session: Arc<dyn AdkSession>,
293 ) -> adk_core::Result<Self> {
294 let identity = ExecutionIdentity {
295 adk: AdkIdentity { app_name, user_id, session_id },
296 invocation_id: InvocationId::try_from(invocation_id)?,
297 branch: String::new(),
298 agent_name: agent.name().to_string(),
299 };
300 Ok(Self {
301 identity,
302 agent,
303 user_content,
304 artifacts: None,
305 memory: None,
306 run_config: RunConfig::default(),
307 ended: Arc::new(AtomicBool::new(false)),
308 session: Arc::new(MutableSession::new(session)),
309 request_context: None,
310 shared_state: None,
311 secret_service: None,
312 cancellation_token: None,
313 })
314 }
315
316 pub fn new(
321 invocation_id: String,
322 agent: Arc<dyn Agent>,
323 user_id: String,
324 app_name: String,
325 session_id: String,
326 user_content: Content,
327 session: Arc<dyn AdkSession>,
328 ) -> adk_core::Result<Self> {
329 Self::new_typed(
330 invocation_id,
331 agent,
332 UserId::try_from(user_id)?,
333 AppName::try_from(app_name)?,
334 SessionId::try_from(session_id)?,
335 user_content,
336 session,
337 )
338 }
339
340 pub fn with_mutable_session_typed(
343 invocation_id: String,
344 agent: Arc<dyn Agent>,
345 user_id: UserId,
346 app_name: AppName,
347 session_id: SessionId,
348 user_content: Content,
349 session: Arc<MutableSession>,
350 ) -> adk_core::Result<Self> {
351 let identity = ExecutionIdentity {
352 adk: AdkIdentity { app_name, user_id, session_id },
353 invocation_id: InvocationId::try_from(invocation_id)?,
354 branch: String::new(),
355 agent_name: agent.name().to_string(),
356 };
357 Ok(Self {
358 identity,
359 agent,
360 user_content,
361 artifacts: None,
362 memory: None,
363 run_config: RunConfig::default(),
364 ended: Arc::new(AtomicBool::new(false)),
365 session,
366 request_context: None,
367 shared_state: None,
368 secret_service: None,
369 cancellation_token: None,
370 })
371 }
372
373 pub fn with_mutable_session(
377 invocation_id: String,
378 agent: Arc<dyn Agent>,
379 user_id: String,
380 app_name: String,
381 session_id: String,
382 user_content: Content,
383 session: Arc<MutableSession>,
384 ) -> adk_core::Result<Self> {
385 Self::with_mutable_session_typed(
386 invocation_id,
387 agent,
388 UserId::try_from(user_id)?,
389 AppName::try_from(app_name)?,
390 SessionId::try_from(session_id)?,
391 user_content,
392 session,
393 )
394 }
395
396 pub fn with_branch(mut self, branch: String) -> Self {
398 self.identity.branch = branch;
399 self
400 }
401
402 pub fn with_artifacts(mut self, artifacts: Arc<dyn Artifacts>) -> Self {
404 self.artifacts = Some(artifacts);
405 self
406 }
407
408 pub fn with_memory(mut self, memory: Arc<dyn Memory>) -> Self {
410 self.memory = Some(memory);
411 self
412 }
413
414 pub fn with_run_config(mut self, config: RunConfig) -> Self {
416 self.run_config = config;
417 self
418 }
419
420 pub fn with_request_context(mut self, ctx: RequestContext) -> Self {
428 self.request_context = Some(ctx);
429 self
430 }
431
432 pub fn with_shared_state(mut self, shared: Arc<adk_core::SharedState>) -> Self {
434 self.shared_state = Some(shared);
435 self
436 }
437
438 pub fn with_secret_service(mut self, service: Arc<dyn SecretService>) -> Self {
444 self.secret_service = Some(service);
445 self
446 }
447
448 pub fn with_cancellation_token(mut self, token: tokio_util::sync::CancellationToken) -> Self {
454 self.cancellation_token = Some(token);
455 self
456 }
457
458 pub fn mutable_session(&self) -> &Arc<MutableSession> {
461 &self.session
462 }
463}
464
465#[async_trait]
466impl ReadonlyContext for InvocationContext {
467 fn invocation_id(&self) -> &str {
468 self.identity.invocation_id.as_ref()
469 }
470
471 fn agent_name(&self) -> &str {
472 self.agent.name()
473 }
474
475 fn user_id(&self) -> &str {
476 self.request_context.as_ref().map_or(self.identity.adk.user_id.as_ref(), |rc| &rc.user_id)
482 }
483
484 fn app_name(&self) -> &str {
485 self.identity.adk.app_name.as_ref()
486 }
487
488 fn session_id(&self) -> &str {
489 self.identity.adk.session_id.as_ref()
490 }
491
492 fn branch(&self) -> &str {
493 &self.identity.branch
494 }
495
496 fn user_content(&self) -> &Content {
497 &self.user_content
498 }
499}
500
501#[async_trait]
502impl CallbackContext for InvocationContext {
503 fn artifacts(&self) -> Option<Arc<dyn Artifacts>> {
504 self.artifacts.clone()
505 }
506
507 fn shared_state(&self) -> Option<Arc<adk_core::SharedState>> {
508 self.shared_state.clone()
509 }
510}
511
512#[async_trait]
513impl InvocationContextTrait for InvocationContext {
514 fn agent(&self) -> Arc<dyn Agent> {
515 self.agent.clone()
516 }
517
518 fn memory(&self) -> Option<Arc<dyn Memory>> {
519 self.memory.clone()
520 }
521
522 fn session(&self) -> &dyn adk_core::Session {
523 self.session.as_ref()
524 }
525
526 fn run_config(&self) -> &RunConfig {
527 &self.run_config
528 }
529
530 fn end_invocation(&self) {
531 self.ended.store(true, std::sync::atomic::Ordering::SeqCst);
532 }
533
534 fn ended(&self) -> bool {
535 self.ended.load(std::sync::atomic::Ordering::SeqCst)
536 }
537
538 fn is_cancelled(&self) -> bool {
539 self.cancellation_token
540 .as_ref()
541 .is_some_and(tokio_util::sync::CancellationToken::is_cancelled)
542 }
543
544 fn user_scopes(&self) -> Vec<String> {
545 self.request_context.as_ref().map_or_else(Vec::new, |rc| rc.scopes.clone())
546 }
547
548 fn request_metadata(&self) -> HashMap<String, serde_json::Value> {
549 self.request_context.as_ref().map_or_else(HashMap::new, |rc| {
550 rc.metadata
551 .iter()
552 .map(|(k, v)| (k.clone(), serde_json::Value::String(v.clone())))
553 .collect()
554 })
555 }
556
557 async fn get_secret(&self, name: &str) -> adk_core::Result<Option<String>> {
558 let request = adk_core::SecretRequest::new(name)
559 .with_identity(self.app_name(), self.user_id(), self.session_id())
560 .with_invocation_id(self.invocation_id());
561 self.get_secret_for(&request).await
562 }
563
564 async fn get_secret_for(
565 &self,
566 request: &adk_core::SecretRequest,
567 ) -> adk_core::Result<Option<String>> {
568 match &self.secret_service {
569 Some(service) => service.get_secret_for(request).await.map(Some),
570 None => Ok(None),
571 }
572 }
573}