1use log::{debug, warn, error, info, trace};
2use anyhow::{anyhow, Result};
3use base64::{engine::general_purpose::STANDARD, Engine as _};
4use futures::StreamExt;
5use rquest::header::{HeaderMap, HeaderValue};
6use rquest::Client;
7use rquest_util::Emulation;
8use serde::{Deserialize, Serialize};
9use serde_json::{json, Value};
10use std::fs;
11use std::path::Path;
12use std::time::{Duration, Instant};
13use rand::Rng;
14use crate::sha3;
15
16
17const BASE_URL: &str = "https://chat.deepseek.com/api/v0";
19
20#[derive(Clone, Copy)]
22pub enum ThinkingMode {
23 Detailed,
24 Simple,
25 Disabled,
26}
27
28impl ThinkingMode {
29 fn as_bool(&self) -> bool {
30 !matches!(self, ThinkingMode::Disabled)
31 }
32}
33
34#[derive(Clone, Copy)]
35pub enum SearchMode {
36 Enabled,
37 Disabled,
38}
39
40impl SearchMode {
41 fn as_bool(&self) -> bool {
42 matches!(self, SearchMode::Enabled)
43 }
44}
45
46#[derive(Clone)]
48pub struct Session {
49 pub auth_token: String,
50 pub cookies: serde_json::Value,
51}
52
53#[derive(Debug, Deserialize, Serialize)]
55pub struct PowChallenge {
56 pub algorithm: String,
57 pub challenge: String,
58 pub salt: String,
59 pub difficulty: f64,
60 pub expire_at: u64,
61 pub signature: String,
62 pub target_path: String,
63}
64
65pub struct DeepSeekPOW {}
67
68impl DeepSeekPOW {
69 pub fn new() -> Result<Self> {
70 debug!("Initializing native SHA3 POW solver");
71 Ok(Self {})
72 }
73
74 fn solve_with_wasm(&self, config: &PowChallenge) -> Result<i64> {
75 debug!("Attempting POW solve with WASM implementation");
76 use crate::sha3::WasmInstance;
77
78 match WasmInstance::new() {
79 Ok(mut instance) => {
80 let prefix = format!("{}_{}_", config.salt, config.expire_at);
81
82 match instance.calculate_hash(&config.challenge, &prefix, config.difficulty) {
83 Ok(Some(nonce)) => {
84 Ok(nonce as i64)
85 }
86 Ok(None) => {
87 Err(anyhow!("WASM did not find solution"))
88 }
89 Err(e) => {
90 Err(anyhow!("WASM error: {}", e))
91 }
92 }
93 }
94 Err(e) => {
95 Err(anyhow!("Failed to create WASM instance: {}", e))
96 }
97 }
98 }
99
100 fn solve_with_native(&self, config: &PowChallenge) -> Result<i64> {
101 debug!("Solving POW with native SHA3 implementation");
102 let challenge_str = &config.challenge;
103 let prefix = format!("{}_{}_", config.salt, config.expire_at);
104
105 let prefix_bytes = prefix.as_bytes();
107 let challenge_bytes = challenge_str.as_bytes();
108
109 debug!("Solving with prefix: '{}', challenge: '{}', difficulty: {}",
110 prefix, challenge_str, config.difficulty);
111
112 let (success, nonce) = sha3::solve(challenge_bytes, prefix_bytes, config.difficulty);
113 if success {
114 info!("Native POW solver found solution: nonce = {nonce}");
115 Ok(nonce as i64)
116 } else {
117 Err(anyhow!("Failed to find POW solution within attempt limit"))
118 }
119 }
120
121
122 pub fn solve_challenge(&self, config: &PowChallenge) -> Result<String> {
123 debug!("Processing verification: algorithm={}, difficulty={}", config.algorithm, config.difficulty);
124
125 let _solve_start = std::time::Instant::now();
126
127 let answer = match self.solve_with_wasm(config) {
129 Ok(nonce) => {
130 nonce
131 }
132 Err(_wasm_err) => {
133
134 self.solve_with_native(config)?
135 }
136 };
137
138 let result = json!({
139 "algorithm": config.algorithm,
140 "challenge": config.challenge,
141 "salt": config.salt,
142 "answer": answer,
143 "signature": config.signature,
144 "target_path": config.target_path
145 });
146
147 let encoded = STANDARD.encode(result.to_string());
148 debug!("POW response encoded: {encoded}");
149 Ok(encoded)
150 }
151}
152
153pub struct DeepSeek {
155 http: Client,
156 session: Session,
157 pow_solver: DeepSeekPOW,
158 cookies_refreshed: bool,
159 last_request_time: Instant,
160 message_counter: i32,
161 message_history: Vec<ChatMessage>,
162}
163
164#[derive(Clone, Debug, Serialize)]
165struct ChatMessage {
166 role: String,
167 content: String,
168}
169
170impl DeepSeek {
171 pub fn new(session: Session) -> Result<Self> {
173 info!("Creating DeepSeek client");
174
175 let http = Client::builder()
176 .emulation(Emulation::Chrome131)
177 .timeout(Duration::from_secs(600))
178 .connect_timeout(Duration::from_secs(15))
179 .gzip(true)
180 .brotli(true)
181 .deflate(true)
182 .build()?;
183
184 let pow_solver = DeepSeekPOW::new()?;
185
186 debug!("DeepSeek client initialized successfully");
187 Ok(Self {
188 http,
189 session,
190 pow_solver,
191 cookies_refreshed: false,
192 last_request_time: Instant::now(),
193 message_counter: 0,
194 message_history: Vec::new(),
195 })
196 }
197
198 fn default_headers(&self, pow_response: Option<&str>) -> HeaderMap {
199 let mut headers = HeaderMap::new();
200 headers.insert("accept", HeaderValue::from_static("*/*"));
201 headers.insert(
202 "accept-encoding",
203 HeaderValue::from_static("gzip, deflate, br, zstd")
204 );
205 headers.insert(
206 "accept-language",
207 HeaderValue::from_static("en-US,en;q=0.9")
208 );
209 headers.insert(
210 "authorization",
211 HeaderValue::from_str(&format!("Bearer {}", self.session.auth_token)).unwrap()
212 );
213 headers.insert(
214 "cache-control",
215 HeaderValue::from_static("no-cache")
216 );
217 headers.insert(
218 "content-type",
219 HeaderValue::from_static("application/json")
220 );
221 headers.insert(
222 "dnt",
223 HeaderValue::from_static("1")
224 );
225 headers.insert(
226 "origin",
227 HeaderValue::from_static("https://chat.deepseek.com")
228 );
229 headers.insert(
230 "pragma",
231 HeaderValue::from_static("no-cache")
232 );
233 headers.insert(
234 "priority",
235 HeaderValue::from_static("u=1, i")
236 );
237 headers.insert(
238 "referer",
239 HeaderValue::from_static("https://chat.deepseek.com/")
240 );
241 headers.insert(
242 "sec-ch-ua",
243 HeaderValue::from_static("\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"")
244 );
245 headers.insert(
246 "sec-ch-ua-mobile",
247 HeaderValue::from_static("?0")
248 );
249 headers.insert(
250 "sec-ch-ua-platform",
251 HeaderValue::from_static("\"Windows\"")
252 );
253 headers.insert(
254 "sec-fetch-dest",
255 HeaderValue::from_static("empty")
256 );
257 headers.insert(
258 "sec-fetch-mode",
259 HeaderValue::from_static("cors")
260 );
261 headers.insert(
262 "sec-fetch-site",
263 HeaderValue::from_static("same-origin")
264 );
265 headers.insert(
266 "user-agent",
267 HeaderValue::from_static("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
268 );
269 headers.insert(
270 "x-app-version",
271 HeaderValue::from_static("20241201.0")
272 );
273 headers.insert(
274 "x-client-locale",
275 HeaderValue::from_static("en_US")
276 );
277 headers.insert(
278 "x-client-platform",
279 HeaderValue::from_static("web")
280 );
281 headers.insert(
282 "x-client-version",
283 HeaderValue::from_static("1.0.0")
284 );
285
286 if let Some(pow_res) = pow_response {
287 headers.insert(
288 "x-ds-pow-response",
289 HeaderValue::from_str(pow_res).unwrap()
290 );
291 }
292
293 headers
294 }
295
296 async fn refresh_cookies(&mut self) -> Result<()> {
298 warn!("Attempting to refresh cookies");
299 if self.cookies_refreshed {
301 return Err(anyhow!("Already attempted to refresh cookies once this session"));
302 }
303
304 self.cookies_refreshed = true;
307
308 Err(anyhow!(
311 "Cookie refresh needed. Please run the Python bypass script manually and update the cookie file"
312 ))
313 }
314
315 async fn make_request<T: for<'de> Deserialize<'de>>(
317 &mut self,
318 method: &str,
319 endpoint: &str,
320 json_data: Value,
321 pow_required: bool
322 ) -> Result<T> {
323 let url = format!("{BASE_URL}{endpoint}");
324 debug!("Making {method} request to: {url}");
325 let mut retry_count = 0;
326 trace!("Request JSON data: {json_data}");
327 let max_retries = 2;
328
329 while retry_count < max_retries {
330 debug!("Request attempt {} of {}", retry_count + 1, max_retries);
331 let mut headers = self.default_headers(None);
332
333 if pow_required {
334 let challenge = self.get_pow_challenge().await?;
335 let pow_response = self.pow_solver.solve_challenge(&challenge)?;
336 headers = self.default_headers(Some(&pow_response));
337 }
338
339 let request_builder = match method {
340 "GET" => self.http.get(&url),
341 "POST" => self.http.post(&url),
342 "DELETE" => self.http.delete(&url),
343 "PUT" => self.http.put(&url),
344 _ => return Err(anyhow!("Unsupported HTTP method: {}", method)),
345 };
346
347 let cookies_map = match &self.session.cookies {
349 Value::Object(obj) => obj,
350 _ => return Err(anyhow!("Invalid cookies format in session")),
351 };
352
353 let mut cookie_str = String::new();
354 for (key, value) in cookies_map {
355 if let Value::String(val) = value {
356 if !cookie_str.is_empty() {
357 cookie_str.push_str("; ");
358 }
359 cookie_str.push_str(&format!("{key}={val}"));
360 }
361 }
362
363 if !cookie_str.is_empty() {
364 headers.insert("Cookie", HeaderValue::from_str(&cookie_str)?);
365 debug!("Added cookies: {} characters", cookie_str.len());
366 } else {
367 warn!("No cookies available for request");
368 }
369
370 let elapsed = self.last_request_time.elapsed();
372 if elapsed < Duration::from_millis(200) {
373 let mut rng = rand::thread_rng();
374 let delay_ms = rng.gen_range(200..800);
375 tokio::time::sleep(Duration::from_millis(delay_ms)).await;
376 }
377 self.last_request_time = Instant::now();
378
379 let response = match request_builder
380 .headers(headers)
381 .json(&json_data)
382 .send()
383 .await
384 {
385 Ok(resp) => resp,
386 Err(e) => {
387 error!("Network error on attempt {}: {}", retry_count + 1, e);
388 return Err(anyhow!("Network error: {}", e));
389 }
390 };
391
392 let status = response.status();
394 let text = response.text().await?;
395 debug!("Response received - Status: {}, Content length: {}", status, text.len());
396 trace!("Response body preview: {}", &text[..std::cmp::min(200, text.len())]);
397
398 if text.contains("<!DOCTYPE html>") && text.contains("Just a moment") {
399 debug!("Page verification required on attempt {}", retry_count + 1);
400 debug!("Response text snippet: {}", &text[..std::cmp::min(200, text.len())]);
401 if retry_count < max_retries - 1 {
402 self.refresh_cookies().await.ok(); retry_count += 1;
404 continue;
405 } else {
406 return Err(anyhow!("Request verification failed"));
407 }
408 }
409
410 match status.as_u16() {
412 200 => {
413 debug!("Successful response received, parsing JSON");
414 match serde_json::from_str(&text) {
416 Ok(parsed) => return Ok(parsed),
417 Err(e) => {
418 error!("Failed to parse JSON response: {e}");
419 debug!("Response text: {text}");
420 return Err(anyhow!("Failed to parse API response: {}", e));
421 }
422 }
423 }
424 401 => {
425 error!("Authentication error (401): {text}");
426 return Err(anyhow!("Authentication error: Invalid or expired authentication token"));
427 }
428 429 => {
429 warn!("Rate limit exceeded (429): {text}");
430 return Err(anyhow!("Rate limit exceeded"));
431 }
432 _ => {
433 warn!("HTTP error {} on attempt {}: {}", status, retry_count + 1, text);
434 if retry_count < max_retries - 1 {
435 retry_count += 1;
436 tokio::time::sleep(Duration::from_secs(1)).await;
437 continue;
438 }
439 error!("API request failed after {max_retries} attempts with status {status}: {text}");
440 return Err(anyhow!("API request failed with status {}: {}", status, text));
441 }
442 }
443 }
444
445 Err(anyhow!("Failed to get a valid response after {} attempts", max_retries))
446 }
447
448 async fn get_pow_challenge(&mut self) -> Result<PowChallenge> {
450 debug!("Requesting verification token");
451 let url = format!("{BASE_URL}/chat/create_pow_challenge");
452 debug!("POW challenge URL: {url}");
453
454 let request_body = json!({"target_path": "/api/v0/chat/completion"}).to_string();
455 debug!("POW request body: {request_body}");
456
457 let mut headers = self.default_headers(None);
458 debug!("Prepared {} headers for POW request", headers.len());
459
460 let cookies_map = match &self.session.cookies {
462 Value::Object(obj) => {
463 debug!("Found {} cookie entries for POW request", obj.len());
464 obj
465 },
466 _ => {
467 error!("Invalid cookies format in session for POW request");
468 return Err(anyhow!("Invalid cookies format in session"));
469 }
470 };
471
472 let mut cookie_str = String::new();
473 let mut cookie_count = 0;
474 for (key, value) in cookies_map {
475 if let Value::String(val) = value {
476 if !cookie_str.is_empty() {
477 cookie_str.push_str("; ");
478 }
479 cookie_str.push_str(&format!("{key}={val}"));
480 cookie_count += 1;
481 }
482 }
483
484 if !cookie_str.is_empty() {
485 headers.insert("Cookie", HeaderValue::from_str(&cookie_str)?);
486 debug!("Added {} cookies to POW request ({} chars)", cookie_count, cookie_str.len());
487 } else {
488 warn!("No cookies available for POW challenge request");
489 }
490
491 debug!("Sending POW challenge HTTP request...");
493 let response = match self.http.post(&url)
494 .headers(headers)
495 .body(request_body)
496 .send()
497 .await {
498 Ok(r) => {
499 debug!("POW challenge request completed, status: {}", r.status());
500 r
501 },
502 Err(e) => {
503 error!("POW challenge HTTP request failed: {e}");
504 return Err(anyhow!("POW challenge request failed: {}", e));
505 }
506 };
507
508 let status = response.status();
509 debug!("POW challenge response status: {status}");
510
511 if !status.is_success() {
512 let error_text = response.text().await?;
513 error!("POW challenge request failed: HTTP {status} - {error_text}");
514 return Err(anyhow!(
515 "Failed to get challenge: HTTP {} - {}",
516 status,
517 error_text
518 ));
519 }
520
521 debug!("POW challenge request successful, parsing response...");
523 let response_json: Value = match response.json().await {
524 Ok(json) => {
525 debug!("Successfully parsed POW challenge response JSON");
526 trace!("POW challenge response: {json:?}");
527 json
528 },
529 Err(e) => {
530 error!("Failed to parse POW challenge response as JSON: {e}");
531 return Err(anyhow!("Failed to parse POW response: {}", e));
532 }
533 };
534
535 match response_json.get("data").and_then(|d| d.get("biz_data")).and_then(|b| b.get("challenge")) {
537 Some(challenge) => {
538 let challenge_value = challenge.clone();
539 debug!("Received POW challenge: {challenge_value:?}");
540 match serde_json::from_value::<PowChallenge>(challenge_value) {
541 Ok(c) => Ok(c),
542 Err(e) => {
543 error!("Failed to parse POW challenge: {e}");
544 Err(anyhow!("Failed to parse challenge: {}", e))
545 }
546 }
547 },
548 None => {
549 error!("Invalid challenge response format. Full response: {response_json:?}");
550 Err(anyhow!("Invalid challenge response format from server"))
551 }
552 }
553 }
554
555 async fn get_fresh_pow_response(&mut self) -> Result<String> {
557 debug!("Requesting new POW challenge");
558 let challenge = self.get_pow_challenge().await?;
559
560 debug!("Solving POW challenge with difficulty: {}", challenge.difficulty);
561 let pow_response = self.pow_solver.solve_challenge(&challenge)?;
562
563
564 Ok(pow_response)
565 }
566
567 pub async fn create_chat_session(&mut self) -> Result<String> {
569 debug!("Creating new chat session");
570 let response: Value = self.make_request(
571 "POST",
572 "/chat_session/create",
573 json!({"character_id": null}),
574 false
575 ).await?;
576
577 debug!("Chat session creation response: {response:?}");
578
579 match &response.get("data").and_then(|d| d.get("biz_data")).and_then(|b| b.get("id")) {
581 Some(Value::String(id)) => {
582 info!("Created chat session with ID: {id}");
583 Ok(id.clone())
584 },
585 _ => {
586 error!("Invalid session creation response format. Response: {response:?}");
587 Err(anyhow!("Invalid session creation response format from server"))
588 }
589 }
590 }
591
592 pub async fn chat_completion(
594 &mut self,
595 chat_session_id: &str,
596 prompt: &str,
597 parent_message_id: Option<&str>,
598 thinking_mode: ThinkingMode,
599 search_mode: SearchMode,
600 system_prompt: Option<&str>,
601 ) -> Result<String> {
602 info!("Starting chat completion for session: {chat_session_id}");
603 debug!("Prompt length: {} chars", prompt.len());
604 trace!("Parent message ID: {parent_message_id:?}");
605 trace!("Thinking mode enabled: {}, Search mode enabled: {}", thinking_mode.as_bool(), search_mode.as_bool());
606
607 if prompt.is_empty() {
608 return Err(anyhow!("Prompt must be a non-empty string"));
609 }
610
611 if chat_session_id.is_empty() {
612 return Err(anyhow!("Chat session ID must be a non-empty string"));
613 }
614
615 let mut full_prompt = String::new();
617
618 if self.message_counter == 0 {
620 if let Some(sys_prompt) = system_prompt {
621 full_prompt.push_str("System: ");
622 full_prompt.push_str(sys_prompt);
623 full_prompt.push_str("\n\n");
624 }
625 }
626
627 full_prompt.push_str("User: ");
631 full_prompt.push_str(prompt);
632 self.message_history.push(ChatMessage {
633 role: "user".to_string(),
634 content: prompt.to_string(),
635 });
636
637 let parent_id = if self.message_counter == 0 { None } else { Some(format!("{}", self.message_counter * 2)) };
639 self.message_counter += 1;
640
641 let json_data = json!({
642 "chat_session_id": chat_session_id,
643 "parent_message_id": parent_id,
644 "prompt": full_prompt,
645 "ref_file_ids": [],
646 "thinking_enabled": thinking_mode.as_bool(),
647 "search_enabled": search_mode.as_bool(),
648 });
649
650 debug!("Chat completion request data prepared");
651 trace!("Request JSON: {json_data}");
652
653 let pow_response = self.get_fresh_pow_response().await?;
655
656 debug!("Preparing request headers with verification...");
658 let mut headers = self.default_headers(Some(&pow_response));
659
660 debug!("Processing session cookies...");
662 let cookies_map = match &self.session.cookies {
663 Value::Object(obj) => {
664 debug!("Found {} cookie entries in session", obj.len());
665 obj
666 },
667 _ => {
668 error!("Invalid cookies format in session - not an object");
669 return Err(anyhow!("Invalid cookies format in session"));
670 }
671 };
672
673 let mut cookie_str = String::new();
674 let mut cookie_count = 0;
675 for (key, value) in cookies_map {
676 if let Value::String(val) = value {
677 if !cookie_str.is_empty() {
678 cookie_str.push_str("; ");
679 }
680 cookie_str.push_str(&format!("{key}={val}"));
681 cookie_count += 1;
682 }
683 }
684
685 if !cookie_str.is_empty() {
686 headers.insert("Cookie", HeaderValue::from_str(&cookie_str)?);
687 debug!("Added {} cookies to request headers ({} chars total)", cookie_count, cookie_str.len());
688 } else {
689 warn!("No valid cookies found for chat completion request");
690 }
691
692 debug!("About to send HTTP POST request to chat completion endpoint...");
694 let url = format!("{BASE_URL}/chat/completion");
695 debug!("Target URL: {url}");
696 debug!("Request headers count: {}", headers.len());
697 let response = match self.http
698 .post(&url)
699 .headers(headers)
700 .json(&json_data)
701 .send()
702 .await
703 {
704 Ok(resp) => {
705 debug!("HTTP request completed successfully");
706 debug!("Response status: {}", resp.status());
707 debug!("Response headers count: {}", resp.headers().len());
708
709 for (name, value) in resp.headers() {
711 debug!("Response header: {name}: {value:?}");
712 }
713
714 if !resp.status().is_success() {
715 let status = resp.status();
716 warn!("HTTP request failed with status: {status}");
717 let error_text = match resp.text().await {
718 Ok(text) => {
719 debug!("Error response body length: {}", text.len());
720 trace!("Error response body: {text}");
721 text
722 },
723 Err(e) => {
724 error!("Failed to read error response body: {e}");
725 format!("Failed to read response: {e}")
726 }
727 };
728
729 error!("Chat completion failed: {status} - {error_text}");
730 match status.as_u16() {
731 401 => return Err(anyhow!("Authentication error: Invalid or expired token. Please check your auth_token file.")),
732 403 => return Err(anyhow!("Forbidden: Invalid cookies or IP blocked. Please refresh your cookies.")),
733 429 => return Err(anyhow!("Rate limit exceeded. Please wait and try again.")),
734 _ => return Err(anyhow!("API request failed: HTTP {} - {}", status, error_text)),
735 }
736 }
737
738 debug!("HTTP response is successful, proceeding to process stream...");
739 resp
740 },
741 Err(e) => {
742 error!("Network error during HTTP request: {e}");
743 error!("Error type: {e:?}");
744 return Err(anyhow!("Network error occurred during streaming: {}", e));
745 }
746 };
747
748 debug!("Processing SSE stream...");
750 let mut result = String::new();
751 let mut stream = response.bytes_stream();
752 let mut buffer = String::new();
753 let mut finished = false;
754 let mut chunk_count = 0;
755
756 while let Some(chunk_result) = stream.next().await {
757 let chunk_bytes = match chunk_result {
758 Ok(c) => c,
759 Err(e) => {
760 error!("Error reading stream chunk: {e}");
761 break;
762 }
763 };
764
765 chunk_count += 1;
766 let chunk_str = String::from_utf8_lossy(&chunk_bytes);
767 buffer.push_str(&chunk_str);
768
769 trace!("Received chunk #{}, buffer length: {}", chunk_count, buffer.len());
770
771 while let Some(newline_pos) = buffer.find('\n') {
773 let line = buffer[..newline_pos].to_string();
774 buffer = buffer[newline_pos + 1..].to_string();
775 let line = line.trim();
776
777 if line.is_empty() {
778 continue;
779 }
780
781 trace!("SSE line: {line}");
782
783 if let Some(event_str) = line.strip_prefix("event: ") {
785 match event_str {
786 "finish" => {
787 debug!("Received finish event, stream completed");
788 finished = true;
789 break;
790 }
791 "close" => {
792 debug!("Received close event");
793 finished = true;
794 break;
795 }
796 _ => {
797 trace!("SSE event: {event_str}");
798 }
799 }
800 continue;
801 }
802
803 if let Some(data_str) = line.strip_prefix("data: ") {
804 if data_str == "[DONE]" {
805 debug!("Received stream completion marker [DONE]");
806 finished = true;
807 break;
808 }
809
810 match serde_json::from_str::<Value>(data_str) {
811 Ok(data) => {
812 debug!("Parsed SSE data: {data:?}");
813
814 if let Some(value) = data.get("v") {
816 let mut content_added = false;
817
818 if let Some(path) = data.get("p").and_then(|p| p.as_str()) {
820 if let Some(operation) = data.get("o").and_then(|o| o.as_str()) {
821 if path == "response/content" && operation == "APPEND" {
822 if let Some(text) = value.as_str() {
823 result.push_str(text);
824 trace!("Appended response content: {text}");
825 content_added = true;
826 }
827 }
828 }
829 }
830
831 if !content_added {
834 if data.get("p").is_none() && data.get("o").is_none() {
836 if let Some(text) = value.as_str() {
837 result.push_str(text);
838 trace!("Appended thinking content: {text}");
839 content_added = true;
840 }
841 }
842 }
843
844 if !content_added {
846 if let Some(content) = extract_content_from_response(&data) {
847 result.push_str(&content);
848 trace!("Added fallback content: {content}");
849 }
850 }
851 }
852
853 if let Some(status) = data.get("status") {
855 if status.as_str() == Some("completed") || status.as_str() == Some("FINISHED") {
856 debug!("Stream finished with completed status");
857 finished = true;
858 break;
859 }
860 }
861 },
862 Err(e) => {
863 warn!("Error parsing SSE JSON: {e} - Data: {data_str}");
864 }
865 }
866 }
867 }
868
869 if finished {
870 break;
871 }
872 }
873
874 info!("Chat completion finished, processed {} chunks, result length: {}", chunk_count, result.len());
875 if result.is_empty() {
876 warn!("Empty result from SSE stream, buffer content: {:?}", buffer);
877 }
878
879 if !result.is_empty() {
881 self.message_history.push(ChatMessage {
882 role: "assistant".to_string(),
883 content: result.clone(),
884 });
885 debug!("Added assistant response to message history");
886 }
887
888 Ok(result)
889 }
890
891 pub async fn get_chat_history(&mut self, chat_session_id: &str) -> Result<Value> {
893 debug!("Getting chat history for session: {chat_session_id}");
894
895 let url = format!("{BASE_URL}/chat/history_messages?chat_session_id={chat_session_id}&cache_version=8");
896 debug!("History URL: {url}");
897
898 let response: Value = self.make_request(
899 "GET",
900 &format!("/chat/history_messages?chat_session_id={chat_session_id}&cache_version=8"),
901 json!(null),
902 false
903 ).await?;
904
905 debug!("Chat history response received");
906 Ok(response)
907 }
908}
909
910fn extract_content_from_response(data: &Value) -> Option<String> {
912 if let Some(content) = data.get("content") {
914 if let Some(text) = content.as_str() {
915 return Some(text.to_string());
916 }
917 }
918
919 if let Some(response) = data.get("v").and_then(|v| v.get("response")) {
921 if let Some(content) = response.get("content") {
922 if let Some(text) = content.as_str() {
923 return Some(text.to_string());
924 }
925 }
926 }
927
928 if let Some(choices) = data.get("choices").and_then(|c| c.as_array()) {
930 if let Some(choice) = choices.first() {
931 if let Some(delta) = choice.get("delta") {
932 if let Some(content) = delta.get("content") {
933 if let Some(text) = content.as_str() {
934 return Some(text.to_string());
935 }
936 }
937 }
938 }
939 }
940
941 None
942}
943
944pub fn load_cookies(cookie_file: &Path) -> Result<serde_json::Value> {
946 debug!("Loading cookies from file: {cookie_file:?}");
947
948 if !cookie_file.exists() {
949 return Err(anyhow!("Cookie file does not exist at {:?}", cookie_file));
950 }
951
952 let cookie_data = fs::read_to_string(cookie_file)?;
953 let cookies: Value = serde_json::from_str(&cookie_data)?;
954
955 debug!("Successfully loaded cookies file, {} bytes", cookie_data.len());
956 Ok(cookies.get("cookies").cloned().unwrap_or(json!({})))
957}
958
959pub fn get_config_help(file_name: &str) -> String {
961 match file_name {
962 "deepseek_auth_token" => "To get your DeepSeek auth token:
9631. Go to chat.deepseek.com in your browser
9642. Open Developer Tools (F12 or right-click and select 'Inspect')
9653. Go to the Network tab
9664. Refresh the page or make a request
9675. Look for requests to the DeepSeek API
9686. In the 'Headers' tab, find 'Request Headers'
9697. Look for the 'Authorization' header with format 'Bearer {token}'
9708. Copy the token part (without 'Bearer ') and save it to this folder with filename: deepseek_auth_token".to_string(),
971
972 "deepseek_cookies" => "The DeepSeek API requires Cloudflare cookies.
973Please run the Python bypass script from deepseek4free to generate these cookies:
9741. Run the bypass.py script from deepseek4free/dsk/
9752. Copy the resulting cookies.json file to this folder with filename: deepseek_cookies".to_string(),
976
977 _ => format!("Configuration file {file_name} is missing."),
978 }
979}