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::{json, Value as JsonValue};
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 fn new(src: impl Into<String>, ctx: &RequestContext) -> Self {
46        Self {
47            artifact_type: "video".to_string(),
48            src: src.into(),
49            mime_type: None,
50            poster: None,
51            caption: None,
52            controls: true,
53            autoplay: false,
54            loop_playback: false,
55            muted: false,
56            metadata: ExecutionMetadata::with_request(ctx),
57        }
58    }
59
60    pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
61        self.mime_type = Some(mime_type.into());
62        self
63    }
64
65    pub fn with_poster(mut self, poster: impl Into<String>) -> Self {
66        self.poster = Some(poster.into());
67        self
68    }
69
70    pub fn with_caption(mut self, caption: impl Into<String>) -> Self {
71        self.caption = Some(caption.into());
72        self
73    }
74
75    pub const fn with_autoplay(mut self) -> Self {
76        self.autoplay = true;
77        self.muted = true;
78        self
79    }
80
81    pub const fn with_loop(mut self) -> Self {
82        self.loop_playback = true;
83        self
84    }
85
86    pub const fn without_controls(mut self) -> Self {
87        self.controls = false;
88        self
89    }
90
91    pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
92        self.metadata.execution_id = Some(id.into());
93        self
94    }
95
96    pub fn with_skill(
97        mut self,
98        skill_id: impl Into<SkillId>,
99        skill_name: impl Into<String>,
100    ) -> Self {
101        self.metadata.skill_id = Some(skill_id.into());
102        self.metadata.skill_name = Some(skill_name.into());
103        self
104    }
105}
106
107impl Artifact for VideoArtifact {
108    fn artifact_type(&self) -> ArtifactType {
109        ArtifactType::Video
110    }
111
112    fn to_schema(&self) -> JsonValue {
113        json!({
114            "type": "object",
115            "properties": {
116                "src": {
117                    "type": "string",
118                    "description": "Video source URL or base64 data URI"
119                },
120                "mime_type": {
121                    "type": "string",
122                    "description": "MIME type (e.g., video/mp4)"
123                },
124                "poster": {
125                    "type": "string",
126                    "description": "Poster/thumbnail image URL"
127                },
128                "caption": {
129                    "type": "string",
130                    "description": "Caption displayed below the video"
131                },
132                "controls": {
133                    "type": "boolean",
134                    "description": "Show playback controls",
135                    "default": true
136                },
137                "autoplay": {
138                    "type": "boolean",
139                    "description": "Auto-play on load",
140                    "default": false
141                },
142                "loop": {
143                    "type": "boolean",
144                    "description": "Loop playback",
145                    "default": false
146                },
147                "muted": {
148                    "type": "boolean",
149                    "description": "Mute audio",
150                    "default": false
151                }
152            },
153            "required": ["src"],
154            "x-artifact-type": "video"
155        })
156    }
157}