返回提交历史
Modified
g4f/Provider/needs_auth/DeepSeekAPI.py
+16
-113
Modified
g4f/cookies.py
+28
-13
Modified
requirements.txt
+3
-1
Modified
setup.py
+3
-1
XFEstudio/gpt4free
Refactor DeepSeekAPI for improved authentication handling and add headers management; update requirements for wasmtime and numpy
2e85ad5b
代码差异
4 个文件
+50
-128
@@ -11,7 +11,7 @@ from pathlib import Path
11
11
12
12
from g4f.typing import AsyncResult, Messages, Cookies
13
13
from g4f.requests import StreamSession, raise_for_status, sse_stream, FormData
14
from g4f.cookies import get_cookies, get_cookies_dir
14
from g4f.cookies import get_cookies, get_headers, get_cookies_dir
15
15
from g4f.providers.response import (
16
16
JsonConversation, JsonRequest, JsonResponse,
17
17
Reasoning, FinishReason
@@ -163,76 +163,6 @@ def generate_client_stream_id() -> str:
163
163
hex_part = uuid.uuid4().hex[:16]
164
164
return f"{date_str}-{hex_part}"
165
165
166
167
def get_har_files():
168
"""Get list of DeepSeek HAR files from har_and_cookies directory."""
169
if not os.access(get_cookies_dir(), os.R_OK):
170
return []
171
172
har_files = []
173
for root, _, files in os.walk(get_cookies_dir()):
174
for file in files:
175
# Look for DeepSeek HAR files
176
if file.endswith(".har") and "deepseek" in file.lower():
177
har_files.append(os.path.join(root, file))
178
179
# Sort by modification time, newest first
180
har_files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
181
return har_files
182
183
def read_deepseek_har():
184
"""
185
Read DeepSeek HAR file to extract cookies and auth token.
186
187
Returns:
188
dict with 'cookies' and 'authorization' keys or None if not found
189
"""
190
import g4f.cookies
191
192
har_files = get_har_files()
193
194
if not har_files:
195
debug.log("DeepSeekAuth: No DeepSeek HAR files found in har_and_cookies/")
196
return None
197
198
# Read HAR files to get cookies and authorization header
199
for har_path in har_files:
200
debug.log(f"DeepSeekAuth: Reading HAR file: {har_path}")
201
202
# Get cookies using g4f's HAR parser
203
cookies_by_domain = g4f.cookies._parse_har_file(har_path)
204
205
# Look for DeepSeek cookies
206
deepseek_cookies = None
207
for domain, cookies in cookies_by_domain.items():
208
if 'deepseek.com' in domain:
209
deepseek_cookies = cookies
210
debug.log(f"DeepSeekAuth: Found {len(cookies)} cookies for {domain}")
211
break
212
213
if not deepseek_cookies:
214
continue
215
216
# Now look for authorization header in HAR
217
with open(har_path, 'r', encoding='utf-8') as f:
218
har_data = json.load(f)
219
220
for entry in har_data.get('log', {}).get('entries', []):
221
url = entry.get('request', {}).get('url', '')
222
if 'deepseek.com' in url.lower():
223
for header in entry.get('request', {}).get('headers', []):
224
if header.get('name', '').lower() == 'authorization':
225
auth_header = header.get('value')
226
debug.log(f"DeepSeekAuth: Found authorization token in HAR")
227
return {
228
"cookies": deepseek_cookies,
229
"authorization": auth_header
230
}
231
232
debug.log("DeepSeekAuth: No valid DeepSeek auth found in any HAR file")
233
return None
234
235
236
166
class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
237
167
"""
238
168
DeepSeek provider using browser emulation with HAR file support.
@@ -245,7 +175,7 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
245
175
label = "DeepSeek (HAR Auth)"
246
176
url = DEEPSEEK_URL
247
177
cookie_domain = DEEPSEEK_DOMAIN
248
working = True
178
working = has_wasmtime_and_numpy
249
179
active_by_default = True
250
180
needs_auth = True
251
181
supports_file_upload = True
@@ -392,6 +322,7 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
392
322
model: str,
393
323
messages: Messages,
394
324
cookies: Cookies = None,
325
headers: dict = None,
395
326
proxy: str = None,
396
327
conversation: JsonConversation = None,
397
328
web_search: bool = False,
@@ -422,23 +353,17 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
422
353
model = cls.default_model
423
354
424
355
# Try to get auth from HAR file first
425
auth_data = None
426
356
if cookies is None:
427
auth_data = read_deepseek_har()
428
if auth_data:
429
cookies = auth_data.get("cookies")
430
debug.log(f"DeepSeekAuth: Using {len(cookies)} cookies from HAR file")
357
cookies = get_cookies(cls.cookie_domain, False)
358
headers = get_headers(cls.cookie_domain)
359
if cookies:
360
debug.log(f"DeepSeekAuth: Using {len(cookies)} cookies and {len(headers)} headers from cookie jar")
431
361
else:
432
# Fall back to cookie jar
433
cookies = get_cookies(cls.cookie_domain, False)
434
if cookies:
435
debug.log(f"DeepSeekAuth: Using {len(cookies)} cookies from cookie jar")
436
else:
437
raise MissingAuthError(
438
"DeepSeekAuth: No authentication found. "
439
"Please add a DeepSeek HAR file to har_and_cookies/ directory "
440
"with an authorization token."
441
)
362
raise MissingAuthError(
363
"DeepSeekAuth: No authentication found. "
364
"Please add a DeepSeek HAR file to har_and_cookies/ directory "
365
"with an authorization token."
366
)
442
367
443
368
# Initialize conversation if needed
444
369
if conversation is None:
@@ -448,8 +373,8 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
448
373
449
374
# Get auth token from HAR data or conversation
450
375
authorization = None
451
if auth_data:
452
authorization = auth_data.get("authorization")
376
if headers:
377
authorization = headers.get("authorization")
453
378
elif hasattr(conversation, 'authorization'):
454
379
authorization = conversation.authorization
455
380
@@ -587,22 +512,7 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
587
512
# Check if response is actually SSE or regular JSON
588
513
content_type = response.headers.get('content-type', '')
589
514
if 'text/event-stream' not in content_type.lower():
590
# Not a streaming response - try regular JSON
591
# debug.log(f"DeepSeekAuth: Response is NOT SSE (content-type: {content_type})")
592
data = await response.json()
593
# debug.log(f"DeepSeekAuth: Full response: {data}")
594
595
# Check for content in response
596
if 'content' in data:
597
content = data.get('content', '')
598
yield content
599
if 'choices' in data and len(data['choices']) > 0:
600
choice = data['choices'][0]
601
if 'message' in choice and 'content' in choice['message']:
602
yield choice['message']['content']
603
if 'finish_reason' in data:
604
yield FinishReason(data['finish_reason'])
605
return
515
raise RuntimeError(f"Expected SSE response but got content-type: {content_type}")
606
516
607
517
is_thinking = False
608
518
async for stream_data in sse_stream(response):
@@ -666,14 +576,7 @@ class DeepSeekAPI(AsyncGeneratorProvider, ProviderModelMixin):
666
576
elif 'v' in stream_data and isinstance(stream_data['v'], str):
667
577
yield Reasoning(stream_data['v']) if is_thinking else stream_data['v']
668
578
# debug.log(f"DeepSeekAuth: Shorthand content: '{stream_data['v']}'")
669
670
# Handle finish reason
671
elif isinstance(stream_data, FinishReason):
672
if hasattr(stream_data, 'response_message_id'):
673
conversation.parent_message_id = stream_data.response_message_id
674
yield conversation
675
yield stream_data
676
break
579
677
580
678
581
# Ensure we yield the conversation object at the end
679
582
yield conversation
@@ -43,6 +43,9 @@ from .errors import MissingRequirementsError
43
43
from .config import AppConfig, COOKIES_DIR, CUSTOM_COOKIES_DIR
44
44
from . import debug
45
45
46
class HeadersConfig:
47
headers: Dict[str, Dict[str, str]] = {}
48
46
49
class CookiesConfig:
47
50
cookies: Dict[str, Cookies] = {}
48
51
cookies_dir: str = CUSTOM_COOKIES_DIR if os.path.exists(CUSTOM_COOKIES_DIR) else str(COOKIES_DIR)
@@ -58,7 +61,7 @@ class BrowserConfig:
58
61
59
62
browser_executable_path: str = None
60
63
61
DOMAINS = (
64
COOKIE_DOMAINS = (
62
65
".bing.com",
63
66
".meta.ai",
64
67
".google.com",
@@ -70,13 +73,18 @@ DOMAINS = (
70
73
".cerebras.ai",
71
74
"github.com",
72
75
"yupp.ai",
73
"deepseek.com",
76
"chat.deepseek.com",
74
77
)
75
78
76
79
if has_browser_cookie3 and os.environ.get("DBUS_SESSION_BUS_ADDRESS", "/dev/null") == "/dev/null":
77
80
_LinuxPasswordManager.get_password = lambda a, b: b"secret"
78
81
79
82
83
def get_headers(domain_name: str) -> Dict[str, str]:
84
"""Get cached headers for a domain."""
85
return HeadersConfig.headers.get(domain_name, {})
86
87
80
88
def get_cookies(domain_name: str, raise_requirements_error: bool = True,
81
89
single_browser: bool = False, cache_result: bool = True) -> Dict[str, str]:
82
90
"""Load cookies for a given domain from all supported browsers."""
@@ -135,6 +143,19 @@ def get_cookies_dir() -> str:
135
143
return CookiesConfig.cookies_dir
136
144
137
145
146
def _get_domain(entry: dict) -> Optional[str]:
147
headers = entry["request"].get("headers", [])
148
host_values = [h["value"] for h in headers if h["name"].lower() in ("host", ":authority")]
149
if not host_values:
150
return None
151
host = host_values.pop()
152
return next((d for d in COOKIE_DOMAINS if d in host), None)
153
154
155
def _get_headers(entry) -> dict:
156
return {h['name'].lower(): h['value'] for h in entry['request']['headers'] if h['name'].lower() not in ['content-length', 'cookie'] and not h['name'].startswith(':')}
157
158
138
159
def _parse_har_file(path: str) -> Dict[str, Dict[str, str]]:
139
160
"""Parse a HAR file and return cookies by domain."""
140
161
cookies_by_domain = {}
@@ -143,17 +164,10 @@ def _parse_har_file(path: str) -> Dict[str, Dict[str, str]]:
143
164
har_file = json.load(file)
144
165
debug.log(f"Read .har file: {path}")
145
166
146
def get_domain(entry: dict) -> Optional[str]:
147
headers = entry["request"].get("headers", [])
148
host_values = [h["value"] for h in headers if h["name"].lower() in ("host", ":authority")]
149
if not host_values:
150
return None
151
host = host_values.pop()
152
return next((d for d in DOMAINS if d in host), None)
153
154
167
for entry in har_file.get("log", {}).get("entries", []):
155
domain = get_domain(entry)
168
domain = _get_domain(entry)
156
169
if domain:
170
HeadersConfig.headers[domain] = {**HeadersConfig.headers.get(domain, {}), **_get_headers(entry)}
157
171
v_cookies = {c["name"]: c["value"] for c in entry["request"].get("cookies", [])}
158
172
if v_cookies:
159
173
cookies_by_domain[domain] = v_cookies
@@ -191,8 +205,9 @@ def read_cookie_files(dir_path: Optional[str] = None, domains_filter: Optional[L
191
205
# Optionally load environment variables
192
206
try:
193
207
from dotenv import load_dotenv
194
load_dotenv(os.path.join(dir_path, ".env"), override=True)
195
debug.log(f"Read cookies: Loaded env vars from {dir_path}/.env")
208
env_path = os.path.join(dir_path, ".env")
209
load_dotenv(env_path, override=True)
210
debug.log(f"Read cookies: Loaded env vars from {env_path}")
196
211
except ImportError:
197
212
debug.error("Warning: 'python-dotenv' is not installed. Env vars not loaded.")
198
213
@@ -18,4 +18,6 @@ python-multipart
18
18
a2wsgi
19
19
python-dotenv
20
20
ddgs
21
cloudscraper
21
cloudscraper
22
wasmtime
23
numpy
@@ -39,7 +39,9 @@ EXTRA_REQUIRE = {
39
39
"markitdown[all]",
40
40
"python-dotenv",
41
41
"aiofile",
42
"cloudscraper"
42
"cloudscraper",
43
"wasmtime",
44
"numpy"
43
45
],
44
46
'slim': [
45
47
"curl_cffi>=0.6.2",