Skip to main content

systemprompt_models/a2a/
task.rs

1//! A2A task, status, and state wire types.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::artifact::Artifact;
7use super::message::Message;
8use super::task_metadata::TaskMetadata;
9use serde::{Deserialize, Serialize};
10
11use crate::errors::ParseEnumError;
12use systemprompt_identifiers::{ContextId, TaskId};
13
14#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
15#[serde(rename_all = "camelCase")]
16pub struct Task {
17    pub id: TaskId,
18    pub context_id: ContextId,
19    pub status: TaskStatus,
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub history: Option<Vec<Message>>,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub artifacts: Option<Vec<Artifact>>,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub metadata: Option<TaskMetadata>,
26    #[serde(skip)]
27    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
28    #[serde(skip)]
29    pub last_modified: Option<chrono::DateTime<chrono::Utc>>,
30}
31
32impl Default for Task {
33    fn default() -> Self {
34        let now = chrono::Utc::now();
35        Self {
36            id: TaskId::generate(),
37            context_id: ContextId::generate(),
38            status: TaskStatus::default(),
39            history: None,
40            artifacts: None,
41            metadata: None,
42            created_at: Some(now),
43            last_modified: Some(now),
44        }
45    }
46}
47
48#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
49pub struct TaskStatus {
50    pub state: TaskState,
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub message: Option<Message>,
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub timestamp: Option<chrono::DateTime<chrono::Utc>>,
55}
56
57impl Default for TaskStatus {
58    fn default() -> Self {
59        Self {
60            state: TaskState::Submitted,
61            message: None,
62            timestamp: None,
63        }
64    }
65}
66
67#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
68pub enum TaskState {
69    #[serde(rename = "TASK_STATE_SUBMITTED")]
70    Submitted,
71    #[serde(rename = "TASK_STATE_WORKING")]
72    Working,
73    #[serde(rename = "TASK_STATE_COMPLETED")]
74    Completed,
75    #[serde(rename = "TASK_STATE_FAILED")]
76    Failed,
77    #[serde(rename = "TASK_STATE_CANCELED")]
78    Canceled,
79    #[serde(rename = "TASK_STATE_REJECTED")]
80    Rejected,
81    #[serde(rename = "TASK_STATE_INPUT_REQUIRED")]
82    InputRequired,
83    #[serde(rename = "TASK_STATE_AUTH_REQUIRED")]
84    AuthRequired,
85}
86
87impl TaskState {
88    pub const fn as_str(self) -> &'static str {
89        match self {
90            Self::Submitted => "TASK_STATE_SUBMITTED",
91            Self::Working => "TASK_STATE_WORKING",
92            Self::Completed => "TASK_STATE_COMPLETED",
93            Self::Failed => "TASK_STATE_FAILED",
94            Self::Canceled => "TASK_STATE_CANCELED",
95            Self::Rejected => "TASK_STATE_REJECTED",
96            Self::InputRequired => "TASK_STATE_INPUT_REQUIRED",
97            Self::AuthRequired => "TASK_STATE_AUTH_REQUIRED",
98        }
99    }
100
101    pub const fn is_terminal(&self) -> bool {
102        matches!(
103            self,
104            Self::Completed | Self::Failed | Self::Canceled | Self::Rejected
105        )
106    }
107
108    pub const fn can_transition_to(&self, target: &Self) -> bool {
109        if self.is_terminal() {
110            return false;
111        }
112        match self {
113            Self::Submitted => matches!(
114                target,
115                Self::Working
116                    | Self::Completed
117                    | Self::Failed
118                    | Self::Canceled
119                    | Self::Rejected
120                    | Self::AuthRequired
121            ),
122            Self::Working => matches!(
123                target,
124                Self::Completed | Self::Failed | Self::Canceled | Self::InputRequired
125            ),
126            Self::InputRequired => matches!(
127                target,
128                Self::Working | Self::Completed | Self::Failed | Self::Canceled
129            ),
130            Self::AuthRequired => matches!(target, Self::Working | Self::Failed | Self::Canceled),
131            Self::Completed | Self::Failed | Self::Canceled | Self::Rejected => false,
132        }
133    }
134}
135
136impl std::str::FromStr for TaskState {
137    type Err = ParseEnumError;
138
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        match s {
141            "TASK_STATE_SUBMITTED" => Ok(Self::Submitted),
142            "TASK_STATE_WORKING" => Ok(Self::Working),
143            "TASK_STATE_COMPLETED" => Ok(Self::Completed),
144            "TASK_STATE_FAILED" => Ok(Self::Failed),
145            "TASK_STATE_CANCELED" => Ok(Self::Canceled),
146            "TASK_STATE_REJECTED" => Ok(Self::Rejected),
147            "TASK_STATE_INPUT_REQUIRED" => Ok(Self::InputRequired),
148            "TASK_STATE_AUTH_REQUIRED" => Ok(Self::AuthRequired),
149            _ => Err(ParseEnumError::new("task state", s)),
150        }
151    }
152}