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