Skip to main content

systemprompt_models/artifacts/list/
mod.rs

1use crate::artifacts::metadata::ExecutionMetadata;
2use crate::artifacts::traits::Artifact;
3use crate::artifacts::types::ArtifactType;
4use crate::execution::context::RequestContext;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use serde_json::{Value as JsonValue, json};
8use systemprompt_identifiers::SkillId;
9
10fn default_artifact_type() -> String {
11    "list".to_string()
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
15pub struct ListItem {
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub id: Option<String>,
18    pub title: String,
19    pub summary: String,
20    pub link: String,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub uri: Option<String>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub slug: Option<String>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub source_id: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub category: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub description: Option<String>,
31}
32
33impl ListItem {
34    pub fn new(
35        title: impl Into<String>,
36        summary: impl Into<String>,
37        link: impl Into<String>,
38    ) -> Self {
39        Self {
40            id: None,
41            title: title.into(),
42            summary: summary.into(),
43            link: link.into(),
44            uri: None,
45            slug: None,
46            source_id: None,
47            category: None,
48            description: None,
49        }
50    }
51
52    pub fn with_id(mut self, id: impl Into<String>) -> Self {
53        self.id = Some(id.into());
54        self
55    }
56
57    pub fn with_uri(mut self, uri: impl Into<String>) -> Self {
58        self.uri = Some(uri.into());
59        self
60    }
61
62    pub fn with_slug(mut self, slug: impl Into<String>) -> Self {
63        self.slug = Some(slug.into());
64        self
65    }
66
67    pub fn with_source_id(mut self, source_id: impl Into<String>) -> Self {
68        self.source_id = Some(source_id.into());
69        self
70    }
71
72    pub fn with_category(mut self, category: impl Into<String>) -> Self {
73        self.category = Some(category.into());
74        self
75    }
76
77    pub fn with_description(mut self, description: impl Into<String>) -> Self {
78        self.description = Some(description.into());
79        self
80    }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
84pub struct ListArtifact {
85    #[serde(rename = "x-artifact-type")]
86    #[serde(default = "default_artifact_type")]
87    pub artifact_type: String,
88    pub items: Vec<ListItem>,
89    pub count: usize,
90    #[serde(skip)]
91    #[schemars(skip)]
92    metadata: ExecutionMetadata,
93}
94
95impl ListArtifact {
96    pub const ARTIFACT_TYPE_STR: &'static str = "list";
97
98    pub fn new(ctx: &RequestContext) -> Self {
99        Self {
100            artifact_type: "list".to_string(),
101            items: Vec::new(),
102            count: 0,
103            metadata: ExecutionMetadata::with_request(ctx),
104        }
105    }
106
107    pub fn with_items(mut self, items: Vec<ListItem>) -> Self {
108        self.count = items.len();
109        self.items = items;
110        self
111    }
112
113    pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
114        self.metadata.execution_id = Some(id.into());
115        self
116    }
117
118    pub fn with_skill(
119        mut self,
120        skill_id: impl Into<SkillId>,
121        skill_name: impl Into<String>,
122    ) -> Self {
123        self.metadata.skill_id = Some(skill_id.into());
124        self.metadata.skill_name = Some(skill_name.into());
125        self
126    }
127}
128
129impl Artifact for ListArtifact {
130    fn artifact_type(&self) -> ArtifactType {
131        ArtifactType::List
132    }
133
134    fn to_schema(&self) -> JsonValue {
135        json!({
136            "type": "object",
137            "properties": {
138                "items": {
139                    "type": "array",
140                    "description": "Array of list items",
141                    "items": {
142                        "type": "object",
143                        "properties": {
144                            "title": {
145                                "type": "string",
146                                "description": "Item title"
147                            },
148                            "summary": {
149                                "type": "string",
150                                "description": "Item summary"
151                            },
152                            "link": {
153                                "type": "string",
154                                "description": "Item URL (full HTTPS URL compatible with resource_loading tool's uris parameter)"
155                            },
156                            "uri": {
157                                "type": "string",
158                                "description": "Standardized URI format (tyingshoelaces://blog/slug) for use with resource_loading tool"
159                            },
160                            "slug": {
161                                "type": "string",
162                                "description": "Content slug - can be used directly with resource_loading tool as tyingshoelaces://blog/{slug}"
163                            }
164                        },
165                        "required": ["title", "summary", "link"]
166                    }
167                },
168                "count": {
169                    "type": "integer",
170                    "description": "Total number of items"
171                },
172                "_execution_id": {
173                    "type": "string",
174                    "description": "Execution ID for tracking"
175                }
176            },
177            "required": ["items"],
178            "x-artifact-type": "list"
179        })
180    }
181}