返回提交历史
Modified
g4f/providers/base_provider.py
+2
-2
Modified
g4f/requests/__init__.py
+1
-0
Modified
g4f/requests/aiohttp.py
+44
-1
XFEstudio/gpt4free
perf(network): implement reusable connection pools with keep-alive and DNS caching
ede3339c
代码差异
3 个文件
+47
-3
@@ -20,7 +20,7 @@ from .types import BaseProvider
20
20
from .asyncio import to_sync_generator, to_async_iterator
21
21
from .response import BaseConversation, AuthResult
22
22
from ..cookies import get_cookies_dir
23
from ..requests import raise_for_status
23
from ..requests import raise_for_status, StreamSession
24
24
from ..errors import (
25
25
ResponseError,
26
26
MissingAuthError,
@@ -337,7 +337,7 @@ class AsyncGeneratorProvider(AbstractProvider):
337
337
if not api_key and cls.needs_auth:
338
338
raise MissingAuthError("API key is required.")
339
339
headers = {"authorization": f"Bearer {api_key}"} if api_key else {}
340
async with ClientSession() as session:
340
async with StreamSession() as session:
341
341
async with session.get(cls.quota_url, headers=headers) as response:
342
342
await raise_for_status(response)
343
343
return await response.json()
@@ -59,6 +59,7 @@ from ..errors import MissingRequirementsError
59
59
from ..typing import Cookies
60
60
from ..cookies import BrowserConfig, get_cookies_dir
61
61
from .defaults import DEFAULT_HEADERS, WEBVIEW_HAEDERS
62
from .aiohttp import get_shared_connector, close_shared_connectors
62
63
63
64
if not has_curl_cffi:
64
65
@@ -1,11 +1,14 @@
1
1
from __future__ import annotations
2
2
3
3
import json
4
import asyncio
5
import weakref
4
6
from aiohttp import (
5
7
ClientSession,
6
8
ClientResponse,
7
9
ClientTimeout,
8
10
BaseConnector,
11
TCPConnector,
9
12
FormData,
10
13
)
11
14
from typing import AsyncIterator, Any, Optional
@@ -13,6 +16,35 @@ from typing import AsyncIterator, Any, Optional
13
16
from .defaults import DEFAULT_HEADERS, has_brotli
14
17
from ..errors import MissingRequirementsError
15
18
19
_loop_connectors: weakref.WeakKeyDictionary[asyncio.AbstractEventLoop, TCPConnector] = weakref.WeakKeyDictionary()
20
21
22
def get_shared_connector() -> Optional[TCPConnector]:
23
"""Retrieve or create a loop-bound TCPConnector with keep-alive and DNS caching."""
24
try:
25
loop = asyncio.get_running_loop()
26
except RuntimeError:
27
return None
28
connector = _loop_connectors.get(loop)
29
if connector is None or connector.closed:
30
connector = TCPConnector(
31
limit=100,
32
limit_per_host=20,
33
keepalive_timeout=30,
34
ttl_dns_cache=300,
35
enable_cleanup_closed=True,
36
)
37
_loop_connectors[loop] = connector
38
return connector
39
40
41
async def close_shared_connectors() -> None:
42
"""Close all open shared connectors."""
43
for connector in list(_loop_connectors.values()):
44
if not connector.closed:
45
await connector.close()
46
_loop_connectors.clear()
47
16
48
17
49
class StreamResponse(ClientResponse):
18
50
async def iter_lines(self) -> AsyncIterator[bytes]:
@@ -48,6 +80,7 @@ class StreamSession:
48
80
proxy: str = None,
49
81
proxies=None,
50
82
impersonate=None,
83
connector_owner: bool = None,
51
84
**kwargs,
52
85
):
53
86
if proxies is None:
@@ -65,11 +98,21 @@ class StreamSession:
65
98
timeout = ClientTimeout(timeout, connect)
66
99
if proxy is None:
67
100
proxy = proxies.get("all", proxies.get("https"))
101
102
actual_connector = get_connector(connector, proxy)
103
if actual_connector is None and not proxy:
104
actual_connector = get_shared_connector()
105
if connector_owner is None and actual_connector is not None:
106
connector_owner = False
107
if connector_owner is None:
108
connector_owner = True
109
68
110
self.inner = ClientSession(
69
111
**kwargs,
70
112
timeout=timeout,
71
113
response_class=StreamResponse,
72
connector=get_connector(connector, proxy),
114
connector=actual_connector,
115
connector_owner=connector_owner,
73
116
headers=headers,
74
117
)
75
118