Spaces:
Runtime error
Runtime error
| """ | |
| DeepSeek Web API Proxy Server (OpenAI-Compatible Gateway) | |
| Hugging Face Space Edition | |
| Version: 1.2.1 | |
| Description: Zero-dependency proxy mapping OpenAI completions to DeepSeek Web API | |
| """ | |
| import http.server | |
| import socketserver | |
| import json | |
| import urllib.request | |
| import urllib.error | |
| import sys | |
| import uuid | |
| import struct | |
| import binascii | |
| import base64 | |
| import time | |
| import threading | |
| import traceback | |
| import os | |
| from collections import defaultdict | |
| sys.stdout.reconfigure(encoding='utf-8') | |
| # --- CONFIGURATION --- | |
| PORT = int(os.environ.get("PORT", "7860")) | |
| HOST = "0.0.0.0" | |
| # Load token from env or file | |
| def _load_token(): | |
| tok = os.environ.get("DEEPSEEK_TOKEN", "").strip() | |
| if tok: | |
| return tok | |
| for path in ["token.txt", "/app/token.txt"]: | |
| try: | |
| with open(path, "r") as f: | |
| tok = f.read().strip() | |
| if tok: | |
| return tok | |
| except FileNotFoundError: | |
| pass | |
| return "" | |
| USER_TOKEN = _load_token() | |
| if not USER_TOKEN: | |
| print("WARNING: DEEPSEEK_TOKEN not set! Set it in HF Secrets.") | |
| print("Token format: oxMs/... (from browser at chat.deepseek.com)") | |
| # --- SESSION MANAGEMENT --- | |
| ACTIVE_SESSIONS = defaultdict(lambda: {"session_id": None, "message_history": []}) | |
| # Terminal Colors (disabled if no TTY) | |
| class Colors: | |
| if sys.stdout.isatty(): | |
| HEADER = '\033[95m'; BLUE = '\033[94m'; CYAN = '\033[96m'; GREEN = '\033[92m' | |
| WARNING = '\033[93m'; FAIL = '\033[91m'; ENDC = '\033[0m'; BOLD = '\033[1m' | |
| else: | |
| HEADER = BLUE = CYAN = GREEN = WARNING = FAIL = ENDC = BOLD = '' | |
| # --- HIGH PERFORMANCE DEEPSEEK POW SOLVER --- | |
| RC = [ | |
| 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, | |
| 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, | |
| 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A, | |
| 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003, | |
| 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A, | |
| 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, | |
| ] | |
| def rotl64(v, k): | |
| return ((v << k) | (v >> (64 - k))) & 0xffffffffffffffff | |
| def keccakF23(s): | |
| a0, a1, a2, a3, a4 = s[0], s[1], s[2], s[3], s[4] | |
| a5, a6, a7, a8, a9 = s[5], s[6], s[7], s[8], s[9] | |
| a10, a11, a12, a13, a14 = s[10], s[11], s[12], s[13], s[14] | |
| a15, a16, a17, a18, a19 = s[15], s[16], s[17], s[18], s[19] | |
| a20, a21, a22, a23, a24 = s[20], s[21], s[22], s[23], s[24] | |
| for r in range(1, 24): | |
| c0 = a0 ^ a5 ^ a10 ^ a15 ^ a20 | |
| c1 = a1 ^ a6 ^ a11 ^ a16 ^ a21 | |
| c2 = a2 ^ a7 ^ a12 ^ a17 ^ a22 | |
| c3 = a3 ^ a8 ^ a13 ^ a18 ^ a23 | |
| c4 = a4 ^ a9 ^ a14 ^ a19 ^ a24 | |
| d0 = c4 ^ rotl64(c1, 1) | |
| d1 = c0 ^ rotl64(c2, 1) | |
| d2 = c1 ^ rotl64(c3, 1) | |
| d3 = c2 ^ rotl64(c4, 1) | |
| d4 = c3 ^ rotl64(c0, 1) | |
| a0 ^= d0; a5 ^= d0; a10 ^= d0; a15 ^= d0; a20 ^= d0 | |
| a1 ^= d1; a6 ^= d1; a11 ^= d1; a16 ^= d1; a21 ^= d1 | |
| a2 ^= d2; a7 ^= d2; a12 ^= d2; a17 ^= d2; a22 ^= d2 | |
| a3 ^= d3; a8 ^= d3; a13 ^= d3; a18 ^= d3; a23 ^= d3 | |
| a4 ^= d4; a9 ^= d4; a14 ^= d4; a19 ^= d4; a24 ^= d4 | |
| b0 = a0 | |
| b10 = rotl64(a1, 1) | |
| b20 = rotl64(a2, 62) | |
| b5 = rotl64(a3, 28) | |
| b15 = rotl64(a4, 27) | |
| b16 = rotl64(a5, 36) | |
| b1 = rotl64(a6, 44) | |
| b11 = rotl64(a7, 6) | |
| b21 = rotl64(a8, 55) | |
| b6 = rotl64(a9, 20) | |
| b7 = rotl64(a10, 3) | |
| b17 = rotl64(a11, 10) | |
| b2 = rotl64(a12, 43) | |
| b12 = rotl64(a13, 25) | |
| b22 = rotl64(a14, 39) | |
| b23 = rotl64(a15, 41) | |
| b8 = rotl64(a16, 45) | |
| b18 = rotl64(a17, 15) | |
| b3 = rotl64(a18, 21) | |
| b13 = rotl64(a19, 8) | |
| b14 = rotl64(a20, 18) | |
| b24 = rotl64(a21, 2) | |
| b9 = rotl64(a22, 61) | |
| b19 = rotl64(a23, 56) | |
| b4 = rotl64(a24, 14) | |
| a0 = b0 ^ ((~b1) & b2) | |
| a1 = b1 ^ ((~b2) & b3) | |
| a2 = b2 ^ ((~b3) & b4) | |
| a3 = b3 ^ ((~b4) & b0) | |
| a4 = b4 ^ ((~b0) & b1) | |
| a5 = b5 ^ ((~b6) & b7) | |
| a6 = b6 ^ ((~b7) & b8) | |
| a7 = b7 ^ ((~b8) & b9) | |
| a8 = b8 ^ ((~b9) & b5) | |
| a9 = b9 ^ ((~b5) & b6) | |
| a10 = b10 ^ ((~b11) & b12) | |
| a11 = b11 ^ ((~b12) & b13) | |
| a12 = b12 ^ ((~b13) & b14) | |
| a13 = b13 ^ ((~b14) & b10) | |
| a14 = b14 ^ ((~b10) & b11) | |
| a15 = b15 ^ ((~b16) & b17) | |
| a16 = b16 ^ ((~b17) & b18) | |
| a17 = b17 ^ ((~b18) & b19) | |
| a18 = b18 ^ ((~b19) & b15) | |
| a19 = b19 ^ ((~b15) & b16) | |
| a20 = b20 ^ ((~b21) & b22) | |
| a21 = b21 ^ ((~b22) & b23) | |
| a22 = b22 ^ ((~b23) & b24) | |
| a23 = b23 ^ ((~b24) & b20) | |
| a24 = b24 ^ ((~b20) & b21) | |
| a0 &= 0xffffffffffffffff; a1 &= 0xffffffffffffffff; a2 &= 0xffffffffffffffff; a3 &= 0xffffffffffffffff; a4 &= 0xffffffffffffffff | |
| a5 &= 0xffffffffffffffff; a6 &= 0xffffffffffffffff; a7 &= 0xffffffffffffffff; a8 &= 0xffffffffffffffff; a9 &= 0xffffffffffffffff | |
| a10 &= 0xffffffffffffffff; a11 &= 0xffffffffffffffff; a12 &= 0xffffffffffffffff; a13 &= 0xffffffffffffffff; a14 &= 0xffffffffffffffff | |
| a15 &= 0xffffffffffffffff; a16 &= 0xffffffffffffffff; a17 &= 0xffffffffffffffff; a18 &= 0xffffffffffffffff; a19 &= 0xffffffffffffffff | |
| a20 &= 0xffffffffffffffff; a21 &= 0xffffffffffffffff; a22 &= 0xffffffffffffffff; a23 &= 0xffffffffffffffff; a24 &= 0xffffffffffffffff | |
| a0 ^= RC[r] | |
| s[0], s[1], s[2], s[3], s[4] = a0, a1, a2, a3, a4 | |
| s[5], s[6], s[7], s[8], s[9] = a5, a6, a7, a8, a9 | |
| s[10], s[11], s[12], s[13], s[14] = a10, a11, a12, a13, a14 | |
| s[15], s[16], s[17], s[18], s[19] = a15, a16, a17, a18, a19 | |
| s[20], s[21], s[22], s[23], s[24] = a20, a21, a22, a23, a24 | |
| def solve_pow(challenge_hex, salt, expire_at, difficulty): | |
| target = binascii.unhexlify(challenge_hex) | |
| t0, t1, t2, t3 = struct.unpack("<QQQQ", target) | |
| prefix = f"{salt}_{expire_at}_".encode('utf-8') | |
| rate = 136 | |
| # Pre-absorb the prefix state | |
| base_state = [0] * 25 | |
| off = 0 | |
| while off + rate <= len(prefix): | |
| for i in range(rate // 8): | |
| val = struct.unpack_from("<Q", prefix, off + i * 8)[0] | |
| base_state[i] ^= val | |
| keccakF23(base_state) | |
| off += rate | |
| tail_len = len(prefix) - off | |
| tail = bytearray(rate) | |
| tail[:tail_len] = prefix[off:] | |
| # Iterate through possible nonces | |
| for n in range(difficulty): | |
| num_str = str(n).encode('utf-8') | |
| num_len = len(num_str) | |
| s = list(base_state) | |
| total_tail = tail_len + num_len | |
| if total_tail < rate: | |
| buf = bytearray(rate) | |
| buf[:tail_len] = tail[:tail_len] | |
| buf[tail_len:total_tail] = num_str | |
| buf[total_tail] = 0x06 | |
| buf[rate - 1] |= 0x80 | |
| for i in range(rate // 8): | |
| s[i] ^= struct.unpack_from("<Q", buf, i * 8)[0] | |
| keccakF23(s) | |
| else: | |
| buf = bytearray(rate) | |
| buf[:tail_len] = tail[:tail_len] | |
| buf[tail_len:rate] = num_str[:rate - tail_len] | |
| for i in range(rate // 8): | |
| s[i] ^= struct.unpack_from("<Q", buf, i * 8)[0] | |
| keccakF23(s) | |
| buf2 = bytearray(rate) | |
| rem = total_tail - rate | |
| buf2[:rem] = num_str[rate - tail_len:] | |
| buf2[rem] = 0x06 | |
| buf2[rate - 1] |= 0x80 | |
| for i in range(rate // 8): | |
| s[i] ^= struct.unpack_from("<Q", buf2, i * 8)[0] | |
| keccakF23(s) | |
| if s[0] == t0 and s[1] == t1 and s[2] == t2 and s[3] == t3: | |
| return n | |
| raise ValueError("PoW: No solution found within difficulty range.") | |
| # --- DEEPSEEK WEB API DRIVER --- | |
| def create_chat_session(): | |
| url = "https://chat.deepseek.com/api/v0/chat_session/create" | |
| headers = { | |
| "Authorization": f"Bearer {USER_TOKEN}", | |
| "Content-Type": "application/json", | |
| "Accept": "application/json", | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "Referer": "https://chat.deepseek.com/", | |
| "Origin": "https://chat.deepseek.com", | |
| "x-client-platform": "web", | |
| "x-app-version": "20241129.1" | |
| } | |
| payload = json.dumps({"agent": "chat"}).encode('utf-8') | |
| req = urllib.request.Request(url, data=payload, headers=headers, method="POST") | |
| with urllib.request.urlopen(req, timeout=15) as response: | |
| res_json = json.loads(response.read().decode('utf-8')) | |
| if res_json.get("code") != 0: | |
| raise ValueError(f"Failed to create session: {res_json.get('msg')}") | |
| biz_data = res_json.get("data", {}).get("biz_data", {}) | |
| session_id = biz_data.get("id") or biz_data.get("chat_session", {}).get("id") | |
| if not session_id: | |
| raise ValueError("Failed to extract session id") | |
| return session_id | |
| POW_CACHE = {"header": None, "expires": 0} | |
| POW_CACHE_LOCK = threading.Lock() | |
| def get_pow_header(target_path="/api/v0/chat/completion"): | |
| with POW_CACHE_LOCK: | |
| if POW_CACHE["header"] and time.time() < POW_CACHE["expires"]: | |
| print(f"{Colors.CYAN}β‘ Using cached PoW{Colors.ENDC}") | |
| return POW_CACHE["header"] | |
| url = "https://chat.deepseek.com/api/v0/chat/create_pow_challenge" | |
| headers = { | |
| "Authorization": f"Bearer {USER_TOKEN}", | |
| "Content-Type": "application/json", | |
| "Accept": "application/json", | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "Referer": "https://chat.deepseek.com/", | |
| "Origin": "https://chat.deepseek.com", | |
| "x-client-platform": "web", | |
| "x-app-version": "20241129.1" | |
| } | |
| payload = json.dumps({"target_path": target_path}).encode('utf-8') | |
| req = urllib.request.Request(url, data=payload, headers=headers, method="POST") | |
| with urllib.request.urlopen(req, timeout=15) as response: | |
| res_json = json.loads(response.read().decode('utf-8')) | |
| if res_json.get("code") != 0: | |
| raise ValueError(f"Failed to fetch challenge: {res_json.get('msg')}") | |
| challenge_data = res_json.get("data", {}).get("biz_data", {}).get("challenge", {}) | |
| algo = challenge_data.get("algorithm") | |
| ch_hex = challenge_data.get("challenge") | |
| salt = challenge_data.get("salt") | |
| expire_at = challenge_data.get("expire_at") | |
| difficulty = challenge_data.get("difficulty") | |
| signature = challenge_data.get("signature") | |
| t_path = challenge_data.get("target_path") | |
| start_time = time.time() | |
| print(f"{Colors.WARNING}β³ Solving PoW (difficulty: {difficulty})...{Colors.ENDC}") | |
| answer = solve_pow(ch_hex, salt, expire_at, difficulty) | |
| elapsed = time.time() - start_time | |
| print(f"{Colors.GREEN}β Solved PoW! Answer: {answer} | Took {elapsed:.1f}s{Colors.ENDC}") | |
| response_json = { | |
| "algorithm": algo, | |
| "challenge": ch_hex, | |
| "salt": salt, | |
| "answer": answer, | |
| "signature": signature, | |
| "target_path": t_path | |
| } | |
| pow_header = base64.b64encode(json.dumps(response_json).encode('utf-8')).decode('utf-8') | |
| with POW_CACHE_LOCK: | |
| POW_CACHE["header"] = pow_header | |
| POW_CACHE["expires"] = time.time() + 300 | |
| print(f"{Colors.GREEN}πΎ PoW cached for 5 minutes{Colors.ENDC}") | |
| return pow_header | |
| # --- OPENAI SPEC COMPLIANT GATEWAY --- | |
| class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): | |
| daemon_threads = True | |
| allow_reuse_address = True | |
| class OpenAICompatibleHandler(http.server.BaseHTTPRequestHandler): | |
| def log_message(self, format, *args): | |
| pass | |
| def send_cors_headers(self): | |
| self.send_header('Access-Control-Allow-Origin', '*') | |
| self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') | |
| self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-ds-pow-response') | |
| def do_HEAD(self): | |
| # Cline and other clients validate endpoints with HEAD requests before POSTing | |
| chat_paths = ("/v1/chat/completions", "/chat/completions") | |
| model_paths = ("/v1/models", "/models") | |
| if self.path in model_paths + chat_paths + ("/health", "/healthz", "/", "/status"): | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| self.end_headers() | |
| else: | |
| self.send_response(404) | |
| self.end_headers() | |
| def do_OPTIONS(self): | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| self.end_headers() | |
| def do_GET(self): | |
| # Support both /models and /v1/models paths | |
| path = self.path | |
| if self.path == "/v1/models": | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| self.send_header('Content-Type', 'application/json') | |
| self.end_headers() | |
| models_data = { | |
| "object": "list", | |
| "data": [ | |
| {"id": "deepseek-chat", "object": "model", "created": 1700000000, "owned_by": "deepseek"}, | |
| {"id": "deepseek-reasoner", "object": "model", "created": 1700000000, "owned_by": "deepseek"} | |
| ] | |
| } | |
| self.wfile.write(json.dumps(models_data).encode('utf-8')) | |
| elif self.path in ("/health", "/healthz"): | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| self.send_header('Content-Type', 'application/json') | |
| self.end_headers() | |
| health = { | |
| "status": "healthy", | |
| "service": "deepseek-proxy", | |
| "timestamp": int(time.time()), | |
| "token_configured": bool(USER_TOKEN), | |
| "version": "1.2.1" | |
| } | |
| self.wfile.write(json.dumps(health).encode('utf-8')) | |
| elif self.path in ("/", "/status"): | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| self.send_header('Content-Type', 'application/json') | |
| self.end_headers() | |
| status = { | |
| "service": "DeepSeek Proxy (HF Space)", | |
| "version": "1.2.1", | |
| "endpoints": { | |
| "chat": "POST /v1/chat/completions", | |
| "models": "GET /v1/models", | |
| "health": "GET /health" | |
| }, | |
| "token_configured": bool(USER_TOKEN), | |
| "instructions": "Set DEEPSEEK_TOKEN secret, then use with VS Code/Cline" | |
| } | |
| self.wfile.write(json.dumps(status).encode('utf-8')) | |
| else: | |
| self.send_response(404) | |
| self.end_headers() | |
| self.wfile.write(b"Not Found") | |
| def do_POST(self): | |
| # Support both /chat/completions and /v1/chat/completions paths | |
| path = self.path | |
| if self.path not in ("/v1/chat/completions", "/chat/completions"): | |
| self.send_response(404) | |
| self.end_headers() | |
| self.wfile.write(b"Not Found") | |
| return | |
| if not USER_TOKEN: | |
| self.send_error_response(500, "DEEPSEEK_TOKEN not configured. Set it in HF Space Settings -> Secrets.") | |
| return | |
| content_length = int(self.headers.get('Content-Length', 0)) | |
| post_data = self.rfile.read(content_length) | |
| try: | |
| req_body = json.loads(post_data.decode('utf-8')) | |
| except Exception as e: | |
| self.send_error_response(400, f"Invalid JSON: {e}") | |
| return | |
| messages = req_body.get("messages", []) | |
| stream = req_body.get("stream", False) | |
| model = req_body.get("model", "deepseek-chat") | |
| conversation_id = req_body.get("conversation_id", "default") | |
| thinking_enabled = (model == "deepseek-reasoner") | |
| session_data = ACTIVE_SESSIONS[conversation_id] | |
| prompt = "" | |
| for msg in messages: | |
| role = msg.get("role", "user") | |
| content = msg.get("content", "") | |
| if isinstance(content, list): | |
| text_parts = [] | |
| for part in content: | |
| if isinstance(part, dict): | |
| pt = part.get("type", "text") | |
| if pt == "text": | |
| text_parts.append(part.get("text", "")) | |
| elif pt == "file": | |
| text_parts.append(f"\n```{part.get('name','file')}\n{part.get('content','')}\n```\n") | |
| elif pt == "image_url": | |
| text_parts.append("[Image: not supported]") | |
| elif isinstance(part, str): | |
| text_parts.append(part) | |
| content = "".join(text_parts) | |
| if role in ("system", "user", "assistant"): | |
| prompt += f"{content}\n\n" | |
| prompt = prompt.strip() | |
| print(f"{Colors.BLUE}π¬ Request | Model: {model} | Stream: {stream}{Colors.ENDC}") | |
| try: | |
| if session_data["session_id"] is None: | |
| session_id = create_chat_session() | |
| session_data["session_id"] = session_id | |
| print(f"{Colors.GREEN}π Session: {session_id[:16]}...{Colors.ENDC}") | |
| else: | |
| session_id = session_data["session_id"] | |
| print(f"{Colors.CYAN}β»οΈ Reusing session{Colors.ENDC}") | |
| pow_header = get_pow_header() | |
| except Exception as e: | |
| traceback.print_exc() | |
| self.send_error_response(500, f"Upstream setup error: {e}") | |
| return | |
| ds_headers = { | |
| "Authorization": f"Bearer {USER_TOKEN}", | |
| "Content-Type": "application/json", | |
| "Accept": "text/event-stream", | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "Referer": "https://chat.deepseek.com/", | |
| "Origin": "https://chat.deepseek.com", | |
| "x-client-platform": "web", | |
| "x-app-version": "20241129.1", | |
| "x-ds-pow-response": pow_header | |
| } | |
| ds_payload = json.dumps({ | |
| "chat_session_id": session_id, | |
| "prompt": prompt, | |
| "parent_message_id": None, | |
| "ref": "web", | |
| "ref_file_ids": [], | |
| "search_enabled": False, | |
| "thinking_enabled": thinking_enabled | |
| }).encode('utf-8') | |
| ds_req = urllib.request.Request( | |
| "https://chat.deepseek.com/api/v0/chat/completion", | |
| data=ds_payload, | |
| headers=ds_headers, | |
| method="POST" | |
| ) | |
| try: | |
| response = urllib.request.urlopen(ds_req, timeout=90) | |
| first_line = response.readline().decode('utf-8').strip() | |
| if "INVALID_POW_RESPONSE" in first_line or "40301" in first_line: | |
| response.close() | |
| with POW_CACHE_LOCK: | |
| POW_CACHE["header"] = None | |
| POW_CACHE["expires"] = 0 | |
| pow_header = get_pow_header() | |
| ds_headers["x-ds-pow-response"] = pow_header | |
| ds_req = urllib.request.Request( | |
| "https://chat.deepseek.com/api/v0/chat/completion", | |
| data=ds_payload, | |
| headers=ds_headers, | |
| method="POST" | |
| ) | |
| response = urllib.request.urlopen(ds_req, timeout=90) | |
| print(f"{Colors.GREEN}π Retried with fresh PoW{Colors.ENDC}") | |
| else: | |
| class ResponseWithFirstLine: | |
| def __init__(self, first_line, response): | |
| self.first_line = first_line.encode('utf-8') + b'\n' | |
| self.response = response | |
| self.first_yielded = False | |
| def __iter__(self): | |
| if not self.first_yielded: | |
| yield self.first_line | |
| self.first_yielded = True | |
| for line in self.response: | |
| yield line | |
| def close(self): | |
| self.response.close() | |
| response = ResponseWithFirstLine(first_line, response) | |
| except urllib.error.HTTPError as e: | |
| err_body = e.read().decode('utf-8') if e.fp else "" | |
| print(f"{Colors.FAIL}β Upstream HTTP {e.code}: {err_body}{Colors.ENDC}") | |
| self.send_error_response(e.code, f"Upstream error: {err_body}") | |
| return | |
| except Exception as e: | |
| traceback.print_exc() | |
| self.send_error_response(503, f"Failed to connect: {e}") | |
| return | |
| self.send_response(200) | |
| self.send_cors_headers() | |
| if stream: | |
| self.send_header('Content-Type', 'text/event-stream') | |
| self.send_header('Cache-Control', 'no-cache') | |
| self.send_header('Connection', 'keep-alive') | |
| self.end_headers() | |
| self.stream_response(response, model) | |
| else: | |
| self.send_header('Content-Type', 'application/json') | |
| self.end_headers() | |
| self.blocking_response(response, model) | |
| def stream_response(self, response, model): | |
| created_time = int(time.time()) | |
| req_id = f"chatcmpl-{uuid.uuid4()}" | |
| is_thinking = (model == "deepseek-reasoner") | |
| try: | |
| chunk_count = 0 | |
| for raw_line in response: | |
| line = raw_line.decode('utf-8').strip() | |
| if not line: | |
| continue | |
| if line.startswith("event:"): | |
| event_name = line[6:].strip() | |
| if event_name in ("close", "finish"): | |
| break | |
| continue | |
| if not line.startswith("data:"): | |
| continue | |
| data_str = line[5:].strip() | |
| if data_str == "[DONE]": | |
| break | |
| try: | |
| ds_chunk = json.loads(data_str) | |
| except: | |
| continue | |
| p_val = ds_chunk.get("p") | |
| v_val = ds_chunk.get("v") | |
| if p_val in ("response/accumulated_token_usage", "response/status"): | |
| if p_val == "response/status" and v_val == "FINISHED": | |
| break | |
| continue | |
| if p_val == "response/thinking_elapsed_secs": | |
| is_thinking = False | |
| continue | |
| elif p_val == "response/content": | |
| is_thinking = False | |
| if isinstance(v_val, str): | |
| self.send_openai_chunk(req_id, created_time, model, v_val, None, None) | |
| continue | |
| if isinstance(v_val, str): | |
| chunk_count += 1 | |
| if is_thinking: | |
| self.send_openai_chunk(req_id, created_time, model, None, v_val, None) | |
| else: | |
| self.send_openai_chunk(req_id, created_time, model, v_val, None, None) | |
| self.send_openai_chunk(req_id, created_time, model, None, None, "stop") | |
| self.wfile.write(b"data: [DONE]\n\n") | |
| self.wfile.flush() | |
| print(f"{Colors.GREEN}>> Stream done. {chunk_count} chunks{Colors.ENDC}") | |
| except Exception as e: | |
| print(f"{Colors.FAIL}>> Stream error: {e}{Colors.ENDC}") | |
| finally: | |
| response.close() | |
| def send_openai_chunk(self, req_id, created_time, model, content, reasoning_content, finish_reason): | |
| delta = {} | |
| if content is not None: | |
| delta["content"] = content | |
| if reasoning_content is not None: | |
| delta["reasoning_content"] = reasoning_content | |
| chunk_payload = { | |
| "id": req_id, | |
| "object": "chat.completion.chunk", | |
| "created": created_time, | |
| "model": model, | |
| "choices": [{"index": 0, "delta": delta, "finish_reason": finish_reason}] | |
| } | |
| sse_str = f"data: {json.dumps(chunk_payload)}\n\n" | |
| self.wfile.write(sse_str.encode('utf-8')) | |
| self.wfile.flush() | |
| def blocking_response(self, response, model): | |
| created_time = int(time.time()) | |
| req_id = f"chatcmpl-{uuid.uuid4()}" | |
| full_content = "" | |
| full_reasoning = "" | |
| is_thinking = (model == "deepseek-reasoner") | |
| try: | |
| for raw_line in response: | |
| line = raw_line.decode('utf-8').strip() | |
| if not line: | |
| continue | |
| if line.startswith("event:"): | |
| event_name = line[6:].strip() | |
| if event_name in ("close", "finish"): | |
| break | |
| continue | |
| if not line.startswith("data:"): | |
| continue | |
| data_str = line[5:].strip() | |
| if data_str == "[DONE]": | |
| break | |
| try: | |
| ds_chunk = json.loads(data_str) | |
| except: | |
| continue | |
| p_val = ds_chunk.get("p") | |
| v_val = ds_chunk.get("v") | |
| if p_val in ("response/accumulated_token_usage", "response/status"): | |
| if p_val == "response/status" and v_val == "FINISHED": | |
| break | |
| continue | |
| if p_val == "response/thinking_elapsed_secs": | |
| is_thinking = False | |
| continue | |
| elif p_val == "response/content": | |
| is_thinking = False | |
| if isinstance(v_val, str): | |
| full_content += v_val | |
| continue | |
| if isinstance(v_val, str): | |
| if is_thinking: | |
| full_reasoning += v_val | |
| else: | |
| full_content += v_val | |
| print(f"{Colors.GREEN}β Blocking response: {len(full_content)} chars content{Colors.ENDC}") | |
| res_payload = { | |
| "id": req_id, | |
| "object": "chat.completion", | |
| "created": created_time, | |
| "model": model, | |
| "choices": [{ | |
| "index": 1, | |
| "message": { | |
| "role": "assistant", | |
| "content": full_content, | |
| "reasoning_content": full_reasoning if full_reasoning else None | |
| }, | |
| "finish_reason": "stop" | |
| }], | |
| "usage": { | |
| "prompt_tokens": len(full_content) // 4 + 10, | |
| "completion_tokens": len(full_content) // 4, | |
| "total_tokens": (len(full_content) // 4) * 2 + 10 | |
| } | |
| } | |
| self.wfile.write(json.dumps(res_payload).encode('utf-8')) | |
| except Exception as e: | |
| traceback.print_exc() | |
| self.send_error_response(500, f"Error gathering response: {e}") | |
| finally: | |
| response.close() | |
| def send_error_response(self, code, message): | |
| self.send_response(code) | |
| self.send_cors_headers() | |
| self.send_header('Content-Type', 'application/json') | |
| self.end_headers() | |
| err_payload = {"error": {"message": message, "type": "api_error", "code": code}} | |
| self.wfile.write(json.dumps(err_payload).encode('utf-8')) | |
| # --- SERVER START --- | |
| def start_server(): | |
| print(f"\n{Colors.HEADER}{Colors.BOLD}==================================================={Colors.ENDC}") | |
| print(f"{Colors.CYAN}{Colors.BOLD} π DEEPSEEK PROXY - HUGGING FACE SPACE EDITION π{Colors.ENDC}") | |
| print(f"{Colors.HEADER}{Colors.BOLD}==================================================={Colors.ENDC}") | |
| print(f"π‘ Endpoint: {Colors.BOLD}http://{HOST}:{PORT}/v1{Colors.ENDC}") | |
| print(f"π Token: {Colors.WARNING}{'β Configured' if USER_TOKEN else 'β NOT SET'}{Colors.ENDC}") | |
| print(f"π§ Models: {Colors.GREEN}deepseek-chat, deepseek-reasoner{Colors.ENDC}") | |
| print(f"π‘οΈ PoW Engine: {Colors.GREEN}Keccak solver (cached 5 min){Colors.ENDC}") | |
| print(f"π Health: {Colors.BOLD}GET /health{Colors.ENDC}") | |
| print(f"{Colors.HEADER}==================================================={Colors.ENDC}\n") | |
| server_address = (HOST, PORT) | |
| try: | |
| with ThreadedHTTPServer(server_address, OpenAICompatibleHandler) as httpd: | |
| print(f"{Colors.GREEN}π₯ Server running on {HOST}:{PORT}{Colors.ENDC}") | |
| httpd.serve_forever() | |
| except KeyboardInterrupt: | |
| print(f"\n{Colors.WARNING}β οΈ Shutting down...{Colors.ENDC}") | |
| except Exception as e: | |
| print(f"\n{Colors.FAIL}β Server error: {e}{Colors.ENDC}") | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| start_server() | |