toast_api/
deepseek.rs

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
17// Constants
18const BASE_URL: &str = "https://chat.deepseek.com/api/v0";
19
20// Define the types for thinking and search modes
21#[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// Session data for DeepSeek API
47#[derive(Clone)]
48pub struct Session {
49    pub auth_token: String,
50    pub cookies: serde_json::Value,
51}
52
53// Challenge from DeepSeek for proof-of-work
54#[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
65// Proof-of-Work solver using WebAssembly with fallback
66pub 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        // Use the sha3 module to solve the challenge
106        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        // Try WASM first
128        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
153/// DeepSeek API client
154pub 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    /// Create a new DeepSeek client
172    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    /// Refresh cookies by running the bypass.py script
297    async fn refresh_cookies(&mut self) -> Result<()> {
298        warn!("Attempting to refresh cookies");
299        // Only attempt to refresh once per session to avoid excessive attempts
300        if self.cookies_refreshed {
301            return Err(anyhow!("Already attempted to refresh cookies once this session"));
302        }
303        
304        // This is where we'd typically run bypass.py
305        // For now we just mark as refreshed to avoid repeated attempts
306        self.cookies_refreshed = true;
307        
308        // In a full implementation, we would start the Python server and fetch the cookies
309        // For now, return an error suggesting manual cookie retrieval
310        Err(anyhow!(
311            "Cookie refresh needed. Please run the Python bypass script manually and update the cookie file"
312        ))
313    }
314    
315    /// Make a request to the DeepSeek API
316    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            // Add cookies from session
348            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                    // Add human-like delay between requests to avoid detection
371        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            // Check for Cloudflare protection
393            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(); // Try to refresh, ignore errors
403                    retry_count += 1;
404                    continue;
405                } else {
406                    return Err(anyhow!("Request verification failed"));
407                }
408            }
409            
410            // Handle response codes
411            match status.as_u16() {
412                200 => {
413                    debug!("Successful response received, parsing JSON");
414                    // Parse JSON response
415                    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    /// Get a proof-of-work challenge from the API
449    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        // Add cookies
461        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        // Make the request
492        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        // Parse the response
522        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        // Extract the challenge from the response
536        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    /// Get a fresh POW response
556    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    /// Create a new chat session
568    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        // Extract the session ID from the response
580        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    /// Send a message and get a streaming response
593    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        // Build the full prompt including conversation history and system prompt
616        let mut full_prompt = String::new();
617        
618        // Add system prompt if provided
619        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        // Add conversation history
628        
629        // Add current user message
630        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        // Set parent_message_id: first request is null, then increment from 2
638        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        // Get POW response (use cached if available, or get fresh one)
654        let pow_response = self.get_fresh_pow_response().await?;
655        
656        // Prepare headers with POW response
657        debug!("Preparing request headers with verification...");
658        let mut headers = self.default_headers(Some(&pow_response));
659        
660        // Add cookies
661        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        // Make streaming request
693        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                // Log all response headers for debugging
710                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        // Process the SSE stream properly
749        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            // Process complete lines from the buffer
772            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                // Handle different SSE event types
784                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                            // Handle DeepSeek's streaming format
815                            if let Some(value) = data.get("v") {
816                                let mut content_added = false;
817                                
818                                // Check for the specific streaming format with path/operation first
819                                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 no content was added via streaming format, check if this is thinking content
832                                // Thinking content comes as just {"v": "text"} without path/operation
833                                if !content_added {
834                                    // Check if this is thinking content (no path/operation fields)
835                                    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 still no content was added, try fallback extraction
845                                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                            // Check for completion signals
854                            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        // Add assistant response to message history for context retention
880        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    /// Get conversation history for a chat session
892    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
910// Extract content from DeepSeek response format
911fn extract_content_from_response(data: &Value) -> Option<String> {
912    // Try direct content field
913    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    // Try v.response.content format
920    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    // Try OpenAI format as fallback
929    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
944// Helper function to load cookies from file
945pub 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
959// Helper function to extract configuration help text
960pub 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}