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    // JSON: JSON Schema document describing the artifact for the model.
133    fn to_schema(&self) -> JsonValue {
134        json!({
135            "type": "object",
136            "properties": {
137                "src": {
138                    "type": "string",
139                    "description": "Audio source URL or base64 data URI"
140                },
141                "mime_type": {
142                    "type": "string",
143                    "description": "MIME type (e.g., audio/mpeg)"
144                },
145                "title": {
146                    "type": "string",
147                    "description": "Track title"
148                },
149                "artist": {
150                    "type": "string",
151                    "description": "Artist name"
152                },
153                "artwork": {
154                    "type": "string",
155                    "description": "Album artwork URL"
156                },
157                "controls": {
158                    "type": "boolean",
159                    "description": "Show playback controls",
160                    "default": true
161                },
162                "autoplay": {
163                    "type": "boolean",
164                    "description": "Auto-play on load",
165                    "default": false
166                },
167                "loop": {
168                    "type": "boolean",
169                    "description": "Loop playback",
170                    "default": false
171                }
172            },
173            "required": ["src"],
174            "x-artifact-type": "audio"
175        })
176    }
177}