ai_lib/types/
common.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value as JsonValue;
3
4/// Message content — moved to an enum to support multimodal and structured content
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub enum Content {
7    #[serde(rename = "text")]
8    Text(String),
9    /// Generic JSON content for structured payloads (e.g. function call args)
10    #[serde(rename = "json")]
11    Json(JsonValue),
12    /// Reference to an image (url) or metadata; adapters may upload or inline as needed
13    #[serde(rename = "image")]
14    Image {
15        url: Option<String>,
16        mime: Option<String>,
17        name: Option<String>,
18    },
19    /// Reference to audio content
20    #[serde(rename = "audio")]
21    Audio {
22        url: Option<String>,
23        mime: Option<String>,
24    },
25}
26
27impl Content {
28    /// Return a best-effort textual representation for legacy code paths
29    pub fn as_text(&self) -> String {
30        match self {
31            Content::Text(s) => s.clone(),
32            Content::Json(v) => v.to_string(),
33            Content::Image { url, .. } => url.clone().unwrap_or_default(),
34            Content::Audio { url, .. } => url.clone().unwrap_or_default(),
35        }
36    }
37
38    /// Convenience constructor for text content
39    pub fn new_text<S: Into<String>>(s: S) -> Self {
40        Content::Text(s.into())
41    }
42
43    /// Convenience constructor for JSON content
44    pub fn new_json(v: JsonValue) -> Self {
45        Content::Json(v)
46    }
47
48    /// Convenience constructor for image content
49    pub fn new_image(url: Option<String>, mime: Option<String>, name: Option<String>) -> Self {
50        Content::Image { url, mime, name }
51    }
52
53    /// Convenience constructor for audio content
54    pub fn new_audio(url: Option<String>, mime: Option<String>) -> Self {
55        Content::Audio { url, mime }
56    }
57
58    /// Create image content from a file path - the file will be automatically processed
59    /// (uploaded or inlined as data URL) by the AI client when used in a request
60    pub fn from_image_file<P: AsRef<std::path::Path>>(path: P) -> Self {
61        let path = path.as_ref();
62        let name = path.file_name()
63            .and_then(|n| n.to_str())
64            .map(|s| s.to_string());
65        let mime = path.extension()
66            .and_then(|ext| ext.to_str())
67            .and_then(|ext| match ext.to_lowercase().as_str() {
68                "png" => Some("image/png"),
69                "jpg" | "jpeg" => Some("image/jpeg"),
70                "gif" => Some("image/gif"),
71                "webp" => Some("image/webp"),
72                "svg" => Some("image/svg+xml"),
73                _ => None,
74            })
75            .map(|s| s.to_string());
76        
77        Content::Image {
78            url: None, // Will be filled by the client
79            mime,
80            name,
81        }
82    }
83
84    /// Create audio content from a file path - the file will be automatically processed
85    /// (uploaded or inlined as data URL) by the AI client when used in a request
86    pub fn from_audio_file<P: AsRef<std::path::Path>>(path: P) -> Self {
87        let path = path.as_ref();
88        let mime = path.extension()
89            .and_then(|ext| ext.to_str())
90            .and_then(|ext| match ext.to_lowercase().as_str() {
91                "mp3" => Some("audio/mpeg"),
92                "wav" => Some("audio/wav"),
93                "ogg" => Some("audio/ogg"),
94                "m4a" => Some("audio/mp4"),
95                "flac" => Some("audio/flac"),
96                _ => None,
97            })
98            .map(|s| s.to_string());
99        
100        Content::Audio {
101            url: None, // Will be filled by the client
102            mime,
103        }
104    }
105
106    /// Create image content from a data URL (base64 encoded)
107    pub fn from_data_url(data_url: String, mime: Option<String>, name: Option<String>) -> Self {
108        Content::Image {
109            url: Some(data_url),
110            mime,
111            name,
112        }
113    }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct Message {
118    pub role: Role,
119    pub content: Content,
120    /// Optional function call payload when assistant invokes a tool
121    pub function_call: Option<crate::types::function_call::FunctionCall>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub enum Role {
126    #[serde(rename = "system")]
127    System,
128    #[serde(rename = "user")]
129    User,
130    #[serde(rename = "assistant")]
131    Assistant,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct Choice {
136    pub index: u32,
137    pub message: Message,
138    pub finish_reason: Option<String>,
139}
140
141// Usage and UsageStatus have moved to `types::response` because they are
142// semantically part of the chat response metadata. We keep these
143// re-exports here for backward compatibility but mark them deprecated to
144// guide users to the new location.
145#[deprecated(note = "Usage has been moved to `ai_lib::types::response::Usage`. Please import from there or use `ai_lib::Usage`. This alias will be removed before 1.0.")]
146pub use crate::types::response::Usage;
147
148#[deprecated(note = "UsageStatus has been moved to `ai_lib::types::response::UsageStatus`. Please import from there or use `ai_lib::UsageStatus`. This alias will be removed before 1.0.")]
149pub use crate::types::response::UsageStatus;