1use crate::Error;
3use base64::{engine::general_purpose::STANDARD, Engine as _};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum MediaKind {
10 Image,
12 Audio,
14}
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct MediaInput {
18 #[serde(rename = "type")]
20 pub kind: MediaKind,
21 pub data: String,
23}
24impl MediaInput {
25 pub fn image(data: &[u8]) -> Self {
27 Self { kind: MediaKind::Image, data: STANDARD.encode(data) }
28 }
29 pub fn audio(data: &[u8]) -> Self {
31 Self { kind: MediaKind::Audio, data: STANDARD.encode(data) }
32 }
33 pub fn from_base64(kind: MediaKind, data: impl Into<String>) -> Result<Self, Error> {
35 let input = Self { kind, data: data.into() };
36 input.validate()?;
37 Ok(input)
38 }
39 pub(crate) fn validate(&self) -> Result<(), Error> {
40 let lower = self.data.get(..8).unwrap_or(&self.data).to_ascii_lowercase();
41 if self.data.trim().is_empty() || lower.starts_with("http://") || lower.starts_with("https://") {
42 return Err(Error::InvalidRequest("media requires inline base64 or a data URL"));
43 }
44 Ok(())
45 }
46}
47#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
49#[serde(rename_all = "lowercase")]
50pub enum Language {
51 Pl,
53 En,
55}
56#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
58#[serde(rename_all = "lowercase")]
59pub enum MediaDetail {
60 Default,
62 High,
64}
65#[derive(Debug, Clone, Deserialize)]
67pub struct MediaUsage {
68 pub images: u64,
70 pub audio: u64,
72 pub media_tokens: u64,
74 pub detail: MediaDetail,
76}