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