Skip to main content

lc_schema/messages/
video.rs

1// lc-schema/src/messages/video.rs
2//! Video content type (multimodal video support).
3//!
4//! Supports both URL-based and base64-encoded video content,
5//! following the same pattern as [`super::AudioContent`].
6
7use serde::{Deserialize, Serialize};
8
9/// Video content (URL or base64 data URI).
10///
11/// Used for video input in multimodal interactions. Provider support varies:
12/// OpenAI accepts `input_video` (https URL / `file_id` / data URI depending on
13/// the model), Gemini accepts `inline_data` / `fileData` video parts, while
14/// Anthropic does not accept video input — providers that cannot map video
15/// surface an explicit error at request-build time instead of dropping it.
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
17pub struct VideoContent {
18    /// Video URL or base64 data URI.
19    pub url: String,
20}
21
22impl VideoContent {
23    /// Creates from a URL.
24    pub fn from_url(url: impl Into<String>) -> Self {
25        Self { url: url.into() }
26    }
27
28    /// Creates from base64 data (auto-wraps as data URI, defaults to `video/mp4`).
29    pub fn from_base64(data: impl Into<String>) -> Self {
30        Self {
31            url: format!("data:video/mp4;base64,{}", data.into()),
32        }
33    }
34
35    /// Creates from base64 data with a specific MIME type.
36    pub fn from_base64_with_mime(data: impl Into<String>, mime: &str) -> Self {
37        Self {
38            url: format!("data:{};base64,{}", mime, data.into()),
39        }
40    }
41
42    /// Returns whether this is a base64 data URI.
43    pub fn is_base64(&self) -> bool {
44        self.url.starts_with("data:")
45    }
46
47    /// Extracts the base64 raw data (if this is a data URI).
48    pub fn base64_data(&self) -> Option<&str> {
49        self.url
50            .split_once(',')
51            .filter(|(prefix, _)| prefix.contains("base64"))
52            .map(|(_, data)| data)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_from_url() {
62        let video = VideoContent::from_url("https://example.com/clip.mp4");
63        assert_eq!(video.url, "https://example.com/clip.mp4");
64        assert!(!video.is_base64());
65    }
66
67    #[test]
68    fn test_from_base64() {
69        let video = VideoContent::from_base64("abc123");
70        assert!(video.is_base64());
71        assert!(video.url.starts_with("data:video/mp4;base64,"));
72        assert_eq!(video.base64_data(), Some("abc123"));
73    }
74
75    #[test]
76    fn test_from_base64_with_mime() {
77        let video = VideoContent::from_base64_with_mime("xyz", "video/webm");
78        assert!(video.url.starts_with("data:video/webm;base64,"));
79        assert_eq!(video.base64_data(), Some("xyz"));
80    }
81
82    #[test]
83    fn test_url_not_base64() {
84        let video = VideoContent::from_url("https://example.com/movie.mov");
85        assert_eq!(video.base64_data(), None);
86    }
87}