Update app.py
Browse files
app.py
CHANGED
|
@@ -31,21 +31,19 @@ DEEPSEEK_EMAIL = os.getenv("DEEPSEEK_EMAIL", "").strip()
|
|
| 31 |
DEEPSEEK_PASSWORD = os.getenv("DEEPSEEK_PASSWORD", "").strip()
|
| 32 |
DEEPSEEK_EMAIL_2 = os.getenv("DEEPSEEK_EMAIL_2", "").strip()
|
| 33 |
DEEPSEEK_PASSWORD_2 = os.getenv("DEEPSEEK_PASSWORD_2", "").strip()
|
| 34 |
-
DEEPSEEK_TOKEN = os.getenv("DEEPSEEK_TOKEN", "").strip()
|
| 35 |
-
DEEPSEEK_TOKEN_2 = os.getenv("DEEPSEEK_TOKEN_2", "").strip()
|
| 36 |
|
| 37 |
ACCOUNTS = []
|
| 38 |
if DEEPSEEK_EMAIL and DEEPSEEK_PASSWORD:
|
| 39 |
ACCOUNTS.append({
|
| 40 |
"email": DEEPSEEK_EMAIL,
|
| 41 |
"password": DEEPSEEK_PASSWORD,
|
| 42 |
-
"token":
|
| 43 |
})
|
| 44 |
if DEEPSEEK_EMAIL_2 and DEEPSEEK_PASSWORD_2:
|
| 45 |
ACCOUNTS.append({
|
| 46 |
"email": DEEPSEEK_EMAIL_2,
|
| 47 |
"password": DEEPSEEK_PASSWORD_2,
|
| 48 |
-
"token":
|
| 49 |
})
|
| 50 |
|
| 51 |
WASM_PATH = os.getenv("WASM_PATH", "./sha3_wasm_bg.7b9ca65ddd.wasm")
|
|
@@ -54,7 +52,6 @@ WASM_URL = "https://raw.githubusercontent.com/iidamie/deepseek2api/main/sha3_was
|
|
| 54 |
KEEP_ALIVE_TIMEOUT = 5
|
| 55 |
CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-20250514"
|
| 56 |
|
| 57 |
-
|
| 58 |
# ============================================================
|
| 59 |
# Logging
|
| 60 |
# ============================================================
|
|
@@ -91,16 +88,13 @@ templates = Jinja2Templates(directory="templates")
|
|
| 91 |
# ============================================================
|
| 92 |
|
| 93 |
def download_wasm_file():
|
| 94 |
-
"""تحميل ملف WASM من GitHub إذا كان مفقوداً"""
|
| 95 |
try:
|
| 96 |
logger.info(f"محاولة تحميل WASM من: {WASM_URL}")
|
| 97 |
urllib.request.urlretrieve(WASM_URL, WASM_PATH)
|
| 98 |
if os.path.isfile(WASM_PATH):
|
| 99 |
logger.info(f"تم تحميل WASM بنجاح إلى: {os.path.abspath(WASM_PATH)}")
|
| 100 |
return True
|
| 101 |
-
|
| 102 |
-
logger.error("فشل تحميل WASM: الملف لم يتم إنشاؤه")
|
| 103 |
-
return False
|
| 104 |
except Exception as e:
|
| 105 |
logger.error(f"فشل تحميل WASM: {e}")
|
| 106 |
return False
|
|
@@ -132,7 +126,7 @@ BASE_HEADERS = {
|
|
| 132 |
|
| 133 |
|
| 134 |
# ============================================================
|
| 135 |
-
# Startup event
|
| 136 |
# ============================================================
|
| 137 |
|
| 138 |
@app.on_event("startup")
|
|
@@ -147,40 +141,36 @@ async def startup_event():
|
|
| 147 |
for acc in ACCOUNTS:
|
| 148 |
logger.info(" - %s", acc.get("email"))
|
| 149 |
else:
|
| 150 |
-
logger.warning("No accounts configured.
|
| 151 |
|
| 152 |
-
# التحقق من وجود ملف WASM وتحميله تلقائياً
|
| 153 |
if not os.path.isfile(WASM_PATH):
|
| 154 |
-
logger.warning(
|
| 155 |
-
logger.info("محاولة تحميل الملف تلقائياً...")
|
| 156 |
if download_wasm_file():
|
| 157 |
-
logger.info("
|
| 158 |
else:
|
| 159 |
-
logger.critical("
|
| 160 |
else:
|
| 161 |
-
logger.info("WASM file found
|
| 162 |
|
| 163 |
logger.info("=" * 60)
|
| 164 |
|
| 165 |
|
| 166 |
# ============================================================
|
| 167 |
-
# Authentication
|
| 168 |
# ============================================================
|
| 169 |
|
| 170 |
account_queue = ACCOUNTS.copy()
|
| 171 |
random.shuffle(account_queue)
|
| 172 |
|
|
|
|
|
|
|
|
|
|
| 173 |
def login_deepseek_via_account(account):
|
| 174 |
-
"""تسجيل الدخول مع تسجيل تفصيلي للاستجابة"""
|
| 175 |
email = account.get("email", "").strip()
|
| 176 |
password = account.get("password", "").strip()
|
| 177 |
|
| 178 |
if not email or not password:
|
| 179 |
-
|
| 180 |
-
raise HTTPException(
|
| 181 |
-
status_code=400,
|
| 182 |
-
detail="الحساب يجب أن يحتوي على email و password"
|
| 183 |
-
)
|
| 184 |
|
| 185 |
payload = {
|
| 186 |
"email": email,
|
|
@@ -189,7 +179,7 @@ def login_deepseek_via_account(account):
|
|
| 189 |
"os": "android",
|
| 190 |
}
|
| 191 |
|
| 192 |
-
logger.info(f"
|
| 193 |
|
| 194 |
try:
|
| 195 |
resp = requests.post(
|
|
@@ -203,25 +193,23 @@ def login_deepseek_via_account(account):
|
|
| 203 |
try:
|
| 204 |
data = resp.json()
|
| 205 |
except Exception as e:
|
| 206 |
-
logger.error(f"فشل تحليل استجابة
|
| 207 |
-
|
| 208 |
-
raise HTTPException(status_code=500, detail="استجابة غير صالحة من الخادم")
|
| 209 |
|
| 210 |
logger.info(f"استجابة تسجيل الدخول: code={data.get('code')}, msg={data.get('msg', 'N/A')}")
|
| 211 |
|
| 212 |
if resp.status_code != 200 or data.get("code") != 0:
|
| 213 |
-
error_msg = data.get("msg", "خطأ غير معروف
|
| 214 |
-
logger.error(f"فشل تسجيل الدخول
|
| 215 |
raise HTTPException(status_code=401, detail=f"فشل تسجيل الدخول: {error_msg}")
|
| 216 |
|
| 217 |
user_data = data.get("data", {}).get("biz_data", {}).get("user")
|
| 218 |
if not user_data:
|
| 219 |
-
logger.error(f"استجابة
|
| 220 |
-
raise HTTPException(status_code=500, detail="استجابة
|
| 221 |
|
| 222 |
new_token = user_data.get("token")
|
| 223 |
if not new_token:
|
| 224 |
-
logger.error(f"لم يتم العثور على token في الاستجابة: {user_data}")
|
| 225 |
raise HTTPException(status_code=500, detail="لم يتم الحصول على token")
|
| 226 |
|
| 227 |
account["token"] = new_token
|
|
@@ -230,47 +218,39 @@ def login_deepseek_via_account(account):
|
|
| 230 |
|
| 231 |
except HTTPException:
|
| 232 |
raise
|
| 233 |
-
except requests.RequestException as e:
|
| 234 |
-
logger.error(f"خطأ في طلب تسجيل الدخول للحساب {email}: {e}")
|
| 235 |
-
raise HTTPException(status_code=500, detail=f"خطأ في الاتصال: {str(e)}")
|
| 236 |
except Exception as e:
|
| 237 |
-
logger.error(f"خطأ
|
| 238 |
-
raise HTTPException(status_code=500, detail=
|
| 239 |
|
| 240 |
-
def refresh_token_for_account(account):
|
| 241 |
-
"""تحديث التوكن لحساب معين"""
|
| 242 |
-
try:
|
| 243 |
-
logger.info(f"تحديث التوكن للحساب: {account.get('email')}")
|
| 244 |
-
return login_deepseek_via_account(account)
|
| 245 |
-
except Exception as e:
|
| 246 |
-
logger.error(f"فشل تحديث التوكن للحساب {account.get('email')}: {e}")
|
| 247 |
-
raise
|
| 248 |
|
| 249 |
def get_valid_token():
|
| 250 |
-
"""الحصول على token صالح مع إعادة المحاولة"""
|
| 251 |
if not account_queue:
|
| 252 |
-
|
| 253 |
-
raise HTTPException(
|
| 254 |
-
status_code=500,
|
| 255 |
-
detail="لا توجد حسابات متاحة. تأكد من تعيين DEEPSEEK_EMAIL و DEEPSEEK_PASSWORD."
|
| 256 |
-
)
|
| 257 |
|
|
|
|
| 258 |
account = account_queue.pop(0)
|
| 259 |
email = account.get("email", "unknown")
|
| 260 |
|
| 261 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
try:
|
| 263 |
-
logger.info(f"تسجيل الدخول للحصول على token جديد: {email}")
|
| 264 |
token = login_deepseek_via_account(account)
|
|
|
|
| 265 |
account_queue.append(account)
|
| 266 |
return token, account
|
| 267 |
except Exception as e:
|
| 268 |
-
logger.error(f"فشل ال
|
| 269 |
if account_queue:
|
| 270 |
-
logger.info("محاولة استخدام الحساب التالي...")
|
| 271 |
return get_valid_token()
|
| 272 |
-
|
| 273 |
-
|
| 274 |
|
| 275 |
def get_deepseek_token():
|
| 276 |
try:
|
|
@@ -278,30 +258,28 @@ def get_deepseek_token():
|
|
| 278 |
except HTTPException:
|
| 279 |
raise
|
| 280 |
except Exception as e:
|
| 281 |
-
logger.error(f"خطأ
|
| 282 |
-
raise HTTPException(status_code=500, detail="خطأ
|
|
|
|
| 283 |
|
| 284 |
def determine_mode_and_token(request: Request):
|
| 285 |
try:
|
| 286 |
token, account = get_deepseek_token()
|
| 287 |
request.state.deepseek_token = token
|
| 288 |
request.state.account = account
|
| 289 |
-
|
| 290 |
-
logger.info(f"تم تعيين التوكن للطلب بنجاح")
|
| 291 |
-
except HTTPException as e:
|
| 292 |
-
logger.error(f"فشل تعيين التوكن: {e.detail}")
|
| 293 |
-
raise
|
| 294 |
except Exception as e:
|
| 295 |
-
logger.error(f"
|
| 296 |
-
raise
|
|
|
|
| 297 |
|
| 298 |
def determine_claude_mode_and_token(request: Request):
|
| 299 |
determine_mode_and_token(request)
|
| 300 |
|
|
|
|
| 301 |
def get_auth_headers(request: Request):
|
| 302 |
token = getattr(request.state, "deepseek_token", None)
|
| 303 |
if not token:
|
| 304 |
-
logger.warning("لم يتم العثور على token في الطلب، محاولة الحصول على واحد جديد")
|
| 305 |
token, _ = get_deepseek_token()
|
| 306 |
request.state.deepseek_token = token
|
| 307 |
return {
|
|
@@ -311,7 +289,7 @@ def get_auth_headers(request: Request):
|
|
| 311 |
|
| 312 |
|
| 313 |
# ============================================================
|
| 314 |
-
# Utility functions
|
| 315 |
# ============================================================
|
| 316 |
|
| 317 |
def safe_json_response_error(status_code: int, message: str):
|
|
@@ -515,7 +493,7 @@ Do not write explanations before or after the JSON.
|
|
| 515 |
|
| 516 |
|
| 517 |
# ============================================================
|
| 518 |
-
# DeepSeek completion helpers
|
| 519 |
# ============================================================
|
| 520 |
|
| 521 |
def call_completion_endpoint(payload, headers, max_attempts=3):
|
|
@@ -539,9 +517,13 @@ def call_completion_endpoint(payload, headers, max_attempts=3):
|
|
| 539 |
time.sleep(1)
|
| 540 |
return None
|
| 541 |
|
| 542 |
-
def create_session(request: Request, max_attempts=
|
|
|
|
|
|
|
|
|
|
| 543 |
for attempt in range(max_attempts):
|
| 544 |
headers = get_auth_headers(request)
|
|
|
|
| 545 |
try:
|
| 546 |
response = requests.post(
|
| 547 |
DEEPSEEK_CREATE_SESSION_URL,
|
|
@@ -550,84 +532,71 @@ def create_session(request: Request, max_attempts=3):
|
|
| 550 |
impersonate="safari15_3",
|
| 551 |
timeout=30
|
| 552 |
)
|
|
|
|
| 553 |
try:
|
| 554 |
data = response.json()
|
| 555 |
-
logger.info(f"Create session response: code={data.get('code')}
|
| 556 |
-
|
| 557 |
-
# طباعة الاستجابة كاملة للتصحيح
|
| 558 |
-
if data.get('code') == 0:
|
| 559 |
-
biz_data = data.get('data', {}).get('biz_data', {})
|
| 560 |
-
logger.info(f"biz_data: {biz_data}")
|
| 561 |
except Exception as e:
|
| 562 |
-
logger.error(f"فشل تحليل استجابة
|
| 563 |
data = {}
|
| 564 |
|
| 565 |
if response.status_code == 200 and data.get("code") == 0:
|
| 566 |
-
#
|
| 567 |
-
session_id
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
)
|
| 572 |
-
|
| 573 |
if session_id:
|
|
|
|
| 574 |
logger.info(f"تم إنشاء الجلسة: {session_id}")
|
| 575 |
return session_id
|
| 576 |
else:
|
| 577 |
-
logger.warning(f"
|
| 578 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
account = getattr(request.state, "account", None)
|
| 580 |
if account:
|
| 581 |
try:
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
request.state.deepseek_token = new_token
|
| 585 |
continue
|
| 586 |
except Exception as e:
|
| 587 |
logger.error(f"فشل تحديث التوكن: {e}")
|
| 588 |
-
|
| 589 |
-
logger.warning("Create session: 401 Unauthorized, refreshing token...")
|
| 590 |
-
account = getattr(request.state, "account", None)
|
| 591 |
-
if account:
|
| 592 |
-
try:
|
| 593 |
-
new_token = refresh_token_for_account(account)
|
| 594 |
-
request.state.deepseek_token = new_token
|
| 595 |
-
continue
|
| 596 |
-
except Exception as e:
|
| 597 |
-
logger.error(f"فشل تحديث التوكن: {e}")
|
| 598 |
-
try:
|
| 599 |
-
token, new_account = get_deepseek_token()
|
| 600 |
-
request.state.deepseek_token = token
|
| 601 |
-
request.state.account = new_account
|
| 602 |
-
continue
|
| 603 |
-
except Exception as e2:
|
| 604 |
-
logger.error(f"فشل الحصول على حساب آخر: {e2}")
|
| 605 |
-
else:
|
| 606 |
-
try:
|
| 607 |
-
token, new_account = get_deepseek_token()
|
| 608 |
-
request.state.deepseek_token = token
|
| 609 |
-
request.state.account = new_account
|
| 610 |
-
continue
|
| 611 |
-
except Exception as e:
|
| 612 |
-
logger.error(f"فشل الحصول على حساب: {e}")
|
| 613 |
-
else:
|
| 614 |
-
error_msg = data.get("msg", f"HTTP {response.status_code}")
|
| 615 |
-
logger.warning(f"Create session failed: {error_msg}")
|
| 616 |
-
response.close()
|
| 617 |
except Exception as exc:
|
| 618 |
logger.warning(f"Create session error: {exc}")
|
|
|
|
| 619 |
if attempt < max_attempts - 1:
|
| 620 |
time.sleep(1)
|
|
|
|
| 621 |
return None
|
| 622 |
|
| 623 |
def compute_pow_answer(algorithm, challenge_str, salt, difficulty, expire_at, signature, target_path, wasm_path):
|
| 624 |
if algorithm != "DeepSeekHashV1":
|
| 625 |
raise ValueError(f"Unsupported PoW algorithm: {algorithm}")
|
| 626 |
if not os.path.isfile(wasm_path):
|
| 627 |
-
logger.error(f"WASM file not found: {wasm_path}")
|
| 628 |
-
# محاولة تحميل الملف
|
| 629 |
if download_wasm_file():
|
| 630 |
-
logger.info("
|
| 631 |
else:
|
| 632 |
raise FileNotFoundError(f"WASM file not found: {wasm_path}")
|
| 633 |
|
|
@@ -677,7 +646,7 @@ def compute_pow_answer(algorithm, challenge_str, salt, difficulty, expire_at, si
|
|
| 677 |
finally:
|
| 678 |
add_to_stack(store, 16)
|
| 679 |
|
| 680 |
-
def get_pow_response(request: Request, max_attempts=
|
| 681 |
for attempt in range(max_attempts):
|
| 682 |
headers = get_auth_headers(request)
|
| 683 |
try:
|
|
@@ -724,34 +693,18 @@ def get_pow_response(request: Request, max_attempts=3):
|
|
| 724 |
response.close()
|
| 725 |
logger.info("تم الحصول على PoW بنجاح")
|
| 726 |
return encoded
|
| 727 |
-
elif response.status_code == 401:
|
| 728 |
logger.warning("PoW: 401 Unauthorized, refreshing token...")
|
| 729 |
account = getattr(request.state, "account", None)
|
| 730 |
if account:
|
| 731 |
try:
|
| 732 |
-
|
| 733 |
-
request.state.deepseek_token = new_token
|
| 734 |
-
continue
|
| 735 |
-
except Exception as e:
|
| 736 |
-
logger.error(f"فشل تحديث التوكن: {e}")
|
| 737 |
-
try:
|
| 738 |
-
token, new_account = get_deepseek_token()
|
| 739 |
-
request.state.deepseek_token = token
|
| 740 |
-
request.state.account = new_account
|
| 741 |
-
continue
|
| 742 |
-
except Exception as e2:
|
| 743 |
-
logger.error(f"فشل الحصول على حساب آخر: {e2}")
|
| 744 |
-
else:
|
| 745 |
-
try:
|
| 746 |
-
token, new_account = get_deepseek_token()
|
| 747 |
request.state.deepseek_token = token
|
| 748 |
-
request.state.account = new_account
|
| 749 |
continue
|
| 750 |
except Exception as e:
|
| 751 |
-
logger.error(f"فشل
|
| 752 |
else:
|
| 753 |
-
logger.warning("PoW request failed: HTTP=%s code=%s
|
| 754 |
-
response.status_code, data.get("code"), data.get("msg"))
|
| 755 |
response.close()
|
| 756 |
except Exception as exc:
|
| 757 |
logger.error("PoW error: %s", exc)
|
|
@@ -771,9 +724,7 @@ def delete_session(request: Request, session_id: str):
|
|
| 771 |
timeout=5
|
| 772 |
)
|
| 773 |
if response.status_code == 200:
|
| 774 |
-
logger.info("Deleted
|
| 775 |
-
else:
|
| 776 |
-
logger.warning("Delete session failed: HTTP %s", response.status_code)
|
| 777 |
response.close()
|
| 778 |
except Exception as exc:
|
| 779 |
logger.warning("Delete session error: %s", exc)
|
|
@@ -831,7 +782,7 @@ def parse_deepseek_stream(response, on_text=None, on_thinking=None):
|
|
| 831 |
|
| 832 |
|
| 833 |
# ============================================================
|
| 834 |
-
#
|
| 835 |
# ============================================================
|
| 836 |
|
| 837 |
@app.get("/v1/models")
|
|
@@ -844,6 +795,7 @@ async def list_models():
|
|
| 844 |
]
|
| 845 |
return {"object": "list", "data": models}
|
| 846 |
|
|
|
|
| 847 |
@app.post("/v1/chat/completions")
|
| 848 |
async def chat_completions(request: Request):
|
| 849 |
try:
|
|
@@ -873,10 +825,12 @@ async def chat_completions(request: Request):
|
|
| 873 |
|
| 874 |
final_prompt = messages_prepare(messages)
|
| 875 |
|
|
|
|
| 876 |
session_id = create_session(request)
|
| 877 |
if not session_id:
|
| 878 |
return safe_json_response_error(401, "Unable to create DeepSeek session.")
|
| 879 |
|
|
|
|
| 880 |
pow_response = get_pow_response(request)
|
| 881 |
if not pow_response:
|
| 882 |
delete_session(request, session_id)
|
|
@@ -903,6 +857,7 @@ async def chat_completions(request: Request):
|
|
| 903 |
created = int(time.time())
|
| 904 |
completion_id = f"chatcmpl-{session_id}"
|
| 905 |
|
|
|
|
| 906 |
if req_data.get("stream", False):
|
| 907 |
def stream_generator():
|
| 908 |
final_text = []
|
|
@@ -1022,6 +977,7 @@ async def chat_completions(request: Request):
|
|
| 1022 |
headers={"Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no"}
|
| 1023 |
)
|
| 1024 |
|
|
|
|
| 1025 |
final_text = []
|
| 1026 |
final_thinking = []
|
| 1027 |
parse_deepseek_stream(deepseek_response, on_text=final_text.append, on_thinking=final_thinking.append)
|
|
@@ -1061,7 +1017,7 @@ async def chat_completions(request: Request):
|
|
| 1061 |
|
| 1062 |
|
| 1063 |
# ============================================================
|
| 1064 |
-
# Claude endpoints (shortened
|
| 1065 |
# ============================================================
|
| 1066 |
|
| 1067 |
@app.get("/anthropic/v1/models")
|
|
|
|
| 31 |
DEEPSEEK_PASSWORD = os.getenv("DEEPSEEK_PASSWORD", "").strip()
|
| 32 |
DEEPSEEK_EMAIL_2 = os.getenv("DEEPSEEK_EMAIL_2", "").strip()
|
| 33 |
DEEPSEEK_PASSWORD_2 = os.getenv("DEEPSEEK_PASSWORD_2", "").strip()
|
|
|
|
|
|
|
| 34 |
|
| 35 |
ACCOUNTS = []
|
| 36 |
if DEEPSEEK_EMAIL and DEEPSEEK_PASSWORD:
|
| 37 |
ACCOUNTS.append({
|
| 38 |
"email": DEEPSEEK_EMAIL,
|
| 39 |
"password": DEEPSEEK_PASSWORD,
|
| 40 |
+
"token": ""
|
| 41 |
})
|
| 42 |
if DEEPSEEK_EMAIL_2 and DEEPSEEK_PASSWORD_2:
|
| 43 |
ACCOUNTS.append({
|
| 44 |
"email": DEEPSEEK_EMAIL_2,
|
| 45 |
"password": DEEPSEEK_PASSWORD_2,
|
| 46 |
+
"token": ""
|
| 47 |
})
|
| 48 |
|
| 49 |
WASM_PATH = os.getenv("WASM_PATH", "./sha3_wasm_bg.7b9ca65ddd.wasm")
|
|
|
|
| 52 |
KEEP_ALIVE_TIMEOUT = 5
|
| 53 |
CLAUDE_DEFAULT_MODEL = "claude-sonnet-4-20250514"
|
| 54 |
|
|
|
|
| 55 |
# ============================================================
|
| 56 |
# Logging
|
| 57 |
# ============================================================
|
|
|
|
| 88 |
# ============================================================
|
| 89 |
|
| 90 |
def download_wasm_file():
|
|
|
|
| 91 |
try:
|
| 92 |
logger.info(f"محاولة تحميل WASM من: {WASM_URL}")
|
| 93 |
urllib.request.urlretrieve(WASM_URL, WASM_PATH)
|
| 94 |
if os.path.isfile(WASM_PATH):
|
| 95 |
logger.info(f"تم تحميل WASM بنجاح إلى: {os.path.abspath(WASM_PATH)}")
|
| 96 |
return True
|
| 97 |
+
return False
|
|
|
|
|
|
|
| 98 |
except Exception as e:
|
| 99 |
logger.error(f"فشل تحميل WASM: {e}")
|
| 100 |
return False
|
|
|
|
| 126 |
|
| 127 |
|
| 128 |
# ============================================================
|
| 129 |
+
# Startup event
|
| 130 |
# ============================================================
|
| 131 |
|
| 132 |
@app.on_event("startup")
|
|
|
|
| 141 |
for acc in ACCOUNTS:
|
| 142 |
logger.info(" - %s", acc.get("email"))
|
| 143 |
else:
|
| 144 |
+
logger.warning("No accounts configured.")
|
| 145 |
|
|
|
|
| 146 |
if not os.path.isfile(WASM_PATH):
|
| 147 |
+
logger.warning("WASM file missing, attempting download...")
|
|
|
|
| 148 |
if download_wasm_file():
|
| 149 |
+
logger.info("WASM downloaded successfully")
|
| 150 |
else:
|
| 151 |
+
logger.critical("Failed to download WASM file")
|
| 152 |
else:
|
| 153 |
+
logger.info("WASM file found.")
|
| 154 |
|
| 155 |
logger.info("=" * 60)
|
| 156 |
|
| 157 |
|
| 158 |
# ============================================================
|
| 159 |
+
# Authentication
|
| 160 |
# ============================================================
|
| 161 |
|
| 162 |
account_queue = ACCOUNTS.copy()
|
| 163 |
random.shuffle(account_queue)
|
| 164 |
|
| 165 |
+
# تخزين التوكنات مؤقتاً
|
| 166 |
+
token_cache = {}
|
| 167 |
+
|
| 168 |
def login_deepseek_via_account(account):
|
|
|
|
| 169 |
email = account.get("email", "").strip()
|
| 170 |
password = account.get("password", "").strip()
|
| 171 |
|
| 172 |
if not email or not password:
|
| 173 |
+
raise HTTPException(status_code=400, detail="الحساب يجب أن يحتوي على email و password")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
|
| 175 |
payload = {
|
| 176 |
"email": email,
|
|
|
|
| 179 |
"os": "android",
|
| 180 |
}
|
| 181 |
|
| 182 |
+
logger.info(f"تسجيل الدخول للحساب: {email}")
|
| 183 |
|
| 184 |
try:
|
| 185 |
resp = requests.post(
|
|
|
|
| 193 |
try:
|
| 194 |
data = resp.json()
|
| 195 |
except Exception as e:
|
| 196 |
+
logger.error(f"فشل تحليل الاستجابة: {e}")
|
| 197 |
+
raise HTTPException(status_code=500, detail="استجابة غير صالحة")
|
|
|
|
| 198 |
|
| 199 |
logger.info(f"استجابة تسجيل الدخول: code={data.get('code')}, msg={data.get('msg', 'N/A')}")
|
| 200 |
|
| 201 |
if resp.status_code != 200 or data.get("code") != 0:
|
| 202 |
+
error_msg = data.get("msg", "خطأ غير معروف")
|
| 203 |
+
logger.error(f"فشل تسجيل الدخول: {error_msg}")
|
| 204 |
raise HTTPException(status_code=401, detail=f"فشل تسجيل الدخول: {error_msg}")
|
| 205 |
|
| 206 |
user_data = data.get("data", {}).get("biz_data", {}).get("user")
|
| 207 |
if not user_data:
|
| 208 |
+
logger.error(f"استجابة غير مكتملة: {data}")
|
| 209 |
+
raise HTTPException(status_code=500, detail="استجابة غير مكتملة")
|
| 210 |
|
| 211 |
new_token = user_data.get("token")
|
| 212 |
if not new_token:
|
|
|
|
| 213 |
raise HTTPException(status_code=500, detail="لم يتم الحصول على token")
|
| 214 |
|
| 215 |
account["token"] = new_token
|
|
|
|
| 218 |
|
| 219 |
except HTTPException:
|
| 220 |
raise
|
|
|
|
|
|
|
|
|
|
| 221 |
except Exception as e:
|
| 222 |
+
logger.error(f"خطأ في تسجيل الدخول: {e}")
|
| 223 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 224 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
def get_valid_token():
|
|
|
|
| 227 |
if not account_queue:
|
| 228 |
+
raise HTTPException(status_code=500, detail="لا توجد حسابات متاحة")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
+
# نأخذ حساب من القائمة
|
| 231 |
account = account_queue.pop(0)
|
| 232 |
email = account.get("email", "unknown")
|
| 233 |
|
| 234 |
+
# التحقق من التخزين المؤقت
|
| 235 |
+
if email in token_cache and token_cache[email].get("token"):
|
| 236 |
+
logger.info(f"استخدام token مخزن للحساب: {email}")
|
| 237 |
+
token = token_cache[email]["token"]
|
| 238 |
+
account["token"] = token
|
| 239 |
+
account_queue.append(account)
|
| 240 |
+
return token, account
|
| 241 |
+
|
| 242 |
+
# تسجيل الدخول
|
| 243 |
try:
|
|
|
|
| 244 |
token = login_deepseek_via_account(account)
|
| 245 |
+
token_cache[email] = {"token": token, "timestamp": time.time()}
|
| 246 |
account_queue.append(account)
|
| 247 |
return token, account
|
| 248 |
except Exception as e:
|
| 249 |
+
logger.error(f"فشل تسجيل الدخول للحساب {email}: {e}")
|
| 250 |
if account_queue:
|
|
|
|
| 251 |
return get_valid_token()
|
| 252 |
+
raise
|
| 253 |
+
|
| 254 |
|
| 255 |
def get_deepseek_token():
|
| 256 |
try:
|
|
|
|
| 258 |
except HTTPException:
|
| 259 |
raise
|
| 260 |
except Exception as e:
|
| 261 |
+
logger.error(f"خطأ في get_deepseek_token: {e}")
|
| 262 |
+
raise HTTPException(status_code=500, detail="خطأ في المصادقة")
|
| 263 |
+
|
| 264 |
|
| 265 |
def determine_mode_and_token(request: Request):
|
| 266 |
try:
|
| 267 |
token, account = get_deepseek_token()
|
| 268 |
request.state.deepseek_token = token
|
| 269 |
request.state.account = account
|
| 270 |
+
logger.info(f"تم تعيين التوكن للطلب")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
except Exception as e:
|
| 272 |
+
logger.error(f"فشل تعيين التوكن: {e}")
|
| 273 |
+
raise
|
| 274 |
+
|
| 275 |
|
| 276 |
def determine_claude_mode_and_token(request: Request):
|
| 277 |
determine_mode_and_token(request)
|
| 278 |
|
| 279 |
+
|
| 280 |
def get_auth_headers(request: Request):
|
| 281 |
token = getattr(request.state, "deepseek_token", None)
|
| 282 |
if not token:
|
|
|
|
| 283 |
token, _ = get_deepseek_token()
|
| 284 |
request.state.deepseek_token = token
|
| 285 |
return {
|
|
|
|
| 289 |
|
| 290 |
|
| 291 |
# ============================================================
|
| 292 |
+
# Utility functions
|
| 293 |
# ============================================================
|
| 294 |
|
| 295 |
def safe_json_response_error(status_code: int, message: str):
|
|
|
|
| 493 |
|
| 494 |
|
| 495 |
# ============================================================
|
| 496 |
+
# DeepSeek completion helpers - FIXED session_id extraction
|
| 497 |
# ============================================================
|
| 498 |
|
| 499 |
def call_completion_endpoint(payload, headers, max_attempts=3):
|
|
|
|
| 517 |
time.sleep(1)
|
| 518 |
return None
|
| 519 |
|
| 520 |
+
def create_session(request: Request, max_attempts=2):
|
| 521 |
+
"""
|
| 522 |
+
إنشاء جلسة DeepSeek مع إصلاح قراءة session_id
|
| 523 |
+
"""
|
| 524 |
for attempt in range(max_attempts):
|
| 525 |
headers = get_auth_headers(request)
|
| 526 |
+
|
| 527 |
try:
|
| 528 |
response = requests.post(
|
| 529 |
DEEPSEEK_CREATE_SESSION_URL,
|
|
|
|
| 532 |
impersonate="safari15_3",
|
| 533 |
timeout=30
|
| 534 |
)
|
| 535 |
+
|
| 536 |
try:
|
| 537 |
data = response.json()
|
| 538 |
+
logger.info(f"Create session response: code={data.get('code')}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
except Exception as e:
|
| 540 |
+
logger.error(f"فشل تحليل الاستجابة: {e}")
|
| 541 |
data = {}
|
| 542 |
|
| 543 |
if response.status_code == 200 and data.get("code") == 0:
|
| 544 |
+
# ============================================================
|
| 545 |
+
# إصلاح قراءة session_id من المكان الصحيح
|
| 546 |
+
# ============================================================
|
| 547 |
+
biz_data = data.get("data", {}).get("biz_data", {})
|
| 548 |
+
chat_session = biz_data.get("chat_session", {})
|
| 549 |
+
session_id = chat_session.get("id")
|
| 550 |
+
|
| 551 |
if session_id:
|
| 552 |
+
response.close()
|
| 553 |
logger.info(f"تم إنشاء الجلسة: {session_id}")
|
| 554 |
return session_id
|
| 555 |
else:
|
| 556 |
+
logger.warning(f"session_id غير موجود في الاستجابة")
|
| 557 |
+
# نحاول مرة واحدة فقط إعادة تسجيل الدخول
|
| 558 |
+
if attempt == 0:
|
| 559 |
+
logger.info("محاولة تسجيل الدخول مرة أخرى...")
|
| 560 |
+
account = getattr(request.state, "account", None)
|
| 561 |
+
if account:
|
| 562 |
+
try:
|
| 563 |
+
# تحديث التوكن
|
| 564 |
+
token = login_deepseek_via_account(account)
|
| 565 |
+
request.state.deepseek_token = token
|
| 566 |
+
continue
|
| 567 |
+
except Exception as e:
|
| 568 |
+
logger.error(f"فشل تحديث التوكن: {e}")
|
| 569 |
+
continue
|
| 570 |
+
else:
|
| 571 |
+
error_msg = data.get("msg", f"HTTP {response.status_code}")
|
| 572 |
+
logger.warning(f"Create session failed: {error_msg}")
|
| 573 |
+
response.close()
|
| 574 |
+
|
| 575 |
+
# في حالة 401، نحاول تحديث التوكن
|
| 576 |
+
if response.status_code == 401 and attempt == 0:
|
| 577 |
account = getattr(request.state, "account", None)
|
| 578 |
if account:
|
| 579 |
try:
|
| 580 |
+
token = login_deepseek_via_account(account)
|
| 581 |
+
request.state.deepseek_token = token
|
|
|
|
| 582 |
continue
|
| 583 |
except Exception as e:
|
| 584 |
logger.error(f"فشل تحديث التوكن: {e}")
|
| 585 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
except Exception as exc:
|
| 587 |
logger.warning(f"Create session error: {exc}")
|
| 588 |
+
|
| 589 |
if attempt < max_attempts - 1:
|
| 590 |
time.sleep(1)
|
| 591 |
+
|
| 592 |
return None
|
| 593 |
|
| 594 |
def compute_pow_answer(algorithm, challenge_str, salt, difficulty, expire_at, signature, target_path, wasm_path):
|
| 595 |
if algorithm != "DeepSeekHashV1":
|
| 596 |
raise ValueError(f"Unsupported PoW algorithm: {algorithm}")
|
| 597 |
if not os.path.isfile(wasm_path):
|
|
|
|
|
|
|
| 598 |
if download_wasm_file():
|
| 599 |
+
logger.info("WASM downloaded successfully")
|
| 600 |
else:
|
| 601 |
raise FileNotFoundError(f"WASM file not found: {wasm_path}")
|
| 602 |
|
|
|
|
| 646 |
finally:
|
| 647 |
add_to_stack(store, 16)
|
| 648 |
|
| 649 |
+
def get_pow_response(request: Request, max_attempts=2):
|
| 650 |
for attempt in range(max_attempts):
|
| 651 |
headers = get_auth_headers(request)
|
| 652 |
try:
|
|
|
|
| 693 |
response.close()
|
| 694 |
logger.info("تم الحصول على PoW بنجاح")
|
| 695 |
return encoded
|
| 696 |
+
elif response.status_code == 401 and attempt == 0:
|
| 697 |
logger.warning("PoW: 401 Unauthorized, refreshing token...")
|
| 698 |
account = getattr(request.state, "account", None)
|
| 699 |
if account:
|
| 700 |
try:
|
| 701 |
+
token = login_deepseek_via_account(account)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 702 |
request.state.deepseek_token = token
|
|
|
|
| 703 |
continue
|
| 704 |
except Exception as e:
|
| 705 |
+
logger.error(f"فشل تحديث التوكن: {e}")
|
| 706 |
else:
|
| 707 |
+
logger.warning("PoW request failed: HTTP=%s code=%s", response.status_code, data.get("code"))
|
|
|
|
| 708 |
response.close()
|
| 709 |
except Exception as exc:
|
| 710 |
logger.error("PoW error: %s", exc)
|
|
|
|
| 724 |
timeout=5
|
| 725 |
)
|
| 726 |
if response.status_code == 200:
|
| 727 |
+
logger.info("Deleted session: %s", session_id)
|
|
|
|
|
|
|
| 728 |
response.close()
|
| 729 |
except Exception as exc:
|
| 730 |
logger.warning("Delete session error: %s", exc)
|
|
|
|
| 782 |
|
| 783 |
|
| 784 |
# ============================================================
|
| 785 |
+
# Endpoints (unchanged)
|
| 786 |
# ============================================================
|
| 787 |
|
| 788 |
@app.get("/v1/models")
|
|
|
|
| 795 |
]
|
| 796 |
return {"object": "list", "data": models}
|
| 797 |
|
| 798 |
+
|
| 799 |
@app.post("/v1/chat/completions")
|
| 800 |
async def chat_completions(request: Request):
|
| 801 |
try:
|
|
|
|
| 825 |
|
| 826 |
final_prompt = messages_prepare(messages)
|
| 827 |
|
| 828 |
+
# إنشاء الجلسة
|
| 829 |
session_id = create_session(request)
|
| 830 |
if not session_id:
|
| 831 |
return safe_json_response_error(401, "Unable to create DeepSeek session.")
|
| 832 |
|
| 833 |
+
# الحصول على PoW
|
| 834 |
pow_response = get_pow_response(request)
|
| 835 |
if not pow_response:
|
| 836 |
delete_session(request, session_id)
|
|
|
|
| 857 |
created = int(time.time())
|
| 858 |
completion_id = f"chatcmpl-{session_id}"
|
| 859 |
|
| 860 |
+
# Streaming
|
| 861 |
if req_data.get("stream", False):
|
| 862 |
def stream_generator():
|
| 863 |
final_text = []
|
|
|
|
| 977 |
headers={"Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no"}
|
| 978 |
)
|
| 979 |
|
| 980 |
+
# Non-streaming
|
| 981 |
final_text = []
|
| 982 |
final_thinking = []
|
| 983 |
parse_deepseek_stream(deepseek_response, on_text=final_text.append, on_thinking=final_thinking.append)
|
|
|
|
| 1017 |
|
| 1018 |
|
| 1019 |
# ============================================================
|
| 1020 |
+
# Claude endpoints (shortened)
|
| 1021 |
# ============================================================
|
| 1022 |
|
| 1023 |
@app.get("/anthropic/v1/models")
|