Skip to main content

systemprompt_models/artifacts/audio/
mod.rs

1//! Audio playback artifact.
2//!
3//! [`AudioArtifact`] is the renderable artifact for audio output — a source URI
4//! plus optional metadata (title, artist, artwork) and playback flags. It
5//! implements [`Artifact`] and emits its own JSON schema for tool output.
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10use crate::artifacts::metadata::ExecutionMetadata;
11use crate::artifacts::traits::Artifact;
12use crate::artifacts::types::ArtifactType;
13use crate::execution::context::RequestContext;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16use serde_json::{Value as JsonValue, json};
17use systemprompt_identifiers::SkillId;
18
19fn default_artifact_type() -> String {
20    "audio".to_owned()
21}
22
23const fn default_true() -> bool {
24    true
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
28pub struct AudioArtifact {
29    #[serde(rename = "x-artifact-type")]
30    #[serde(default = "default_artifact_type")]
31    pub artifact_type: String,
32    pub src: String,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub mime_type: Option<String>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub title: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub artist: Option<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub artwork: Option<String>,
41    #[serde(default = "default_true")]
42    pub controls: bool,
43    #[serde(default)]
44    pub autoplay: bool,
45    #[serde(default)]
46    #[serde(rename = "loop")]
47    pub loop_playback: bool,
48    #[serde(skip)]
49    #[schemars(skip)]
50    metadata: ExecutionMetadata,
51}
52
53impl AudioArtifact {
54    pub const ARTIFACT_TYPE_STR: &'static str = "audio";
55
56    pub fn new(src: impl Into<String>) -> Self {
57        Self {
58            artifact_type: "audio".to_owned(),
59            src: src.into(),
60            mime_type: None,
61            title: None,
62            artist: None,
63            artwork: None,
64            controls: true,
65            autoplay: false,
66            loop_playback: false,
67            metadata: ExecutionMetadata::default(),
68        }
69    }
70
71    pub fn with_request(mut self, ctx: &RequestContext) -> Self {
72        self.metadata = ExecutionMetadata::with_request(ctx);
73        self
74    }
75
76    pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
77        self.mime_type = Some(mime_type.into());
78        self
79    }
80
81    pub fn with_title(mut self, title: impl Into<String>) -> Self {
82        self.title = Some(title.into());
83        self
84    }
85
86    pub fn with_artist(mut self, artist: impl Into<String>) -> Self {
87        self.artist = Some(artist.into());
88        self
89    }
90
91    pub fn with_artwork(mut self, artwork: impl Into<String>) -> Self {
92        self.artwork = Some(artwork.into());
93        self
94    }
95
96    pub const fn with_autoplay(mut self) -> Self {
97        self.autoplay = true;
98        self
99    }
100
101    pub const fn with_loop(mut self) -> Self {
102        self.loop_playback = true;
103        self
104    }
105
106    pub const fn without_controls(mut self) -> Self {
107        self.controls = false;
108        self
109    }
110
111    pub fn with_execution_id(mut self, id: impl Into<String>) -> Self {
112        self.metadata.execution_id = Some(id.into());
113        self
114    }
115
116    pub fn with_skill(
117        mut self,
118        skill_id: impl Into<SkillId>,
119        skill_name: impl Into<String>,
120    ) -> Self {
121        self.metadata.skill_id = Some(skill_id.into());
122        self.metadata.skill_name = Some(skill_name.into());
123        self
124    }
125}
126
127impl Artifact for AudioArtifact {
128    fn artifact_type(&self) -> ArtifactType {
129        ArtifactType::Audio
130    }
131
132    fn to_schema(&self) -> JsonValue {
133        json!({
134            "type": "object",
135            "properties": {
136                "src": {
137                    "type": "string",
138                    "description": "Audio source URL or base64 data URI"
139                },
140                "mime_type": {
141                    "type": "string",
142                    "description": "MIME type (e.g., audio/mpeg)"
143                },
144                "title": {
145                    "type": "string",
146                    "description": "Track title"
147                },
148                "artist": {
149                    "type": "string",
150                    "description": "Artist name"
151                },
152                "artwork": {
153                    "type": "string",
154                    "description": "Album artwork URL"
155                },
156                "controls": {
157                    "type": "boolean",
158                    "description": "Show playback controls",
159                    "default": true
160                },
161                "autoplay": {
162                    "type": "boolean",
163                    "description": "Auto-play on load",
164                    "default": false
165                },
166                "loop": {
167                    "type": "boolean",
168                    "description": "Loop playback",
169                    "default": false
170                }
171            },
172            "required": ["src"],
173            "x-artifact-type": "audio"
174        })
175    }
176}