systemprompt_models/artifacts/video/
mod.rs1use 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}