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