yym68686 commited on
Commit
9871738
·
1 Parent(s): 09cfbba

✨ Feature: Add feature: Support socks5 proxy

Browse files
Files changed (4) hide show
  1. README.md +1 -0
  2. README_CN.md +1 -0
  3. main.py +54 -7
  4. requirements.txt +1 -0
README.md CHANGED
@@ -101,6 +101,7 @@ providers:
101
  gemini-1.5-pro: 10 # Model gemini-1.5-pro timeout is 10 seconds
102
  gemini-1.5-flash: 10 # Model gemini-1.5-flash timeout is 10 seconds
103
  default: 10 # Model does not have a timeout set, use the default timeout of 10 seconds, when requesting a model not in model_timeout, the timeout is also 10 seconds, if default is not set, uni-api will use the default timeout set by the environment variable TIMEOUT, the default timeout is 100 seconds
 
104
 
105
  - provider: vertex
106
  project_id: gen-lang-client-xxxxxxxxxxxxxx # Description: Your Google Cloud project ID. Format: String, usually composed of lowercase letters, numbers, and hyphens. How to obtain: You can find your project ID in the project selector of the Google Cloud Console.
 
101
  gemini-1.5-pro: 10 # Model gemini-1.5-pro timeout is 10 seconds
102
  gemini-1.5-flash: 10 # Model gemini-1.5-flash timeout is 10 seconds
103
  default: 10 # Model does not have a timeout set, use the default timeout of 10 seconds, when requesting a model not in model_timeout, the timeout is also 10 seconds, if default is not set, uni-api will use the default timeout set by the environment variable TIMEOUT, the default timeout is 100 seconds
104
+ proxy: socks5://[username]:[password]@[ip]:[port] # Proxy address, optional. Supports socks5 and http proxies, default is not used.
105
 
106
  - provider: vertex
107
  project_id: gen-lang-client-xxxxxxxxxxxxxx # Description: Your Google Cloud project ID. Format: String, usually composed of lowercase letters, numbers, and hyphens. How to obtain: You can find your project ID in the project selector of the Google Cloud Console.
README_CN.md CHANGED
@@ -101,6 +101,7 @@ providers:
101
  gemini-1.5-pro: 10 # 模型 gemini-1.5-pro 的超时时间为 10 秒
102
  gemini-1.5-flash: 10 # 模型 gemini-1.5-flash 的超时时间为 10 秒
103
  default: 10 # 模型没有设置超时时间,使用默认的超时时间 10 秒,当请求的不在 model_timeout 里面的模型时,超时时间默认是 10 秒,不设置 default,uni-api 会使用全局配置的模型超时时间。
 
104
 
105
  - provider: vertex
106
  project_id: gen-lang-client-xxxxxxxxxxxxxx # 描述: 您的Google Cloud项目ID。格式: 字符串,通常由小写字母、数字和连字符组成。获取方式: 在Google Cloud Console的项目选择器中可以找到您的项目ID。
 
101
  gemini-1.5-pro: 10 # 模型 gemini-1.5-pro 的超时时间为 10 秒
102
  gemini-1.5-flash: 10 # 模型 gemini-1.5-flash 的超时时间为 10 秒
103
  default: 10 # 模型没有设置超时时间,使用默认的超时时间 10 秒,当请求的不在 model_timeout 里面的模型时,超时时间默认是 10 秒,不设置 default,uni-api 会使用全局配置的模型超时时间。
104
+ proxy: socks5://[用户名]:[密码]@[IP地址]:[端口] # 代理地址,选填。支持 socks5 和 http 代理,默认不使用代理。
105
 
106
  - provider: vertex
107
  project_id: gen-lang-client-xxxxxxxxxxxxxx # 描述: 您的Google Cloud项目ID。格式: 字符串,通常由小写字母、数字和连字符组成。获取方式: 在Google Cloud Console的项目选择器中可以找到您的项目ID。
main.py CHANGED
@@ -600,7 +600,7 @@ class ClientManager:
600
  self.default_config = default_config
601
 
602
  @asynccontextmanager
603
- async def get_client(self, timeout_value):
604
  # 直接获取或创建客户端,不使用锁
605
  timeout_value = int(timeout_value)
606
  if timeout_value not in self.clients:
@@ -610,11 +610,42 @@ class ClientManager:
610
  write=30.0,
611
  pool=self.pool_size
612
  )
