everruns_engine/execution/
input.rs1use serde::{Deserialize, Serialize};
11
12use super::ExecutionContext;
13use crate::error::{AgentLoopError, Result};
14use crate::message::Message;
15use crate::message_retriever::MessageRetriever;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct InputAtomInput {
24 pub context: ExecutionContext,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct InputAtomResult {
31 pub message: Message,
33}
34
35pub struct InputAtom<M>
48where
49 M: MessageRetriever,
50{
51 message_retriever: M,
52}
53
54impl<M> InputAtom<M>
55where
56 M: MessageRetriever + Send + Sync,
57{
58 pub fn new(message_retriever: M) -> Self {
60 Self { message_retriever }
61 }
62 pub fn name(&self) -> &'static str {
64 "input"
65 }
66
67 pub async fn execute(&self, input: InputAtomInput) -> Result<InputAtomResult> {
69 let InputAtomInput { context } = input;
70
71 tracing::debug!(
72 session_id = %context.session_id,
73 turn_id = %context.turn_id,
74 input_message_id = %context.input_message_id,
75 exec_id = %context.exec_id,
76 "InputAtom: retrieving user message"
77 );
78
79 let message = self
81 .message_retriever
82 .get(context.session_id, context.input_message_id)
83 .await?
84 .ok_or_else(|| {
85 AgentLoopError::store(format!(
86 "User message not found: {}",
87 context.input_message_id
88 ))
89 })?;
90
91 tracing::info!(
92 session_id = %context.session_id,
93 turn_id = %context.turn_id,
94 message_id = %message.id,
95 "InputAtom: turn started with user message"
96 );
97
98 Ok(InputAtomResult { message })
99 }
100}
101
102#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::message_retriever::InputMessage;
110 use crate::test_fixtures::TestMessageRetriever;
111 use crate::typed_id::{MessageId, SessionId, TurnId};
112
113 #[tokio::test]
114 async fn test_input_atom_retrieves_message() {
115 let retriever = TestMessageRetriever::new();
116 let session_id = SessionId::new();
117 let turn_id = TurnId::new();
118
119 let user_message = retriever
121 .add(session_id, InputMessage::user("Hello, world!"))
122 .await
123 .unwrap();
124
125 let context = ExecutionContext::new(session_id, turn_id, user_message.id);
126 let atom = InputAtom::new(retriever);
127
128 let result = atom.execute(InputAtomInput { context }).await.unwrap();
129
130 assert_eq!(result.message.id, user_message.id);
131 assert_eq!(result.message.text(), Some("Hello, world!"));
132 }
133
134 #[tokio::test]
135 async fn test_input_atom_not_found() {
136 let retriever = TestMessageRetriever::new();
137 let session_id = SessionId::new();
138 let turn_id = TurnId::new();
139 let missing_id = MessageId::new();
140
141 let context = ExecutionContext::new(session_id, turn_id, missing_id);
142 let atom = InputAtom::new(retriever);
143
144 let result = atom.execute(InputAtomInput { context }).await;
145
146 assert!(result.is_err());
147 }
148}