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 { text: String },
86    ImageUrl { url: String },
87}
88
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
90pub struct Message {
91    pub role: MessageRole,
92    pub content: Vec<MessageContent>,
93    #[serde(default, skip_serializing_if = "Option::is_none")]
94    pub name: Option<String>,
95    #[serde(default, skip_serializing_if = "Option::is_none")]
96    pub tool_call_id: Option<String>,
97    #[serde(default, skip_serializing_if = "Vec::is_empty")]
98    pub tool_calls: Vec<ToolCall>,
99}
100
101impl Message {
102    pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
103        Self {
104            role,
105            content: vec![MessageContent::Text { text: text.into() }],
106            name: None,
107            tool_call_id: None,
108            tool_calls: Vec::new(),
109        }
110    }
111
112    pub fn text_content(&self) -> Option<String> {
113        let mut parts = Vec::new();
114        for content in &self.content {
115            if let MessageContent::Text { text } = content {
116                parts.push(text.as_str());
117            }
118        }
119        if parts.is_empty() {
120            None
121        } else {
122            Some(parts.join("\n"))
123        }
124    }
125}
126
127#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
128pub struct ChatRequestOptions {
129    #[serde(default, skip_serializing_if = "Option::is_none")]
130    pub temperature: Option<f32>,
131    #[serde(default, skip_serializing_if = "Option::is_none")]
132    pub max_tokens: Option<u32>,
133    #[serde(default, skip_serializing_if = "Option::is_none")]
134    pub stream: Option<bool>,
135}
136
137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138pub struct ChatRequest {
139    pub model: String,
140    pub messages: Vec<Message>,
141    #[serde(default)]
142    pub options: ChatRequestOptions,
143    #[serde(default, skip_serializing_if = "Vec::is_empty")]
144    pub tools: Vec<ChatTool>,
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub tool_choice: Option<String>,
147}
148
149impl ChatRequest {
150    pub fn new(model: impl Into<String>, messages: Vec<Message>) -> Self {
151        Self {
152            model: model.into(),
153            messages,
154            options: ChatRequestOptions::default(),
155            tools: Vec::new(),
156            tool_choice: None,
157        }
158    }
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub struct ChatTool {
163    pub name: String,
164    #[serde(default, skip_serializing_if = "Option::is_none")]
165    pub description: Option<String>,
166    pub parameters: serde_json::Value,
167}
168
169impl ChatTool {
170    pub fn function(
171        name: impl Into<String>,
172        description: impl Into<String>,
173        parameters: serde_json::Value,
174    ) -> Self {
175        Self {
176            name: name.into(),
177            description: Some(description.into()),
178            parameters,
179        }
180    }
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
184pub struct ToolCall {
185    pub id: String,
186    pub name: String,
187    pub arguments: String,
188}
189
190impl ToolCall {
191    pub fn function(
192        id: impl Into<String>,
193        name: impl Into<String>,
194        arguments: impl Into<String>,
195    ) -> Self {
196        Self {
197            id: id.into(),
198            name: name.into(),
199            arguments: arguments.into(),
200        }
201    }
202}
203
204#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct ChatResponse {
206    pub id: String,
207    pub model: String,
208    pub content: String,
209    #[serde(default, skip_serializing_if = "Vec::is_empty")]
210    pub tool_calls: Vec<ToolCall>,
211    #[serde(default, skip_serializing_if = "Option::is_none")]
212    pub usage: Option<ChatUsage>,
213}
214
215#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
216pub struct ChatStreamDelta {
217    #[serde(default, skip_serializing_if = "String::is_empty")]
218    pub content: String,
219    #[serde(default, skip_serializing_if = "String::is_empty")]
220    pub reasoning_content: String,
221    #[serde(default, skip_serializing_if = "Vec::is_empty")]
222    pub tool_calls: Vec<ToolCall>,
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub usage: Option<ChatUsage>,
225    #[serde(default, skip_serializing_if = "Option::is_none")]
226    pub raw_content: Option<serde_json::Value>,
227    #[serde(default, skip_serializing_if = "is_false")]
228    pub done: bool,
229}
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct ChatUsage {
233    #[serde(default, skip_serializing_if = "Option::is_none")]
234    pub prompt_tokens: Option<u32>,
235    #[serde(default, skip_serializing_if = "Option::is_none")]
236    pub completion_tokens: Option<u32>,
237    #[serde(default, skip_serializing_if = "Option::is_none")]
238    pub total_tokens: Option<u32>,
239}
240
241fn is_false(value: &bool) -> bool {
242    !*value
243}
244
245#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
246pub struct EmbeddingResponse {
247    pub model: String,
248    pub data: Vec<EmbeddingData>,
249}
250
251#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
252pub struct EmbeddingData {
253    pub index: u32,
254    pub embedding: Vec<f32>,
255}
256
257#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
258pub struct RerankResponse {
259    pub results: Vec<RerankResult>,
260}
261
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub struct RerankResult {
264    pub index: usize,
265    pub relevance_score: f32,
266}
267
268#[derive(Debug, thiserror::Error)]
269pub enum VvLlmError {
270    #[error("configuration error: {0}")]
271    Configuration(String),
272    #[error("model not found: backend={backend} model={model}")]
273    ModelNotFound { backend: String, model: String },
274    #[error("endpoint not found: {0}")]
275    EndpointNotFound(String),
276    #[error("serialization error: {0}")]
277    Serialization(#[from] serde_json::Error),
278    #[error("http error: {0}")]
279    Http(String),
280    #[error("provider error: {0}")]
281    Provider(String),
282}