Skip to main content

systemprompt_agent/services/a2a_server/processing/task_builder/
mod.rs

1//! Assembly of A2A [`Task`] values for the various completion paths.
2//!
3//! [`TaskBuilder`] is the general builder; the free functions cover the common
4//! shapes (completed, canceled, mock, submitted, multi-turn) used across the
5//! processing pipeline.
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10mod builders;
11pub mod helpers;
12mod history;
13
14pub use builders::{
15    BuildMultiturnTaskParams, build_canceled_task, build_completed_task, build_mock_task,
16    build_multiturn_task, build_submitted_task,
17};
18
19use crate::models::a2a::{
20    Artifact, Message, MessageRole, Part, Task, TaskState, TaskStatus, TextPart,
21};
22use systemprompt_identifiers::{ContextId, MessageId, TaskId};
23use systemprompt_models::a2a::TaskMetadata;
24
25#[derive(Debug)]
26pub struct TaskBuilder {
27    task_id: TaskId,
28    context_id: ContextId,
29    state: TaskState,
30    response_text: String,
31    id: MessageId,
32    user_message: Option<Message>,
33    artifacts: Vec<Artifact>,
34    metadata: Option<TaskMetadata>,
35}
36
37impl TaskBuilder {
38    pub fn new(context_id: ContextId) -> Self {
39        Self {
40            task_id: TaskId::generate(),
41            context_id,
42            state: TaskState::Completed,
43            response_text: String::new(),
44            id: MessageId::generate(),
45            user_message: None,
46            artifacts: Vec::new(),
47            metadata: None,
48        }
49    }
50
51    pub fn with_task_id(mut self, task_id: TaskId) -> Self {
52        self.task_id = task_id;
53        self
54    }
55
56    pub const fn with_state(mut self, state: TaskState) -> Self {
57        self.state = state;
58        self
59    }
60
61    pub fn with_response_text(mut self, text: String) -> Self {
62        self.response_text = text;
63        self
64    }
65
66    pub fn with_message_id(mut self, id: impl Into<MessageId>) -> Self {
67        self.id = id.into();
68        self
69    }
70
71    pub fn with_user_message(mut self, message: Message) -> Self {
72        self.user_message = Some(message);
73        self
74    }
75
76    pub fn with_artifacts(mut self, artifacts: Vec<Artifact>) -> Self {
77        self.artifacts = artifacts;
78        self
79    }
80
81    pub fn with_metadata(mut self, metadata: TaskMetadata) -> Self {
82        self.metadata = Some(metadata);
83        self
84    }
85
86    pub fn build(self) -> Task {
87        let agent_message = Message {
88            role: MessageRole::Agent,
89            parts: vec![Part::Text(TextPart {
90                text: self.response_text.clone(),
91            })],
92            message_id: self.id.clone(),
93            task_id: Some(self.task_id.clone()),
94            context_id: self.context_id.clone(),
95            metadata: None,
96            extensions: None,
97            reference_task_ids: None,
98        };
99
100        let history = if let Some(user_msg) = self.user_message {
101            Some(vec![
102                user_msg,
103                Message {
104                    role: MessageRole::Agent,
105                    parts: vec![Part::Text(TextPart {
106                        text: self.response_text.clone(),
107                    })],
108                    message_id: MessageId::generate(),
109                    task_id: Some(self.task_id.clone()),
110                    context_id: self.context_id.clone(),
111                    metadata: None,
112                    extensions: None,
113                    reference_task_ids: None,
114                },
115            ])
116        } else {
117            None
118        };
119
120        Task {
121            id: self.task_id.clone(),
122            context_id: self.context_id.clone(),
123            status: TaskStatus {
124                state: self.state,
125                message: Some(agent_message),
126                timestamp: Some(chrono::Utc::now()),
127            },
128            history,
129            artifacts: if self.artifacts.is_empty() {
130                None
131            } else {
132                Some(self.artifacts)
133            },
134            metadata: self.metadata,
135            created_at: Some(chrono::Utc::now()),
136            last_modified: Some(chrono::Utc::now()),
137        }
138    }
139}