systemprompt_models/a2a/
artifact_metadata.rs1use chrono::Utc;
2use serde::{Deserialize, Serialize};
3use systemprompt_identifiers::{ContextId, TaskId};
4use systemprompt_traits::validation::{
5 MetadataValidation, Validate, ValidationError, ValidationResult,
6};
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
9pub struct ArtifactMetadata {
10 pub artifact_type: String,
11 pub context_id: ContextId,
12 pub created_at: String,
13 pub task_id: TaskId,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub rendering_hints: Option<serde_json::Value>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub source: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub mcp_execution_id: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub mcp_schema: Option<serde_json::Value>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub is_internal: Option<bool>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub fingerprint: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub tool_name: Option<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub execution_index: Option<usize>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub skill_id: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub skill_name: Option<String>,
34}
35
36impl ArtifactMetadata {
37 pub fn new(artifact_type: String, context_id: ContextId, task_id: TaskId) -> Self {
38 Self {
39 artifact_type,
40 context_id,
41 task_id,
42 created_at: Utc::now().to_rfc3339(),
43 rendering_hints: None,
44 source: Some("mcp_tool".to_string()),
45 mcp_execution_id: None,
46 mcp_schema: None,
47 is_internal: None,
48 fingerprint: None,
49 tool_name: None,
50 execution_index: None,
51 skill_id: None,
52 skill_name: None,
53 }
54 }
55
56 pub fn with_rendering_hints(mut self, hints: serde_json::Value) -> Self {
57 self.rendering_hints = Some(hints);
58 self
59 }
60
61 pub fn with_source(mut self, source: String) -> Self {
62 self.source = Some(source);
63 self
64 }
65
66 pub fn with_mcp_execution_id(mut self, id: String) -> Self {
67 self.mcp_execution_id = Some(id);
68 self
69 }
70
71 pub fn with_mcp_schema(mut self, schema: serde_json::Value) -> Self {
72 self.mcp_schema = Some(schema);
73 self
74 }
75
76 pub const fn with_is_internal(mut self, is_internal: bool) -> Self {
77 self.is_internal = Some(is_internal);
78 self
79 }
80
81 pub fn with_fingerprint(mut self, fingerprint: String) -> Self {
82 self.fingerprint = Some(fingerprint);
83 self
84 }
85
86 pub fn with_tool_name(mut self, tool_name: String) -> Self {
87 self.tool_name = Some(tool_name);
88 self
89 }
90
91 pub const fn with_execution_index(mut self, index: usize) -> Self {
92 self.execution_index = Some(index);
93 self
94 }
95
96 pub fn with_skill_id(mut self, skill_id: String) -> Self {
97 self.skill_id = Some(skill_id);
98 self
99 }
100
101 pub fn with_skill_name(mut self, skill_name: String) -> Self {
102 self.skill_name = Some(skill_name);
103 self
104 }
105
106 pub fn with_skill(mut self, skill_id: String, skill_name: String) -> Self {
107 self.skill_id = Some(skill_id);
108 self.skill_name = Some(skill_name);
109 self
110 }
111
112 pub fn new_validated(
113 artifact_type: String,
114 context_id: ContextId,
115 task_id: TaskId,
116 ) -> ValidationResult<Self> {
117 if artifact_type.is_empty() {
118 return Err(ValidationError::new(
119 "artifact_type",
120 "Cannot create ArtifactMetadata: artifact_type is empty",
121 )
122 .with_context(format!(
123 "artifact_type={artifact_type:?}, context_id={context_id:?}, task_id={task_id:?}"
124 )));
125 }
126
127 let metadata = Self {
128 artifact_type,
129 context_id,
130 task_id,
131 created_at: Utc::now().to_rfc3339(),
132 rendering_hints: None,
133 source: Some("mcp_tool".to_string()),
134 mcp_execution_id: None,
135 mcp_schema: None,
136 is_internal: None,
137 fingerprint: None,
138 tool_name: None,
139 execution_index: None,
140 skill_id: None,
141 skill_name: None,
142 };
143
144 metadata.validate()?;
145 Ok(metadata)
146 }
147}
148
149impl Validate for ArtifactMetadata {
150 fn validate(&self) -> ValidationResult<()> {
151 self.validate_required_fields()?;
152 Ok(())
153 }
154}
155
156impl MetadataValidation for ArtifactMetadata {
157 fn required_string_fields(&self) -> Vec<(&'static str, &str)> {
158 vec![
159 ("artifact_type", &self.artifact_type),
160 ("context_id", self.context_id.as_str()),
161 ("task_id", self.task_id.as_str()),
162 ("created_at", &self.created_at),
163 ]
164 }
165}