1#![deny(unsafe_code)]
2
3#[cfg(feature = "chat")]
31pub mod chat;
32#[cfg(feature = "completion")]
34pub mod completion;
35#[cfg(feature = "models")]
37pub mod models;
38#[cfg(feature = "balance")]
40pub mod balance;
41pub mod error;
42
43use crate::error::{ApiErrorEnvelope, DeepSeekError};
44
45use reqwest::{Client, Method, RequestBuilder, Response, header::AUTHORIZATION};
46use reqwest_eventsource::{EventSource, RequestBuilderExt};
47use serde::{Serialize, de::DeserializeOwned};
48use std::future::Future;
49use std::sync::LazyLock;
50use tokio::sync::mpsc;
51
52pub static DEFAULT_BASE_URL: LazyLock<String> =
54 LazyLock::new(|| String::from("https://api.deepseek.com"));
55pub static DEFAULT_BETA_BASE_URL: LazyLock<String> =
57 LazyLock::new(|| String::from("https://api.deepseek.com/beta"));
58
59#[derive(Clone, Debug, Eq, PartialEq)]
61struct Credentials {
62 pub(crate) api_key: String,
63 pub(crate) base_url: String,
64}
65
66#[derive(Clone, Debug)]
67pub struct DeepSeekClient {
68 pub(crate) credentials: Credentials,
69 pub client: Client,
70}
71
72impl PartialEq for DeepSeekClient {
73 fn eq(&self, other: &Self) -> bool {
74 self.credentials == other.credentials
75 }
76}
77
78impl Eq for DeepSeekClient {}
79
80impl DeepSeekClient {
81 pub fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
82 DeepSeekClient {
83 credentials: Credentials::new(api_key, base_url),
84 client: Client::new(),
85 }
86 }
87
88 pub fn with_client(mut self, client: Client) -> Self {
89 self.client = client;
90 self
91 }
92
93 pub fn with_credentials(
94 mut self,
95 api_key: impl Into<String>,
96 base_url: impl Into<String>,
97 ) -> Self {
98 self.credentials = Credentials::new(api_key, base_url);
99 self
100 }
101}
102
103impl Credentials {
104 pub(crate) fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
106 Credentials {
107 api_key: api_key.into(),
108 base_url: base_url.into(),
109 }
110 }
111}
112
113pub trait DeepSeekRequest: Sized {
118 type Response;
120
121 type StreamItem;
123 type BlockingStream: Iterator<Item = Self::StreamItem>;
125
126 fn send(self) -> impl Future<Output = Result<Self::Response, DeepSeekError>> + Send;
128 fn stream(
130 self,
131 ) -> impl Future<Output = Result<mpsc::Receiver<Self::StreamItem>, DeepSeekError>> + Send;
132 fn stream_blocking(self) -> Result<Self::BlockingStream, DeepSeekError>;
134}
135
136#[allow(dead_code)]
137async fn api_request_json<F, T>(
138 method: Method,
139 route: &str,
140 builder: F,
141 deepseek_client: DeepSeekClient,
142) -> Result<T, DeepSeekError>
143where
144 F: FnOnce(RequestBuilder) -> RequestBuilder,
145 T: DeserializeOwned,
146{
147 let response = api_request(method, route, builder, deepseek_client).await?;
148 let status = response.status();
149
150 let text = response.text().await?;
151
152 if !status.is_success() {
153 if let Ok(envelope) = serde_json::from_str::<ApiErrorEnvelope>(&text) {
154 return Err(DeepSeekError::api(
155 envelope.error,
156 Some(status.as_u16()),
157 Some(text),
158 ));
159 }
160
161 return Err(DeepSeekError::http(status.as_u16(), text));
162 }
163
164 serde_json::from_str::<T>(&text).map_err(|err| DeepSeekError::decode(err.to_string(), text))
165}
166
167#[allow(dead_code)]
168async fn api_request<F>(
169 method: Method,
170 route: &str,
171 builder: F,
172 deepseek_client: DeepSeekClient,
173) -> Result<Response, DeepSeekError>
174where
175 F: FnOnce(RequestBuilder) -> RequestBuilder,
176{
177 let client = deepseek_client.client;
178 let mut request = client.request(
179 method,
180 format!("{}{route}", deepseek_client.credentials.base_url),
181 );
182 request = builder(request);
183 let response = request
184 .header(
185 AUTHORIZATION,
186 format!("Bearer {}", deepseek_client.credentials.api_key),
187 )
188 .send()
189 .await?;
190 Ok(response)
191}
192
193#[allow(dead_code)]
194async fn api_request_stream<F>(
195 method: Method,
196 route: &str,
197 builder: F,
198 deepseek_client: DeepSeekClient,
199) -> Result<EventSource, DeepSeekError>
200where
201 F: FnOnce(RequestBuilder) -> RequestBuilder,
202{
203 let mut request = deepseek_client.client.request(
204 method,
205 format!("{}{route}", deepseek_client.credentials.base_url),
206 );
207 request = builder(request);
208 let stream = request
209 .header(
210 AUTHORIZATION,
211 format!("Bearer {}", deepseek_client.credentials.api_key),
212 )
213 .eventsource()
214 .map_err(|err| DeepSeekError::decode(err.to_string(), String::new()))?;
215 Ok(stream)
216}
217
218#[allow(dead_code)]
220async fn api_get<T>(route: &str, client: DeepSeekClient) -> Result<T, DeepSeekError>
221where
222 T: DeserializeOwned,
223{
224 api_request_json(Method::GET, route, |request| request, client).await
225}
226
227#[allow(dead_code)]
229async fn api_post<J, T>(route: &str, json: &J, client: DeepSeekClient) -> Result<T, DeepSeekError>
230where
231 J: Serialize + ?Sized,
232 T: DeserializeOwned,
233{
234 api_request_json(Method::POST, route, |request| request.json(json), client).await
235}