├── Readme.md ├── assets ├── account.png ├── challenge_pow.png ├── file_status.png ├── file_upload.png ├── first.png ├── leetcode.png ├── leetcode2.png ├── output-1.png ├── output-2.png ├── output-3.png ├── second.png └── third.png └── deepseek.py /Readme.md: -------------------------------------------------------------------------------- 1 |
An unofficial free deepseek api wrapper written in python.
2 | 3 | A totally free deepseek api(with latest R1 model) written in python. 4 | 5 | # Required Cookies 6 | Create an account in [deepseek.com](https://www.deepseek.com/) and visit [chat.deepseek.com](https://chat.deepseek.com/) and copy the following cookies 7 | ```sh 8 | smidV2 9 | HMACCOUNT 10 | Hm_lpvt_fb5acee01d9182aabb2b61eb816d24ff 11 | intercom-session-guh50jw4 12 | HWWAFSESTIME 13 | Hm_lvt_fb5acee01d9182aabb2b61eb816d24ff 14 | HWWAFSESID 15 | .thumbcache_6b2e5483f9d858d7c661c5e276b6a6ae 16 | Hm_lvt_1fff341d7a963a4043e858ef0e19a17c 17 | intercom-device-id-guh50jw4 18 | __cf_bm 19 | ds_session_id 20 | Hm_lpvt_1fff341d7a963a4043e858ef0e19a17c 21 | ``` 22 | and from **Local Storage** collect ``userToken`` value part 23 | 24 | # http headers 25 | ```sh 26 | headers={ 27 | 28 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0", 29 | "Accept": "*/*", 30 | "Accept-Language": "en-US,en;q=0.5", 31 | "Accept-Encoding": "gzip, deflate, br", 32 | "X-Client-Platform": "web", 33 | "X-Client-Version": "1.0.0-always", 34 | "X-Client-Locale": "en_US", 35 | "X-App-Version": "20241129.1", 36 | "Authorization": "Bearer {userToken}", #collected from local storage 37 | "Origin": "https://chat.deepseek.com", 38 | "Referer": "https://chat.deepseek.com/", #change to 'https://chat.deepseek.com/a/chat/s/{chatroom_id}' after chatroom creation 39 | "Sec-Fetch-Dest": "empty", 40 | "Sec-Fetch-Mode": "cors", 41 | "Sec-Fetch-Site": "same-origin", 42 | "Priority": "u=0", 43 | "Te": "trailers", 44 | } 45 | ``` 46 | 47 | # Install packages 48 | ```sh 49 | pip install -r requirements.txt 50 | ``` 51 | 52 | # Usage 53 | You can usage the terimnal wrapper this way 54 | ```py 55 | from deepseek import DeepSeek 56 | import json 57 | from rich.console import Console 58 | from rich.markdown import Markdown 59 | console = Console() 60 | 61 | 62 | with open("cookies.json", "r") as f: 63 | cookies = json.load(f) 64 | f.close() 65 | 66 | usertoken = "....." #collect from locla storage 67 | 68 | deepseek_engiene:DeepSeek = DeepSeek(cookie=cookies,user_token=usertoken) 69 | ``` 70 | ### Create a chatroom 71 | ```py 72 | chatroom_id = deepseek_engiene.create_chatroom() 73 | 74 | #you can visit the chatroom https://chat.deepseek.com/a/chat/s/{chtroom_id} 75 | ``` 76 | ### Ai Response method blueprint 77 | ```py 78 | def ai_response(self,prompt:str,file_list:list=None,r1_enabled:bool=False,web_search:bool=False)->str: 79 | 80 | """ 81 | Get response from the ai 82 | :param prompt: The prompt to send to the ai 83 | :param file_list: List of files to send to the ai 84 | :param r1_enabled: R1 enabled 85 | :param web_search: Web search enabled 86 | :return: The response from the ai 87 | """ 88 | ``` 89 | ### Normal Ai Response 90 | ```py 91 | markdown = Markdown(deepseek_engiene.ai_response(prompt=prompt)) 92 | console.print(markdown) 93 | ``` 94 | ### Ai hallucination (R1-enabled) 95 | ```py 96 | markdown = Markdown(deepseek_engiene.ai_response(prompt=prompt,r1_enabled=True)) 97 | console.print(markdown) 98 | ``` 99 | ### Web search Enable 100 | ```py 101 | markdown = Markdown(deepseek_engiene.ai_response(prompt=prompt,web_search=True)) 102 | console.print(markdown) 103 | ``` 104 | ### File upload 105 | ```py 106 | markdown = Markdown(deepseek_engiene.ai_response(file_list=["assets/leetcode.png","assets/leetcode2.png"],prompt="solve the problems in python and complete the function")) 107 | ``` 108 | 109 | # Example output 110 | In the following example deepseek solves two leetcode question from screenshots 111 | ```py 112 | markdown = Markdown(deepseek_engiene.ai_response(file_list=["assets/leetcode.png","assets/leetcode2.png"],prompt="solve the problems in python and complete the function")) 113 | ``` 114 |  115 |  116 |  117 | 118 | 119 | # CodeFlow (sorta) 120 | 1. Creating a chatroom 121 |  122 | 123 | 2. Util Function ``create_pow_challenge`` 124 |  125 | 126 | 3. Ai responses\ 127 | The api endpoint for this is ``/api/v0/chat/completion`` 128 | 129 | **First Message** 130 |  131 | 132 | **Second Message** 133 |  134 | 135 | **Third message and onwards** 136 |  137 | 138 | **File upload** 139 |  140 | 141 | **Get file upload status** 142 |  143 | 144 | **Use the File id**\ 145 | Use the ``file_id`` in the ``ref_file_ids`` array in the post request 146 | 147 | # Todo 148 | ~~1. Scrape for file upload~~\ 149 | ~~2. Code implementation in python.~~\ 150 | 3. chat history implementation\ 151 | 4. Start messaging in an already existing chatroom\ 152 | 5. Better error handling 153 | 154 | # Note 155 | **The cloudflare __cf_bm cookie expires every thirty minutes. So if the requests aren't working then refresh the cookies** -------------------------------------------------------------------------------- /assets/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/account.png -------------------------------------------------------------------------------- /assets/challenge_pow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/challenge_pow.png -------------------------------------------------------------------------------- /assets/file_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/file_status.png -------------------------------------------------------------------------------- /assets/file_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/file_upload.png -------------------------------------------------------------------------------- /assets/first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/first.png -------------------------------------------------------------------------------- /assets/leetcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/leetcode.png -------------------------------------------------------------------------------- /assets/leetcode2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/leetcode2.png -------------------------------------------------------------------------------- /assets/output-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/output-1.png -------------------------------------------------------------------------------- /assets/output-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/output-2.png -------------------------------------------------------------------------------- /assets/output-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/output-3.png -------------------------------------------------------------------------------- /assets/second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/second.png -------------------------------------------------------------------------------- /assets/third.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alpha-hexor/deepseek-r1-api/aa107aaa71526c68d244e41431f484c04bc1ec56/assets/third.png -------------------------------------------------------------------------------- /deepseek.py: -------------------------------------------------------------------------------- 1 | import cloudscraper 2 | from base64 import b64encode 3 | import json 4 | import random 5 | from datetime import datetime 6 | import re 7 | import time 8 | from os import path 9 | import mimetypes 10 | from functools import wraps 11 | from rich.console import Console 12 | from rich.spinner import Spinner 13 | from rich.live import Live 14 | 15 | console = Console() 16 | 17 | def function_timer(func): 18 | @wraps(func) 19 | def wrapper(*args, **kwargs): 20 | spinner = Spinner("dots", text=f"[blue]Thinking...[/blue]") 21 | start_time = time.time() 22 | 23 | with Live(spinner, console=console, refresh_per_second=10): 24 | result = func(*args, **kwargs) 25 | 26 | end_time = time.time() 27 | execution_time = end_time - start_time 28 | 29 | console.print(f"[bold green]Answer got in {execution_time:.4f} seconds.[/bold green]") 30 | return result 31 | return wrapper 32 | 33 | class DeepSeek: 34 | def __init__(self,cookie:dict,user_token:str)->None: 35 | """ 36 | initialize the class 37 | """ 38 | self.main_url:str = "https://chat.deepseek.com" 39 | 40 | self.scraper = cloudscraper.create_scraper() 41 | 42 | self.cookie:dict = cookie 43 | 44 | self.headers:dict = { 45 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:134.0) Gecko/20100101 Firefox/134.0", 46 | "Accept": "*/*", 47 | "Accept-Language": "en-US,en;q=0.5", 48 | "Accept-Encoding": "gzip, deflate, br", 49 | "X-Client-Platform": "web", 50 | "X-Client-Version": "1.0.0-always", 51 | "X-Client-Locale": "en_US", 52 | "X-App-Version": "20241129.1", 53 | "Authorization": f"Bearer {user_token}", 54 | "Origin": self.main_url, 55 | "Referer": f"{self.main_url}/", 56 | "Sec-Fetch-Dest": "empty", 57 | "Sec-Fetch-Mode": "cors", 58 | "Sec-Fetch-Site": "same-origin", 59 | "Priority": "u=0", 60 | } 61 | 62 | self.chtroom_id:str = "" 63 | self.message_id = 1 64 | 65 | 66 | def create_chatroom(self) -> str: 67 | """ 68 | Creates a new chatroom 69 | 70 | :return: chatroom id 71 | """ 72 | json_data = { 73 | 'character_id': None, 74 | } 75 | max_retries = 3 76 | for _ in range(max_retries): 77 | try: 78 | r = self.scraper.post( 79 | f"{self.main_url}/api/v0/chat_session/create", 80 | headers=self.headers, 81 | cookies=self.cookie, 82 | json=json_data 83 | ) 84 | if r.status_code == 403: 85 | time.sleep(6) 86 | continue 87 | self.chtroom_id = r.json()["data"]["biz_data"]["id"] 88 | self.headers['Referer'] = f'{self.main_url}/a/chat/s/{self.chtroom_id}' 89 | return self.chtroom_id 90 | except Exception as e: 91 | print(r.text) 92 | raise Exception("[X]Chatroom creation failed with error:", e) 93 | raise Exception("[X]Chatroom creation failed after multiple attempts. Maybe change the cookies") 94 | 95 | def create_pow_challenge(self,file_upload:bool=False)->str: 96 | """ 97 | Creates a pow challenge 98 | 99 | :return: pow challenge data 100 | """ 101 | pow_data_structure:dict={ 102 | 103 | "algorithm":"DeepSeekHashV1", 104 | "challenge":"", 105 | "salt":"", 106 | "answer":random.randint(600000,700000), 107 | "signature":"", 108 | "target_path":"" 109 | } 110 | 111 | if file_upload: 112 | 113 | json_data:dict = { 114 | 'target_path': '/api/v0/file/upload_file' 115 | } 116 | pow_data_structure["target_path"] = "/api/v0/file/upload_file" 117 | else: 118 | json_data:dict = { 119 | 'target_path': '/api/v0/chat/completion' 120 | } 121 | pow_data_structure["target_path"] = "/api/v0/chat/completion" 122 | 123 | try: 124 | r = self.scraper.post( 125 | f'{self.main_url}/api/v0/chat/create_pow_challenge', 126 | cookies=self.cookie, 127 | headers=self.headers, 128 | json=json_data 129 | ) 130 | 131 | if r.status_code == 403: 132 | console.print("[red][X]Retrying pow_challenge...[/red]") 133 | time.sleep(7) 134 | #change Hm_lpvt_1fff341d7a963a4043e858ef0e19a17c cookie 135 | self.cookie["Hm_lpvt_1fff341d7a963a4043e858ef0e19a17c"] = str(int(datetime.now().timestamp())) 136 | 137 | r = self.scraper.post( 138 | f'{self.main_url}/api/v0/chat/create_pow_challenge', 139 | cookies=self.cookie, 140 | headers=self.headers, 141 | json=json_data) 142 | 143 | pow_data_structure["challenge"] = r.json()["data"]["biz_data"]["challenge"]["challenge"] 144 | pow_data_structure["salt"] = r.json()["data"]["biz_data"]["challenge"]["salt"] 145 | pow_data_structure["signature"] = r.json()["data"]["biz_data"]["challenge"]["signature"] 146 | 147 | b64_encoded = b64encode(json.dumps(pow_data_structure).encode()).decode() 148 | 149 | return b64_encoded 150 | except Exception as e: 151 | raise Exception("[X]Pow challenge creation failed with error:",e) 152 | 153 | @function_timer 154 | def ai_response(self,prompt:str,file_list:list=None,r1_enabled:bool=False,web_search:bool=False)->str: 155 | """ 156 | Get response from the ai 157 | 158 | :param prompt: The prompt to send to the ai 159 | :param file_list: List of files to send to the ai 160 | :param r1_enabled: R1 enabled 161 | :param web_search: Web search enabled 162 | :return: The response from the ai 163 | """ 164 | json_data = {} 165 | 166 | if file_list: 167 | #upload the files 168 | #get file ids 169 | #create a new json data 170 | file_ids = [] 171 | for f in file_list: 172 | #chek if file exists or not 173 | if not path.exists(f): 174 | raise Exception("[X]File does not exist:",f) 175 | 176 | file_name = path.basename(f) 177 | mime_type = mimetypes.guess_type(f)[0] 178 | with open(f, "rb") as file: 179 | files = { 180 | "file" : ( 181 | file_name, 182 | file, 183 | mime_type 184 | ) 185 | } 186 | 187 | #get the pow challenge 188 | self.headers["X-Ds-Pow-Response"] = self.create_pow_challenge(file_upload=True) 189 | 190 | #send the request 191 | try: 192 | r = self.scraper.post( 193 | f'{self.main_url}/api/v0/file/upload_file', 194 | headers=self.headers, 195 | cookies=self.cookie, 196 | files=files 197 | ) 198 | except: 199 | print("[X]File upload failed") 200 | pass 201 | 202 | #get the file id 203 | file_ids.append(r.json()["data"]["biz_data"]["id"]) 204 | file.close() 205 | 206 | #check for each file id 207 | print("Sleeping for 10 seconds for file upload process") 208 | time.sleep(10) 209 | 210 | all_files_uploaded = False 211 | while not all_files_uploaded: 212 | all_files_uploaded = True 213 | for file_id in file_ids: 214 | 215 | #removing the pow response header 216 | temp_headers = self.headers.copy() 217 | temp_headers.pop("X-Ds-Pow-Response", None) 218 | 219 | r = self.scraper.get( 220 | f'{self.main_url}/api/v0/file/fetch_files?file_ids={file_id}', 221 | cookies=self.cookie, 222 | headers=temp_headers, 223 | ) 224 | 225 | if r.json()["data"]["biz_data"]["files"][0]["status"] != "SUCCESS": 226 | all_files_uploaded = False 227 | print("[*]Still waiting for file upload") 228 | time.sleep(2) 229 | break 230 | elif r.json()["data"]["biz_data"]["files"][0]["status"] == "SUCCESS": 231 | print(f"[*]{file_id} uploaded successfully") 232 | 233 | json_data = { 234 | 'chat_session_id': self.chtroom_id, 235 | 'prompt': prompt, 236 | 'ref_file_ids': file_ids, 237 | 'thinking_enabled': r1_enabled, 238 | 'search_enabled': False, #can't use web search with file attachment 239 | } 240 | 241 | 242 | else: 243 | #for normal prompt 244 | json_data = { 245 | 'chat_session_id': self.chtroom_id, 246 | 'prompt': prompt, 247 | 'ref_file_ids': [], 248 | 'thinking_enabled': r1_enabled, 249 | 'search_enabled': web_search, 250 | } 251 | 252 | #calculate message id 253 | if self.message_id == 1: 254 | json_data["parent_message_id"] = None 255 | self.message_id += 1 256 | else: 257 | json_data["parent_message_id"] = self.message_id 258 | self.message_id += 3 259 | 260 | try: 261 | #get pow challenge data 262 | pow_challenge = self.create_pow_challenge() 263 | self.headers["X-Ds-Pow-Response"] = pow_challenge 264 | 265 | #send the request 266 | r = self.scraper.post( 267 | f'{self.main_url}/api/v0/chat/completion', 268 | cookies=self.cookie, 269 | headers=self.headers, 270 | json=json_data, 271 | ) 272 | 273 | #cleanup the text 274 | response = r.text.replace('\\"', "'") 275 | data = "".join(re.findall(r'"content":"(.*?)"', response)) 276 | data = data.replace(r'\n', '\n') 277 | 278 | return data 279 | except Exception as e: 280 | raise Exception("[X]AI response failed with error:",e) 281 | --------------------------------------------------------------------------------