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::{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}