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 DeepSeekClient {
63 pub fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
64 DeepSeekClient {
65 credentials: Credentials::new(api_key, base_url),
66 client: Client::new(),
67 }
68 }
69
70 pub fn with_client(mut self, client: Client) -> Self {
71 self.client = client;
72 self
73 }
74
75 pub fn with_credentials(
76 mut self,
77 api_key: impl Into<String>,
78 base_url: impl Into<String>,
79 ) -> Self {
80 self.credentials = Credentials::new(api_key, base_url);
81 self
82 }
83}
84
85impl Credentials {
86 pub(crate) fn new(api_key: impl Into<String>, base_url: impl Into<String>) -> Self {
88 Credentials {
89 api_key: api_key.into(),
90 base_url: base_url.into(),
91 }
92 }
93}
94
95pub trait DeepSeekRequest: Sized {
100 type Response;
102
103 type StreamItem;
105 type BlockingStream: Iterator<Item = Self::StreamItem>;
107
108 fn send(self) -> impl Future<Output = Result<Self::Response, DeepSeekError>> + Send;
110 fn stream(
112 self,
113 ) -> impl Future<Output = Result<mpsc::Receiver<Self::StreamItem>, DeepSeekError>> + Send;
114 fn stream_blocking(self) -> Result<Self::BlockingStream, DeepSeekError>;
116}
117
118async fn api_request_json<F, T>(
119 method: Method,
120 route: &str,
121 builder: F,
122 deepseek_client: DeepSeekClient,
123) -> Result<T, DeepSeekError>
124where
125 F: FnOnce(RequestBuilder) -> RequestBuilder,
126 T: DeserializeOwned,
127{
128 let response = api_request(method, route, builder, deepseek_client).await?;
129 let status = response.status();
130
131 let text = response.text().await?;
132
133 if !status.is_success() {
134 if let Ok(envelope) = serde_json::from_str::<ApiErrorEnvelope>(&text) {
135 return Err(DeepSeekError::api(
136 envelope.error,
137 Some(status.as_u16()),
138 Some(text),
139 ));
140 }
141
142 return Err(DeepSeekError::http(status.as_u16(), text));
143 }
144
145 serde_json::from_str::<T>(&text).map_err(|err| DeepSeekError::decode(err.to_string(), text))
146}
147
148async fn api_request<F>(
149 method: Method,
150 route: &str,
151 builder: F,
152 deepseek_client: DeepSeekClient,
153) -> Result<Response, DeepSeekError>
154where
155 F: FnOnce(RequestBuilder) -> RequestBuilder,
156{
157 let client = deepseek_client.client;
158 let mut request = client.request(
159 method,
160 format!("{}{route}", deepseek_client.credentials.base_url),
161 );
162 request = builder(request);
163 let response = request
164 .header(
165 AUTHORIZATION,
166 format!("Bearer {}", deepseek_client.credentials.api_key),
167 )
168 .send()
169 .await?;
170 Ok(response)
171}
172
173async fn api_request_stream<F>(
174 method: Method,
175 route: &str,
176 builder: F,
177 deepseek_client: DeepSeekClient,
178) -> Result<EventSource, DeepSeekError>
179where
180 F: FnOnce(RequestBuilder) -> RequestBuilder,
181{
182 let mut request = deepseek_client.client.request(
183 method,
184 format!("{}{route}", deepseek_client.credentials.base_url),
185 );
186 request = builder(request);
187 let stream = request
188 .header(
189 AUTHORIZATION,
190 format!("Bearer {}", deepseek_client.credentials.api_key),
191 )
192 .eventsource()
193 .map_err(|err| DeepSeekError::decode(err.to_string(), String::new()))?;
194 Ok(stream)
195}
196
197async fn api_get<T>(route: &str, client: DeepSeekClient) -> Result<T, DeepSeekError>
198where
199 T: DeserializeOwned,
200{
201 api_request_json(Method::GET, route, |request| request, client).await
202}
203
204async fn api_post<J, T>(route: &str, json: &J, client: DeepSeekClient) -> Result<T, DeepSeekError>
205where
206 J: Serialize + ?Sized,
207 T: DeserializeOwned,
208{
209 api_request_json(Method::POST, route, |request| request.json(json), client).await
210}