Skip to main content

vv_llm/
types.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(rename_all = "lowercase")]
6pub enum BackendType {
7    OpenAI,
8    ZhiPuAI,
9    MiniMax,
10    Moonshot,
11    Anthropic,
12    Mistral,
13    DeepSeek,
14    Qwen,
15    Groq,
16    Local,
17    Yi,
18    Gemini,
19    Baichuan,
20    StepFun,
21    XAI,
22    Xiaomi,
23    Ernie,
24}
25
26impl BackendType {
27    pub fn as_str(self) -> &'static str {
28        match self {
29            Self::OpenAI => "openai",
30            Self::ZhiPuAI => "zhipuai",
31            Self::MiniMax => "minimax",
32            Self::Moonshot => "moonshot",
33            Self::Anthropic => "anthropic",
34            Self::Mistral => "mistral",
35            Self::DeepSeek => "deepseek",
36            Self::Qwen => "qwen",
37            Self::Groq => "groq",
38            Self::Local => "local",
39            Self::Yi => "yi",
40            Self::Gemini => "gemini",
41            Self::Baichuan => "baichuan",
42            Self::StepFun => "stepfun",
43            Self::XAI => "xai",
44            Self::Xiaomi => "xiaomi",
45            Self::Ernie => "ernie",
46        }
47    }
48}
49
50impl fmt::Display for BackendType {
51    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
52        formatter.write_str(self.as_str())
53    }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
57#[serde(rename_all = "lowercase")]
58pub enum MessageRole {
59    System,
60    User,
61    Assistant,
62    Tool,
63}
64
65impl MessageRole {
66    pub fn as_str(self) -> &'static str {
67        match self {
68            Self::System => "system",
69            Self::User => "user",
70            Self::Assistant => "assistant",
71            Self::Tool => "tool",
72        }
73    }
74}
75
76impl fmt::Display for MessageRole {
77    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78        formatter.write_str(self.as_str())
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83#[serde(tag = "type", rename_all = "snake_case")]
84pub enum MessageContent {
85    Text {
86        text: String,
87        #[serde(default, skip_serializing_if = "Option::is_none")]
88        cache_control: Option<serde_json::Value>,
89    },
90    ImageUrl {
91        url: String,
92    },
93}
94
95impl MessageContent {
96    pub fn text(text: impl Into<String>) -> Self {
97        Self::Text {
98            text: text.into(),
99            cache_control: None,
100        }
101    }
102
103    pub fn text_with_cache_control(
104        text: impl Into<String>,
105        cache_control: serde_json::Value,
106    ) -> Self {
107        Self::Text {
108            text: text.into(),
109            cache_control: Some(cache_control),
110        }
111    }
112}
113
114#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
115pub struct Message {
116    pub role: MessageRole,
117    pub content: Vec<MessageContent>,
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub name: Option<String>,
120    #[serde(default, skip_serializing_if = "Option::is_none")]
121    pub tool_call_id: Option<String>,
122    #[serde(default, skip_serializing_if = "Vec::is_empty")]
123    pub tool_calls: Vec<ToolCall>,
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub reasoning_content: Option<String>,
126}
127
128impl Message {
129    pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
130        Self {
131            role,
132            content: vec![MessageContent::text(text)],
133            name: None,
134            tool_call_id: None,
135            tool_calls: Vec::new(),
136            reasoning_content: None,
137        }
138    }
139
140    pub fn text_content(&self) -> Option<String> {
141        let mut parts = Vec::new();
142        for content in &self.content {
143            if let MessageContent::Text { text, .. } = content {
144                parts.push(text.as_str());
145            }
146        }
147        if parts.is_empty() {
148            None
149        } else {
150            Some(parts.join("\n"))
151        }
152    }
153}
154
155#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
156pub struct ChatRequestOptions {
157    #[serde(default, skip_serializing_if = "Option::is_none")]
158    pub temperature: Option<f32>,
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub max_tokens: Option<u32>,
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub stream: Option<bool>,
163}
164
165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
166pub struct ChatRequest {
167    pub model: String,
168    pub messages: Vec<Message>,
169    #[serde(default)]
170    pub options: ChatRequestOptions,
171    #[serde(default, skip_serializing_if = "Vec::is_empty")]
172    pub tools: Vec<ChatTool>,
173    #[serde(default, skip_serializing_if = "Option::is_none")]
174    pub tool_choice: Option<String>,
175    #[serde(default, skip_serializing_if = "is_empty_extra_body")]
176    pub extra_body: serde_json::Value,
177}
178
179impl ChatRequest {
180    pub fn new(model: impl Into<String>, messages: Vec<Message>) -> Self {
181        Self {
182            model: model.into(),
183            messages,
184            options: ChatRequestOptions::default(),
185            tools: Vec::new(),
186            tool_choice: None,
187            extra_body: serde_json::Value::Null,
188        }
189    }
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193pub struct ChatTool {
194    pub name: String,
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub description: Option<String>,
197    pub parameters: serde_json::Value,
198    #[serde(default, skip_serializing_if = "Option::is_none")]
199    pub cache_control: Option<serde_json::Value>,
200}
201
202impl ChatTool {
203    pub fn function(
204        name: impl Into<String>,
205        description: impl Into<String>,
206        parameters: serde_json::Value,
207    ) -> Self {
208        Self {
209            name: name.into(),
210            description: Some(description.into()),
211            parameters,
212            cache_control: None,
213        }
214    }
215
216    pub fn with_cache_control(mut self, cache_control: serde_json::Value) -> Self {
217        self.cache_control = Some(cache_control);
218        self
219    }
220}
221
222#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
223pub struct ToolCall {
224    pub id: String,
225    pub name: String,
226    pub arguments: String,
227    #[serde(default, skip_serializing_if = "Option::is_none")]
228    pub index: Option<usize>,
229    #[serde(default, skip_serializing_if = "Option::is_none")]
230    pub extra_content: Option<serde_json::Value>,
231}
232
233impl ToolCall {
234    pub fn function(
235        id: impl Into<String>,
236        name: impl Into<String>,
237        arguments: impl Into<String>,
238    ) -> Self {
239        Self {
240            id: id.into(),
241            name: name.into(),
242            arguments: arguments.into(),
243            index: None,
244            extra_content: None,
245        }
246    }
247}
248
249#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
250pub struct ChatResponse {
251    pub id: String,
252    pub model: String,
253    pub content: String,
254    #[serde(default, skip_serializing_if = "Vec::is_empty")]
255    pub tool_calls: Vec<ToolCall>,
256    #[serde(default, skip_serializing_if = "Option::is_none")]
257    pub reasoning_content: Option<String>,
258    #[serde(default, skip_serializing_if = "Option::is_none")]
259    pub usage: Option<ChatUsage>,
260}
261
262#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
263pub struct ChatStreamDelta {
264    #[serde(default, skip_serializing_if = "String::is_empty")]
265    pub content: String,
266    #[serde(default, skip_serializing_if = "String::is_empty")]
267    pub reasoning_content: String,
268    #[serde(default, skip_serializing_if = "Vec::is_empty")]
269    pub tool_calls: Vec<ToolCall>,
270    #[serde(default, skip_serializing_if = "Option::is_none")]
271    pub usage: Option<ChatUsage>,
272    #[serde(default, skip_serializing_if = "Option::is_none")]
273    pub raw_content: Option<serde_json::Value>,
274    #[serde(default, skip_serializing_if = "is_false")]
275    pub done: bool,
276}
277
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
279pub struct ChatUsage {
280    #[serde(default, skip_serializing_if = "Option::is_none")]
281    pub prompt_tokens: Option<u32>,
282    #[serde(default, skip_serializing_if = "Option::is_none")]
283    pub completion_tokens: Option<u32>,
284    #[serde(default, skip_serializing_if = "Option::is_none")]
285    pub total_tokens: Option<u32>,
286}
287
288fn is_false(value: &bool) -> bool {
289    !*value
290}
291
292fn is_empty_extra_body(value: &serde_json::Value) -> bool {
293    match value {
294        serde_json::Value::Null => true,
295        serde_json::Value::Object(object) => object.is_empty(),
296        _ => false,
297    }
298}
299
300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
301pub struct EmbeddingResponse {
302    pub model: String,
303    pub data: Vec<EmbeddingData>,
304}
305
306#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
307pub struct EmbeddingData {
308    pub index: u32,
309    pub embedding: Vec<f32>,
310}
311
312#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
313pub struct RerankResponse {
314    pub results: Vec<RerankResult>,
315}
316
317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
318pub struct RerankResult {
319    pub index: usize,
320    pub relevance_score: f32,
321}
322
323#[derive(Debug, thiserror::Error)]
324pub enum VvLlmError {
325    #[error("configuration error: {0}")]
326    Configuration(String),
327    #[error("model not found: backend={backend} model={model}")]
328    ModelNotFound { backend: String, model: String },
329    #[error("endpoint not found: {0}")]
330    EndpointNotFound(String),
331    #[error("serialization error: {0}")]
332    Serialization(#[from] serde_json::Error),
333    #[error("http error: {0}")]
334    Http(String),
335    #[error("provider error: {0}")]
336    Provider(String),
337}