ai_agents_runtime/
turn_context.rs1use serde::{Deserialize, Serialize};
2
3use tokio::task_local;
4
5task_local! {
6 static TURN_ACTOR_CONTEXT: TurnActorContext;
7}
8
9#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
11pub struct TurnActorContext {
12 #[serde(default, skip_serializing_if = "Option::is_none")]
14 pub origin_actor_id: Option<String>,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub sender_agent_id: Option<String>,
18}
19
20impl TurnActorContext {
21 pub fn new() -> Self {
23 Self::default()
24 }
25
26 pub fn with_origin_actor(mut self, actor_id: impl Into<String>) -> Self {
28 self.origin_actor_id = Some(actor_id.into());
29 self
30 }
31
32 pub fn with_sender_agent(mut self, agent_id: impl Into<String>) -> Self {
34 self.sender_agent_id = Some(agent_id.into());
35 self
36 }
37
38 pub fn for_sender(&self, agent_id: impl Into<String>) -> Self {
40 let mut next = self.clone();
41 next.sender_agent_id = Some(agent_id.into());
42 next
43 }
44
45 pub fn effective_actor_id(&self) -> Option<&str> {
48 self.origin_actor_id
49 .as_deref()
50 .or(self.sender_agent_id.as_deref())
51 }
52
53 pub fn is_empty(&self) -> bool {
55 self.origin_actor_id.is_none() && self.sender_agent_id.is_none()
56 }
57}
58
59pub(crate) async fn scope_actor_context<F, T>(context: TurnActorContext, future: F) -> T
60where
61 F: std::future::Future<Output = T> + Send,
62 T: Send,
63{
64 TURN_ACTOR_CONTEXT.scope(context, future).await
65}
66
67pub(crate) fn current_turn_actor_context() -> Option<TurnActorContext> {
68 TURN_ACTOR_CONTEXT.try_with(Clone::clone).ok()
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_effective_actor_prefers_origin() {
77 let ctx = TurnActorContext::new()
78 .with_origin_actor("user_1")
79 .with_sender_agent("agent_a");
80 assert_eq!(ctx.effective_actor_id(), Some("user_1"));
81 }
82
83 #[test]
84 fn test_effective_actor_falls_back_to_sender() {
85 let ctx = TurnActorContext::new().with_sender_agent("agent_a");
86 assert_eq!(ctx.effective_actor_id(), Some("agent_a"));
87 }
88
89 #[test]
90 fn test_for_sender_preserves_origin() {
91 let ctx = TurnActorContext::new().with_origin_actor("user_1");
92 let next = ctx.for_sender("agent_b");
93 assert_eq!(next.origin_actor_id.as_deref(), Some("user_1"));
94 assert_eq!(next.sender_agent_id.as_deref(), Some("agent_b"));
95 }
96}