Skip to main content

systemprompt_models/artifacts/image/
mod.rs

1//! Image artifact.
2//!
3//! An [`ImageArtifact`] carries an image source URI with optional alt text,
4//! caption, and pixel dimensions. It implements [`Artifact`] and emits its own
5//! JSON schema for tool output.
6
7use crate::artifacts::metadata::ExecutionMetadata;
8use crate::artifacts::traits::Artifact;
9use crate::artifacts::types::ArtifactType;
10use crate::execution::context::RequestContext;
11use schemars::JsonSchema;
12use serde::{Deserialize, Serialize};
13use serde_json::{Value as JsonValue, json};
14use systemprompt_identifiers::SkillId;
15
16fn default_artifact_type() -> String {
17    "image".to_owned()
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
21pub struct ImageArtifact {
22    #[serde(rename = "x-artifact-type")]
23    #[serde(default = "default_artifact_type")]
24    pub artifact_type: String,
25    pub src: String,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub alt: Option<String>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub caption: Option<String>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub width: Option<u32>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub height: Option<u32>,
34    #[serde(skip)]
35    #[schemars(skip)]
36    metadata: ExecutionMetadata,
37}
38
39impl ImageArtifact {
40    pub const ARTIFACT_TYPE_STR: &'static str = "image";
41
42    pub fn new(src: impl Into<String>) -> Self {
43        Self {
44            artifact_type: "image".to_owned(),
45            src: src.into(),
46            alt: None,
47            caption: None,
48            width: None,
49            height: None,
50            metadata: ExecutionMetadata::default(),
51        }
52    }
53
54    pub fn with_request(mut self, ctx: &RequestContext) -> Self {
55        self.metadata = ExecutionMetadata::with_request(ctx);
56        self
57    }
58
59    pub fn with_alt(mut self, alt: impl Into<String>) -> Self {
60        self.alt = Some(alt.into());
61        self
62    }
63
64    pub fn with_caption(mut self, caption: impl Into<String>) -> Self {
65        self.caption = Some(caption.into());
66        self
67    }
68
69    pub const fn with_dimensions(mut self, width: u32, height: u32) -> Self {
70        self.width = Some(width);
71        self.height = Some(height);
72        self
73    }
74
75    pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
76        self.metadata.execution_id = Some(id.into());
77        self
78    }
79
80    pub fn with_skill(
81        mut self,
82        skill_id: impl Into<SkillId>,
83        skill_name: impl Into<String>,
84    ) -> Self {
85        self.metadata.skill_id = Some(skill_id.into());
86        self.metadata.skill_name = Some(skill_name.into());
87        self
88    }
89}
90
91impl Artifact for ImageArtifact {
92    fn artifact_type(&self) -> ArtifactType {
93        ArtifactType::Image
94    }
95
96    fn to_schema(&self) -> JsonValue {
97        json!({
98            "type": "object",
99            "properties": {
100                "src": {
101                    "type": "string",
102                    "description": "Image source URL or base64 data URI"
103                },
104                "alt": {
105                    "type": "string",
106                    "description": "Alt text for accessibility"
107                },
108                "caption": {
109                    "type": "string",
110                    "description": "Caption displayed below the image"
111                },
112                "width": {
113                    "type": "integer",
114                    "description": "Image width in pixels"
115                },
116                "height": {
117                    "type": "integer",
118                    "description": "Image height in pixels"
119                }
120            },
121            "required": ["src"],
122            "x-artifact-type": "image"
123        })
124    }
125}