1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
7pub enum DeepSeekModel {
8 #[serde(rename = "deepseek-v4-pro")]
10 #[default]
11 V4Pro,
12 #[serde(rename = "deepseek-reasoner")]
14 Reasoner,
15 #[serde(rename = "deepseek-chat")]
17 Chat,
18}
19
20impl DeepSeekModel {
21 pub fn as_str(&self) -> &'static str {
22 match self {
23 Self::V4Pro => "deepseek-v4-pro",
24 Self::Reasoner => "deepseek-reasoner",
25 Self::Chat => "deepseek-chat",
26 }
27 }
28
29 pub fn from_alias(alias: &str) -> Self {
30 match alias {
31 "v4" | "v4-pro" | "pro" | "latest" => Self::V4Pro,
32 "reasoner" | "r1" | "deep" | "opus" => Self::Reasoner,
33 "chat" | "v3" | "fast" | "sonnet" | "haiku" => Self::Chat,
34 _ => Self::V4Pro,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ChatMessage {
43 pub role: String,
44 pub content: ChatContent,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub reasoning_content: Option<String>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub tool_calls: Option<Vec<ToolCall>>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub tool_call_id: Option<String>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub name: Option<String>,
53}
54
55#[derive(Debug, Clone)]
56pub enum ChatContent {
57 Text(String),
58 Null,
59}
60
61impl ChatContent {
62 pub fn as_str(&self) -> &str {
63 match self {
64 Self::Text(s) => s,
65 Self::Null => "",
66 }
67 }
68}
69
70impl Serialize for ChatContent {
71 fn serialize<S: serde::Serializer>(
72 &self,
73 serializer: S,
74 ) -> std::result::Result<S::Ok, S::Error> {
75 match self {
76 Self::Text(s) => serializer.serialize_str(s),
77 Self::Null => serializer.serialize_none(),
78 }
79 }
80}
81
82impl<'de> Deserialize<'de> for ChatContent {
83 fn deserialize<D: serde::Deserializer<'de>>(
84 deserializer: D,
85 ) -> std::result::Result<Self, D::Error> {
86 let value = serde_json::Value::deserialize(deserializer)?;
87 match value {
88 serde_json::Value::String(s) => Ok(Self::Text(s)),
89 serde_json::Value::Null => Ok(Self::Null),
90 other => Ok(Self::Text(other.to_string())),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ToolCall {
97 pub id: String,
98 pub r#type: String,
99 pub function: FunctionCall,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct FunctionCall {
104 pub name: String,
105 pub arguments: String,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct ChatRequest {
110 pub model: String,
111 pub messages: Vec<ChatMessage>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub tools: Option<Vec<ToolSchema>>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub tool_choice: Option<serde_json::Value>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub temperature: Option<f64>,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub max_tokens: Option<u32>,
120 #[serde(skip_serializing_if = "Option::is_none")]
121 pub stream: Option<bool>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub reasoning_effort: Option<String>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub thinking: Option<serde_json::Value>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct ToolSchema {
130 pub r#type: String,
131 pub function: FunctionSchema,
132}
133
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct FunctionSchema {
136 pub name: String,
137 pub description: String,
138 pub parameters: serde_json::Value,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct ChatResponse {
143 pub id: String,
144 pub choices: Vec<Choice>,
145 pub usage: Option<UsageInfo>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct Choice {
150 pub index: u32,
151 pub message: ChatMessage,
152 pub finish_reason: Option<String>,
153}
154
155#[derive(Debug, Clone, Default, Deserialize, Serialize)]
156pub struct UsageInfo {
157 pub prompt_tokens: u32,
158 pub completion_tokens: u32,
159 pub total_tokens: u32,
160 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub prompt_cache_hit_tokens: Option<u32>,
166 #[serde(default, skip_serializing_if = "Option::is_none")]
170 pub prompt_cache_miss_tokens: Option<u32>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct AgentResult {
177 pub success: bool,
178 pub result: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub error: Option<String>,
181 pub turns: u32,
182 pub usage: UsageInfo,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub tool_calls_made: Option<Vec<String>>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub session_id: Option<String>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct AgentDefinition {
193 pub name: String,
194 pub description: String,
195 pub prompt: String,
196 #[serde(default)]
197 pub model: DeepSeekModel,
198 #[serde(default)]
199 pub tools: Vec<String>,
200 #[serde(default)]
201 pub disallowed_tools: Vec<String>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub max_turns: Option<u32>,
204}
205
206#[derive(Debug, Clone, Default, Serialize, Deserialize)]
209pub enum EffortLevel {
210 Low,
211 Medium,
212 #[default]
213 High,
214 Max,
215}
216
217impl EffortLevel {
218 pub fn temperature(&self) -> f64 {
220 match self {
221 Self::Low => 0.1,
222 Self::Medium => 0.5,
223 Self::High => 0.7,
224 Self::Max => 1.0,
225 }
226 }
227
228 pub fn max_tokens(&self) -> u32 {
230 match self {
231 Self::Low => 2048,
232 Self::Medium => 4096,
233 Self::High => 8192,
234 Self::Max => 16384,
235 }
236 }
237}
238
239pub fn system_msg(content: &str) -> ChatMessage {
242 ChatMessage {
243 role: "system".into(),
244 content: ChatContent::Text(content.into()),
245 reasoning_content: None,
246 tool_calls: None,
247 tool_call_id: None,
248 name: None,
249 }
250}
251
252pub fn user_msg(content: &str) -> ChatMessage {
253 ChatMessage {
254 role: "user".into(),
255 content: ChatContent::Text(content.into()),
256 reasoning_content: None,
257 tool_calls: None,
258 tool_call_id: None,
259 name: None,
260 }
261}
262
263pub fn assistant_msg(content: &str) -> ChatMessage {
264 ChatMessage {
265 role: "assistant".into(),
266 content: ChatContent::Text(content.into()),
267 reasoning_content: None,
268 tool_calls: None,
269 tool_call_id: None,
270 name: None,
271 }
272}
273
274pub struct ReasonerOutput {
277 pub reasoning: String,
279 pub content: String,
281}
282
283pub fn tool_result_msg(tool_call_id: &str, content: &str) -> ChatMessage {
284 ChatMessage {
285 role: "tool".into(),
286 content: ChatContent::Text(content.into()),
287 reasoning_content: None,
288 tool_calls: None,
289 tool_call_id: Some(tool_call_id.into()),
290 name: None,
291 }
292}