Skip to main content

systemprompt_models/artifacts/video/
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    "video".to_string()
12}
13
14const fn default_true() -> bool {
15    true
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
19pub struct VideoArtifact {
20    #[serde(rename = "x-artifact-type")]
21    #[serde(default = "default_artifact_type")]
22    pub artifact_type: String,
23    pub src: String,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub mime_type: Option<String>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub poster: Option<String>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub caption: Option<String>,
30    #[serde(default = "default_true")]
31    pub controls: bool,
32    #[serde(default)]
33    pub autoplay: bool,
34    #[serde(default)]
35    #[serde(rename = "loop")]
36    pub loop_playback: bool,
37    #[serde(default)]
38    pub muted: bool,
39    #[serde(skip)]
40    #[schemars(skip)]
41    metadata: ExecutionMetadata,
42}
43
44impl VideoArtifact {
45    pub const ARTIFACT_TYPE_STR: &'static str = "video";
46
47    pub fn new(src: impl Into<String>, ctx: &RequestContext) -> Self {
48        Self {
49            artifact_type: "video".to_string(),
50            src: src.into(),
51            mime_type: None,
52            poster: None,
53            caption: None,
54            controls: true,
55            autoplay: false,
56            loop_playback: false,
57            muted: false,
58            metadata: ExecutionMetadata::with_request(ctx),
59        }
60    }
61
62    pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
63        self.mime_type = Some(mime_type.into());
64        self
65    }
66
67    pub fn with_poster(mut self, poster: impl Into<String>) -> Self {
68        self.poster = Some(poster.into());
69        self
70    }
71
72    pub fn with_caption(mut self, caption: impl Into<String>) -> Self {
73        self.caption = Some(caption.into());
74        self
75    }
76
77    pub const fn with_autoplay(mut self) -> Self {
78        self.autoplay = true;
79        self.muted = true;
80        self
81    }
82
83    pub const fn with_loop(mut self) -> Self {
84        self.loop_playback = true;
85        self
86    }
87
88    pub const fn without_controls(mut self) -> Self {
89        self.controls = false;
90        self
91    }
92
93    pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
94        self.metadata.execution_id = Some(id.into());
95        self
96    }
97
98    pub fn with_skill(
99        mut self,
100        skill_id: impl Into<SkillId>,
101        skill_name: impl Into<String>,
102    ) -> Self {
103        self.metadata.skill_id = Some(skill_id.into());
104        self.metadata.skill_name = Some(skill_name.into());
105        self
106    }
107}
108
109impl Artifact for VideoArtifact {
110    fn artifact_type(&self) -> ArtifactType {
111        ArtifactType::Video
112    }
113
114    fn to_schema(&self) -> JsonValue {
115        json!({
116            "type": "object",
117            "properties": {
118                "src": {
119                    "type": "string",
120                    "description": "Video source URL or base64 data URI"
121                },
122                "mime_type": {
123                    "type": "string",
124                    "description": "MIME type (e.g., video/mp4)"
125                },
126                "poster": {
127                    "type": "string",
128                    "description": "Poster/thumbnail image URL"
129                },
130                "caption": {
131                    "type": "string",
132                    "description": "Caption displayed below the video"
133                },
134                "controls": {
135                    "type": "boolean",
136                    "description": "Show playback controls",
137                    "default": true
138                },
139                "autoplay": {
140                    "type": "boolean",
141                    "description": "Auto-play on load",
142                    "default": false
143                },
144                "loop": {
145                    "type": "boolean",
146                    "description": "Loop playback",
147                    "default": false
148                },
149                "muted": {
150                    "type": "boolean",
151                    "description": "Mute audio",
152                    "default": false
153                }
154            },
155            "required": ["src"],
156            "x-artifact-type": "video"
157        })
158    }
159}