Skip to main content

autoagents_core/agent/
task.rs

1#[cfg(not(target_arch = "wasm32"))]
2use crate::actor::{ActorMessage, CloneableMessage};
3use crate::protocol::SubmissionId;
4use autoagents_llm::chat::ImageMime;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use uuid::Uuid;
8
9/// A unit of work submitted to an agent. Tasks carry a user prompt, optional
10/// image payload for multimodal models, a unique submission id, and a place to
11/// store a final result when executed within an actor runtime.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Task {
14    pub prompt: String,
15    pub image: Option<(ImageMime, Vec<u8>)>,
16    pub submission_id: SubmissionId,
17    pub completed: bool,
18    pub result: Option<Value>,
19}
20
21impl Task {
22    /// Create a new text-only task with a fresh submission id.
23    pub fn new<T: Into<String>>(task: T) -> Self {
24        Self {
25            prompt: task.into(),
26            image: None,
27            submission_id: Uuid::new_v4(),
28            completed: false,
29            result: None,
30        }
31    }
32
33    /// Create a new task with an image payload (e.g., for vision-capable
34    /// chat models) and a fresh submission id.
35    pub fn new_with_image<T: Into<String>>(
36        task: T,
37        image_mime: ImageMime,
38        image_data: Vec<u8>,
39    ) -> Self {
40        Self {
41            prompt: task.into(),
42            image: Some((image_mime, image_data)),
43            submission_id: Uuid::new_v4(),
44            completed: false,
45            result: None,
46        }
47    }
48}
49
50#[cfg(not(target_arch = "wasm32"))]
51impl ActorMessage for Task {}
52#[cfg(not(target_arch = "wasm32"))]
53impl CloneableMessage for Task {}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use serde_json::json;
59
60    #[test]
61    fn test_task_creation() {
62        let task = Task::new("Test task");
63
64        assert_eq!(task.prompt, "Test task");
65        assert!(!task.completed);
66        assert!(task.result.is_none());
67        assert!(!task.submission_id.is_nil());
68    }
69
70    #[test]
71    fn test_task_creation_with_string() {
72        let task_str = "Another test task".to_string();
73        let task = Task::new(task_str);
74
75        assert_eq!(task.prompt, "Another test task");
76    }
77
78    #[test]
79    fn test_task_serialization() {
80        let task = Task::new("Serialize me");
81
82        // Test serialization
83        let serialized = serde_json::to_string(&task).unwrap();
84        assert!(serialized.contains("Serialize me"));
85        assert!(serialized.contains("submission_id"));
86        assert!(serialized.contains("completed"));
87
88        // Test deserialization
89        let deserialized: Task = serde_json::from_str(&serialized).unwrap();
90        assert_eq!(deserialized.prompt, task.prompt);
91        assert_eq!(deserialized.submission_id, task.submission_id);
92        assert_eq!(deserialized.completed, task.completed);
93    }
94
95    #[test]
96    fn test_task_with_result() {
97        let mut task = Task::new("Task with result");
98        let result_value = json!({"output": "success", "value": 42});
99        task.result = Some(result_value.clone());
100        task.completed = true;
101
102        assert!(task.completed);
103        assert_eq!(task.result, Some(result_value));
104    }
105
106    #[test]
107    fn test_task_unique_submission_ids() {
108        let task1 = Task::new("Task 1");
109        let task2 = Task::new("Task 2");
110
111        assert_ne!(task1.submission_id, task2.submission_id);
112    }
113
114    #[test]
115    fn test_task_clone() {
116        let original = Task::new("Original task");
117        let cloned = original.clone();
118
119        assert_eq!(original.prompt, cloned.prompt);
120        assert_eq!(original.submission_id, cloned.submission_id);
121        assert_eq!(original.completed, cloned.completed);
122        assert_eq!(original.result, cloned.result);
123    }
124
125    #[test]
126    fn test_task_debug() {
127        let task = Task::new("Debug test");
128        let debug_str = format!("{task:?}");
129
130        assert!(debug_str.contains("Task"));
131        assert!(debug_str.contains("Debug test"));
132    }
133
134    #[test]
135    fn test_task_with_image() {
136        let image_data = vec![0x89, 0x50, 0x4E, 0x47]; // PNG header bytes
137        let task = Task::new_with_image("Task with image", ImageMime::PNG, image_data.clone());
138
139        assert_eq!(task.prompt, "Task with image");
140        assert!(task.image.is_some());
141        if let Some((mime, data)) = &task.image {
142            assert_eq!(*mime, ImageMime::PNG);
143            assert_eq!(*data, image_data);
144        }
145        assert!(!task.completed);
146        assert!(task.result.is_none());
147    }
148
149    #[test]
150    fn test_task_without_image() {
151        let task = Task::new("Task without image");
152
153        assert_eq!(task.prompt, "Task without image");
154        assert!(task.image.is_none());
155        assert!(!task.completed);
156        assert!(task.result.is_none());
157    }
158
159    #[test]
160    fn test_task_image_serialization() {
161        let image_data = vec![0xFF, 0xD8, 0xFF, 0xE0]; // JPEG header bytes
162        let task = Task::new_with_image("Serialize with image", ImageMime::JPEG, image_data);
163
164        // Test serialization
165        let serialized = serde_json::to_string(&task).unwrap();
166        assert!(serialized.contains("Serialize with image"));
167        assert!(serialized.contains("image"));
168
169        // Test deserialization
170        let deserialized: Task = serde_json::from_str(&serialized).unwrap();
171        assert_eq!(deserialized.prompt, task.prompt);
172        assert_eq!(deserialized.image, task.image);
173        assert_eq!(deserialized.submission_id, task.submission_id);
174    }
175}