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, Deserialize, Serialize)]
156pub struct UsageInfo {
157 pub prompt_tokens: u32,
158 pub completion_tokens: u32,
159 pub total_tokens: u32,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct AgentResult {
166 pub success: bool,
167 pub result: Option<String>,
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub error: Option<String>,
170 pub turns: u32,
171 pub usage: UsageInfo,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub tool_calls_made: Option<Vec<String>>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub session_id: Option<String>,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct AgentDefinition {
182 pub name: String,
183 pub description: String,
184 pub prompt: String,
185 #[serde(default)]
186 pub model: DeepSeekModel,
187 #[serde(default)]
188 pub tools: Vec<String>,
189 #[serde(default)]
190 pub disallowed_tools: Vec<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub max_turns: Option<u32>,
193}
194
195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
198pub enum EffortLevel {
199 Low,
200 Medium,
201 #[default]
202 High,
203 Max,
204}
205
206impl EffortLevel {
207 pub fn temperature(&self) -> f64 {
209 match self {
210 Self::Low => 0.1,
211 Self::Medium => 0.5,
212 Self::High => 0.7,
213 Self::Max => 1.0,
214 }
215 }
216
217 pub fn max_tokens(&self) -> u32 {
219 match self {
220 Self::Low => 2048,
221 Self::Medium => 4096,
222 Self::High => 8192,
223 Self::Max => 16384,
224 }
225 }
226}
227
228pub fn system_msg(content: &str) -> ChatMessage {
231 ChatMessage {
232 role: "system".into(),
233 content: ChatContent::Text(content.into()),
234 reasoning_content: None,
235 tool_calls: None,
236 tool_call_id: None,
237 name: None,
238 }
239}
240
241pub fn user_msg(content: &str) -> ChatMessage {
242 ChatMessage {
243 role: "user".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 assistant_msg(content: &str) -> ChatMessage {
253 ChatMessage {
254 role: "assistant".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 struct ReasonerOutput {
266 pub reasoning: String,
268 pub content: String,
270}
271
272pub fn tool_result_msg(tool_call_id: &str, content: &str) -> ChatMessage {
273 ChatMessage {
274 role: "tool".into(),
275 content: ChatContent::Text(content.into()),
276 reasoning_content: None,
277 tool_calls: None,
278 tool_call_id: Some(tool_call_id.into()),
279 name: None,
280 }
281}