Skip to main content

litellm_rust/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3use std::borrow::Cow;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(untagged)]
7pub enum ChatMessageContent {
8    Text(String),
9    Parts(Vec<ChatContentPart>),
10}
11
12impl ChatMessageContent {
13    pub fn text(text: impl Into<String>) -> Self {
14        Self::Text(text.into())
15    }
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(untagged)]
20pub enum ChatContentPart {
21    Text(ChatContentPartText),
22    ImageUrl(ChatContentPartImageUrl),
23    InputAudio(ChatContentPartInputAudio),
24    File(ChatContentPartFile),
25    Other(Value),
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ChatContentPartText {
30    #[serde(rename = "type")]
31    pub kind: Cow<'static, str>,
32    pub text: String,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct ChatContentPartImageUrl {
37    #[serde(rename = "type")]
38    pub kind: Cow<'static, str>,
39    pub image_url: ChatImageUrl,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(untagged)]
44pub enum ChatImageUrl {
45    Url(String),
46    Object(ChatImageUrlObject),
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ChatImageUrlObject {
51    pub url: String,
52    pub detail: Option<String>,
53    pub format: Option<String>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ChatContentPartInputAudio {
58    #[serde(rename = "type")]
59    pub kind: Cow<'static, str>,
60    pub input_audio: ChatInputAudio,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ChatInputAudio {
65    pub data: String,
66    pub format: String,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct ChatContentPartFile {
71    #[serde(rename = "type")]
72    pub kind: Cow<'static, str>,
73    pub file: ChatFile,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ChatFile {
78    pub file_id: Option<String>,
79    pub file_data: Option<String>,
80    pub format: Option<String>,
81    pub detail: Option<String>,
82    pub video_metadata: Option<Value>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct ChatMessage {
87    pub role: String,
88    pub content: ChatMessageContent,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub name: Option<String>,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub tool_call_id: Option<String>,
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub tool_calls: Option<Value>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub function_call: Option<Value>,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub provider_specific_fields: Option<Value>,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ChatRequest {
103    pub model: String,
104    pub messages: Vec<ChatMessage>,
105    pub temperature: Option<f32>,
106    pub max_tokens: Option<u32>,
107    pub response_format: Option<Value>,
108    pub max_completion_tokens: Option<u32>,
109    pub tools: Option<Value>,
110    pub tool_choice: Option<Value>,
111    pub parallel_tool_calls: Option<bool>,
112    pub stop: Option<Value>,
113    pub top_p: Option<f32>,
114    pub presence_penalty: Option<f32>,
115    pub frequency_penalty: Option<f32>,
116    pub seed: Option<u64>,
117    pub user: Option<String>,
118    pub metadata: Option<Value>,
119    pub reasoning_effort: Option<Value>,
120    pub thinking: Option<Value>,
121    /// Provider-specific JSON body fields forwarded at the top level.
122    ///
123    /// Values are sent raw to the upstream provider. Core request fields and
124    /// LiteLLM-internal-looking keys are rejected when the request is built.
125    pub extra_body: Option<Map<String, Value>>,
126}
127
128impl ChatRequest {
129    pub fn new(model: impl Into<String>) -> Self {
130        Self {
131            model: model.into(),
132            messages: Vec::new(),
133            temperature: None,
134            max_tokens: None,
135            response_format: None,
136            max_completion_tokens: None,
137            tools: None,
138            tool_choice: None,
139            parallel_tool_calls: None,
140            stop: None,
141            top_p: None,
142            presence_penalty: None,
143            frequency_penalty: None,
144            seed: None,
145            user: None,
146            metadata: None,
147            reasoning_effort: None,
148            thinking: None,
149            extra_body: None,
150        }
151    }
152
153    pub fn message(mut self, role: impl Into<String>, content: impl Into<String>) -> Self {
154        self.messages.push(ChatMessage {
155            role: role.into(),
156            content: ChatMessageContent::Text(content.into()),
157            name: None,
158            tool_call_id: None,
159            tool_calls: None,
160            function_call: None,
161            provider_specific_fields: None,
162        });
163        self
164    }
165
166    pub fn message_with_content(
167        mut self,
168        role: impl Into<String>,
169        content: ChatMessageContent,
170    ) -> Self {
171        self.messages.push(ChatMessage {
172            role: role.into(),
173            content,
174            name: None,
175            tool_call_id: None,
176            tool_calls: None,
177            function_call: None,
178            provider_specific_fields: None,
179        });
180        self
181    }
182
183    pub fn temperature(mut self, temperature: f32) -> Self {
184        self.temperature = Some(temperature);
185        self
186    }
187
188    pub fn max_tokens(mut self, max_tokens: u32) -> Self {
189        self.max_tokens = Some(max_tokens);
190        self
191    }
192
193    pub fn response_format(mut self, format: Value) -> Self {
194        self.response_format = Some(format);
195        self
196    }
197
198    pub fn extra_body(mut self, body: Map<String, Value>) -> Self {
199        self.extra_body = Some(body);
200        self
201    }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct ChatResponse {
206    pub content: String,
207    /// Provider reasoning, kept separate from answer content.
208    pub reasoning: Option<Reasoning>,
209    pub usage: Usage,
210    pub response_id: Option<String>,
211    pub header_cost: Option<f64>,
212    pub raw: Option<Value>,
213}
214
215/// Provider reasoning details associated with a response.
216///
217/// The raw `details` payload is the canonical representation. Use `text()` for
218/// a best-effort textual view when providers expose text or summary entries.
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct Reasoning {
221    pub details: Vec<Value>,
222}
223
224impl Reasoning {
225    /// Normalize provider text-only reasoning into the common detail shape.
226    pub fn from_text(text: impl Into<String>) -> Option<Self> {
227        let text = text.into();
228        if text.trim().is_empty() {
229            return None;
230        }
231        Some(Self {
232            details: vec![serde_json::json!({
233                "type": "reasoning.text",
234                "text": text,
235            })],
236        })
237    }
238
239    pub fn from_details(details: Vec<Value>) -> Option<Self> {
240        (!details.is_empty()).then_some(Self { details })
241    }
242
243    pub fn text(&self) -> Option<String> {
244        let mut out = String::new();
245        for detail in &self.details {
246            collect_reasoning_text(detail, &mut out);
247        }
248        (!out.trim().is_empty()).then_some(out)
249    }
250}
251
252fn collect_reasoning_text(value: &Value, out: &mut String) {
253    match value {
254        Value::String(text) => out.push_str(text),
255        Value::Array(items) => {
256            for item in items {
257                collect_reasoning_text(item, out);
258            }
259        }
260        Value::Object(map) => {
261            if let Some(text) = map.get("text") {
262                collect_reasoning_text(text, out);
263            } else if let Some(summary) = map.get("summary") {
264                collect_reasoning_text(summary, out);
265            }
266        }
267        _ => {}
268    }
269}
270
271/// Token usage statistics from an LLM API response.
272///
273/// Uses `u64` for token counts to handle large values consistently
274/// across different platforms and avoid truncation.
275#[derive(Debug, Clone, Serialize, Deserialize, Default)]
276pub struct Usage {
277    pub prompt_tokens: Option<u64>,
278    pub completion_tokens: Option<u64>,
279    pub thoughts_tokens: Option<u64>,
280    pub total_tokens: Option<u64>,
281    pub cost_usd: Option<f64>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct EmbeddingRequest {
286    pub model: String,
287    pub input: Value,
288    /// Provider-specific JSON body fields forwarded at the top level.
289    pub extra_body: Option<Map<String, Value>>,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct EmbeddingResponse {
294    pub vectors: Vec<Vec<f32>>,
295    pub usage: Usage,
296    pub raw: Option<Value>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ImageRequest {
301    pub model: String,
302    pub prompt: String,
303    pub n: Option<u32>,
304    pub size: Option<String>,
305    pub quality: Option<String>,
306    pub background: Option<String>,
307    /// Provider-specific JSON body fields forwarded at the top level.
308    pub extra_body: Option<Map<String, Value>>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct ImageData {
313    pub b64_json: Option<String>,
314    pub url: Option<String>,
315    pub revised_prompt: Option<String>,
316    pub mime_type: Option<String>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub struct ImageEditRequest {
321    pub model: String,
322    pub prompt: String,
323    pub images: Vec<ImageInputData>,
324    pub n: Option<u32>,
325    pub size: Option<String>,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub struct ImageInputData {
330    pub b64_json: Option<String>,
331    pub url: Option<String>,
332    pub mime_type: Option<String>,
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct ImageResponse {
337    pub images: Vec<ImageData>,
338    pub usage: Usage,
339    pub raw: Option<Value>,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct VideoRequest {
344    pub model: String,
345    pub prompt: String,
346    pub seconds: Option<u32>,
347    pub size: Option<String>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct VideoResponse {
352    pub video_url: Option<String>,
353    pub raw: Option<Value>,
354}