1#![deny(unsafe_code)]
2
3#[cfg(feature = "balance")]
32pub mod balance;
33#[cfg(feature = "chat")]
35pub mod chat;
36#[cfg(feature = "completion")]
38pub mod completion;
39pub mod error;
40#[cfg(feature = "models")]
42pub mod models;
43#[cfg(feature = "responses")]
45pub mod responses;
46
47use crate::error::{ApiErrorEnvelope, DeepSeekError};
48
49use futures_util::StreamExt;
50use reqwest::{Client, Method, RequestBuilder, Response, header::AUTHORIZATION};
51use reqwest_eventsource::{Event, EventSource, RequestBuilderExt};
52use serde::{Serialize, de::DeserializeOwned};
53use std::future::Future;
54use std::sync::LazyLock;
55use tokio::sync::mpsc;
56
57pub static DEFAULT_BASE_URL: LazyLock<String> =
59 LazyLock::new(|| String::from("https://api.deepseek.com"));
60pub static DEFAULT_BETA_BASE_URL: LazyLock<String> =
62 LazyLock::new(|| String::from("https://api.deepseek.com/beta"));
63
64#[derive(Clone, Debug, Eq, PartialEq)]
66struct Credentials {
67 pub(crate) api_key: String,
68 pub(crate) base_url: String,
69}
70
71#[derive(Clone, Debug)]
72pub struct DeepSeekClient {
73 pub(crate) credentials: Credentials,
74 pub client: Client,
75}
76
77impl PartialEq for DeepSeekClient {
78 fn eq(&self, other: &Self) -> bool {
79 self.credentials == other.credentials
80 }
81}
82
83impl Eq for DeepSeekClient {}
84
85impl DeepSeekClient {
86 pub fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
87 DeepSeekClient {
88 credentials: Credentials::new(api_key, base_url),
89 client: Client::new(),
90 }
91 }
92
93 pub fn with_client(mut self, client: Client) -> Self {
94 self.client = client;
95 self
96 }
97
98 pub fn with_credentials(
99 mut self,
100 api_key: impl Into<String>,
101 base_url: impl Into<String>,
102 ) -> Self {
103 self.credentials = Credentials::new(api_key, base_url);
104 self
105 }
106}
107
108impl Credentials {
109 pub(crate) fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
111 Credentials {
112 api_key: api_key.into(),
113 base_url: base_url.into(),
114 }
115 }
116}
117
118pub trait DeepSeekRequest: Sized {
123 type Response;
125
126 type StreamItem;
128 type BlockingStream: Iterator<Item = Self::StreamItem>;
130
131 fn send(self) -> impl Future<Output = Result<Self::Response, DeepSeekError>> + Send;
133 fn stream(
135 self,
136 ) -> impl Future<Output = Result<mpsc::Receiver<Self::StreamItem>, DeepSeekError>> + Send;
137 fn stream_blocking(self) -> Result<Self::BlockingStream, DeepSeekError>;
139}
140
141#[allow(dead_code)]
142async fn api_request_json<F, T>(
143 method: Method,
144 route: &str,
145 builder: F,
146 deepseek_client: DeepSeekClient,
147) -> Result<T, DeepSeekError>
148where
149 F: FnOnce(RequestBuilder) -> RequestBuilder,
150 T: DeserializeOwned,
151{
152 let response = api_request(method, route, builder, deepseek_client).await?;
153 let status = response.status();
154
155 let text = response.text().await?;
156
157 if !status.is_success() {
158 if let Ok(envelope) = serde_json::from_str::<ApiErrorEnvelope>(&text) {
159 return Err(DeepSeekError::api(
160 envelope.error,
161 Some(status.as_u16()),
162 Some(text),
163 ));
164 }
165
166 return Err(DeepSeekError::http(status.as_u16(), text));
167 }
168
169 serde_json::from_str::<T>(&text).map_err(|err| DeepSeekError::decode(err.to_string(), text))
170}
171
172#[allow(dead_code)]
173async fn api_request<F>(
174 method: Method,
175 route: &str,
176 builder: F,
177 deepseek_client: DeepSeekClient,
178) -> Result<Response, DeepSeekError>
179where
180 F: FnOnce(RequestBuilder) -> RequestBuilder,
181{
182 let client = deepseek_client.client;
183 let mut request = client.request(
184 method,
185 format!("{}{route}", deepseek_client.credentials.base_url),
186 );
187 request = builder(request);
188 let response = request
189 .header(
190 AUTHORIZATION,
191 format!("Bearer {}", deepseek_client.credentials.api_key),
192 )
193 .send()
194 .await?;
195 Ok(response)
196}
197
198#[allow(dead_code)]
199async fn api_request_stream<F>(
200 method: Method,
201 route: &str,
202 builder: F,
203 deepseek_client: DeepSeekClient,
204) -> Result<EventSource, DeepSeekError>
205where
206 F: FnOnce(RequestBuilder) -> RequestBuilder,
207{
208 let mut request = deepseek_client.client.request(
209 method,
210 format!("{}{route}", deepseek_client.credentials.base_url),
211 );
212 request = builder(request);
213 let stream = request
214 .header(
215 AUTHORIZATION,
216 format!("Bearer {}", deepseek_client.credentials.api_key),
217 )
218 .eventsource()
219 .map_err(|err| DeepSeekError::decode(err.to_string(), String::new()))?;
220 Ok(stream)
221}
222
223#[allow(dead_code)]
230pub(crate) fn consume_sse<T, F>(
231 mut event_source: EventSource,
232 mut parse: F,
233) -> mpsc::Receiver<Result<T, DeepSeekError>>
234where
235 T: Send + 'static,
236 F: FnMut(String) -> Result<Option<T>, DeepSeekError> + Send + 'static,
237{
238 let (tx, rx) = mpsc::channel(32);
239
240 tokio::spawn(async move {
241 while let Some(event) = event_source.next().await {
242 match event {
243 Ok(Event::Open) => {}
244 Ok(Event::Message(message)) => {
245 if message.data == "[DONE]" {
246 break;
247 }
248 match parse(message.data) {
249 Ok(Some(item)) => {
250 if tx.send(Ok(item)).await.is_err() {
251 break;
252 }
253 }
254 Ok(None) => break,
255 Err(err) => {
256 let _ = tx.send(Err(err)).await;
257 break;
258 }
259 }
260 }
261 Err(err) => {
262 if matches!(err, reqwest_eventsource::Error::StreamEnded) {
263 break;
264 }
265 let _ = tx
266 .send(Err(DeepSeekError::decode(err.to_string(), String::new())))
267 .await;
268 break;
269 }
270 }
271 }
272 });
273
274 rx
275}
276
277#[allow(dead_code)]
283pub(crate) fn spawn_blocking_stream<T>(
284 fut: impl Future<Output = Result<mpsc::Receiver<Result<T, DeepSeekError>>, DeepSeekError>>
285 + Send
286 + 'static,
287) -> Result<std::sync::mpsc::Receiver<Result<T, DeepSeekError>>, DeepSeekError>
288where
289 T: Send + 'static,
290{
291 let (tx, rx) = std::sync::mpsc::channel();
292
293 std::thread::spawn(move || {
294 let runtime = match tokio::runtime::Builder::new_current_thread()
295 .enable_all()
296 .build()
297 {
298 Ok(runtime) => runtime,
299 Err(err) => {
300 let _ = tx.send(Err(DeepSeekError::decode(err.to_string(), String::new())));
301 return;
302 }
303 };
304
305 runtime.block_on(async move {
306 match fut.await {
307 Ok(mut stream_rx) => {
308 while let Some(item) = stream_rx.recv().await {
309 if tx.send(item).is_err() {
310 break;
311 }
312 }
313 }
314 Err(err) => {
315 let _ = tx.send(Err(err));
316 }
317 }
318 });
319 });
320
321 Ok(rx)
322}
323
324#[allow(dead_code)]
326async fn api_get<T>(route: &str, client: DeepSeekClient) -> Result<T, DeepSeekError>
327where
328 T: DeserializeOwned,
329{
330 api_request_json(Method::GET, route, |request| request, client).await
331}
332
333#[allow(dead_code)]
335async fn api_post<J, T>(route: &str, json: &J, client: DeepSeekClient) -> Result<T, DeepSeekError>
336where
337 J: Serialize + ?Sized,
338 T: DeserializeOwned,
339{
340 api_request_json(Method::POST, route, |request| request.json(json), client).await
341}