use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::typed_id::{ExecId, MessageId, SessionId, TurnId};
mod act;
pub mod act_hooks;
mod input;
mod reason;
pub mod tool_scheduler;
pub use act::{ActAtom, ActInput, ActResult, ToolCallResult};
pub use act_hooks::{
ClientSideToolHook, ConnectionSetupHook, OutputHardLimitHook, PostActAction, PostActHook,
PostToolExecHook, PreToolUseDecision, PreToolUseHook,
};
pub use input::{InputAtom, InputAtomInput, InputAtomResult};
pub use reason::{ReasonAtom, ReasonInput, ReasonResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AtomContext {
pub session_id: SessionId,
pub turn_id: TurnId,
pub input_message_id: MessageId,
pub exec_id: ExecId,
}
impl AtomContext {
pub fn new(session_id: SessionId, turn_id: TurnId, input_message_id: MessageId) -> Self {
Self {
session_id,
turn_id,
input_message_id,
exec_id: ExecId::new(),
}
}
pub fn next_exec(&self) -> Self {
Self {
session_id: self.session_id,
turn_id: self.turn_id,
input_message_id: self.input_message_id,
exec_id: ExecId::new(),
}
}
}
#[async_trait]
pub trait Atom: Send + Sync {
type Input: Send;
type Output: Send;
fn name(&self) -> &'static str;
async fn execute(&self, input: Self::Input) -> Result<Self::Output>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atom_context_new() {
let session_id = SessionId::new();
let turn_id = TurnId::new();
let input_message_id = MessageId::new();
let context = AtomContext::new(session_id, turn_id, input_message_id);
assert_eq!(context.session_id, session_id);
assert_eq!(context.turn_id, turn_id);
assert_eq!(context.input_message_id, input_message_id);
assert!(!context.exec_id.uuid().is_nil());
}
#[test]
fn test_atom_context_next_exec() {
let session_id = SessionId::new();
let turn_id = TurnId::new();
let input_message_id = MessageId::new();
let context1 = AtomContext::new(session_id, turn_id, input_message_id);
let context2 = context1.next_exec();
assert_eq!(context2.session_id, context1.session_id);
assert_eq!(context2.turn_id, context1.turn_id);
assert_eq!(context2.input_message_id, context1.input_message_id);
assert_ne!(context2.exec_id, context1.exec_id);
}
#[test]
fn test_atom_context_serialization() {
let context = AtomContext::new(SessionId::new(), TurnId::new(), MessageId::new());
let json = serde_json::to_string(&context).unwrap();
let parsed: AtomContext = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.session_id, context.session_id);
assert_eq!(parsed.turn_id, context.turn_id);
assert_eq!(parsed.input_message_id, context.input_message_id);
assert_eq!(parsed.exec_id, context.exec_id);
}
}