613
- self.clients[timeout_value] = httpx.AsyncClient(
614
- timeout=timeout,
615
- limits=httpx.Limits(max_connections=self.pool_size),
616
- **self.default_config
617
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
  try:
620
  yield self.clients[timeout_value]
@@ -795,8 +826,24 @@ async def process_request(request: Union[RequestModel, ImageGenerationRequest, A
795
  timeout_value = app.state.timeouts.get("default", DEFAULT_TIMEOUT)
796
  # print("timeout_value", timeout_value)
797
 
 
 
 
798
  try:
799
- async with app.state.client_manager.get_client(timeout_value) as client:
 
 
 
 
 
 
 
 
 
 
 
 
 
800
  if request.stream:
801
  generator = fetch_response_stream(client, url, headers, payload, engine, original_model)
802
  wrapped_generator, first_response_time = await error_handling_wrapper(generator, channel_id)
 
600
  self.default_config = default_config
601
 
602
  @asynccontextmanager
603
+ async def get_client(self, timeout_value, proxy=None):
604
  # 直接获取或创建客户端,不使用锁
605
  timeout_value = int(timeout_value)
606
  if timeout_value not in self.clients:
 
610
  write=30.0,
611
  pool=self.pool_size
612
  )
613
+ limits = httpx.Limits(max_connections=self.pool_size)
614
+
615
+ client_config = {
616
+ **self.default_config,
617
+ "timeout": timeout,
618
+ "limits": limits
619
+ }
620
+
621
+ if proxy:
622
+ # 解析代理URL
623
+ from urllib.parse import urlparse
624
+ parsed = urlparse(proxy)
625
+
626
+ # 修改这里: 将 socks5h 转换为 socks5
627
+ scheme = parsed.scheme.rstrip('h')
628
+ # print("scheme", scheme)
629
+
630
+ if scheme == 'socks5':
631
+ try:
632
+ from httpx_socks import AsyncProxyTransport
633
+ # 使用修改后的scheme创建代理URL
634
+ proxy = proxy.replace('socks5h://', 'socks5://')
635
+ # 创建SOCKS5代理传输
636
+ transport = AsyncProxyTransport.from_url(proxy)
637
+ client_config["transport"] = transport
638
+ except ImportError:
639
+ logger.error("httpx-socks package is required for SOCKS proxy support")
640
+ raise ImportError("Please install httpx-socks package for SOCKS proxy support: pip install httpx-socks")
641
+ else:
642
+ # 对于HTTP/HTTPS代理使用原有方式
643
+ client_config["proxies"] = {
644
+ "http://": proxy,
645
+ "https://": proxy
646
+ }
647
+
648
+ self.clients[timeout_value] = httpx.AsyncClient(**client_config)
649
 
650
  try:
651
  yield self.clients[timeout_value]
 
826
  timeout_value = app.state.timeouts.get("default", DEFAULT_TIMEOUT)
827
  # print("timeout_value", timeout_value)
828
 
829
+ proxy = safe_get(provider, "preferences", "proxy", default=None)
830
+ # print("proxy", proxy)
831
+
832
  try:
833
+ async with app.state.client_manager.get_client(timeout_value, proxy) as client:
834
+ # 打印client配置信息
835
+ # logger.info(f"Client config - Timeout: {client.timeout}")
836
+ # logger.info(f"Client config - Headers: {client.headers}")
837
+ # if hasattr(client, '_transport'):
838
+ # if hasattr(client._transport, 'proxy_url'):
839
+ # logger.info(f"Client config - Proxy: {client._transport.proxy_url}")
840
+ # elif hasattr(client._transport, 'proxies'):
841
+ # logger.info(f"Client config - Proxies: {client._transport.proxies}")
842
+ # else:
843
+ # logger.info("Client config - No proxy configured")
844
+ # else:
845
+ # logger.info("Client config - No transport configured")
846
+ # logger.info(f"Client config - Follow Redirects: {client.follow_redirects}")
847
  if request.stream:
848
  generator = fetch_response_stream(client, url, headers, payload, engine, original_model)
849
  wrapped_generator, first_response_time = await error_handling_wrapper(generator, channel_id)
requirements.txt CHANGED
@@ -9,5 +9,6 @@ sqlalchemy
9
  watchfiles
10
  ruamel.yaml
11
  httpx[http2]
 
12
  cryptography
13
  python-multipart
 
9
  watchfiles
10
  ruamel.yaml
11
  httpx[http2]
12
+ httpx-socks
13
  cryptography
14
  python-multipart