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