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