fix: 移除代理自动检测,改用直连模式

- 删除 base_client.py 中的 _detect_proxy/_get_proxy 代理检测逻辑
- 移除请求时动态注入 proxy 参数
- 设置 trust_env=False 避免读取系统/环境变量代理
- 新增 universal_llm 节点「令牌」输入,支持自定义 API Key

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
This commit is contained in:
o1key
2026-04-21 17:52:44 +08:00
co-authored by Claude Sonnet 4.5
parent 07f0c5ed5f
commit ba468f5ca0
3 changed files with 13 additions and 119 deletions
+3 -116
View File
@@ -10,122 +10,11 @@ from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List, Optional from typing import Any, Callable, Dict, List, Optional
import os import os
import platform
import time import time
import aiohttp import aiohttp
def _detect_proxy() -> str | None:
"""
检测当前系统的 HTTP 代理地址。
只读取,不修改任何环境变量或系统设置。
检测顺序:
1. 环境变量 HTTPS_PROXY / HTTP_PROXY(用户/启动脚本已配置时直接用)
2. Windows 注册表 Internet Settings
3. macOS networksetup
Returns:
代理 URL 字符串(如 "http://127.0.0.1:10808"),未检测到返回 None。
"""
# 1. 环境变量优先
for key in ("HTTPS_PROXY", "HTTP_PROXY", "https_proxy", "http_proxy", "ALL_PROXY", "all_proxy"):
val = os.environ.get(key)
if val:
return val
system = platform.system()
# 2. Windows 注册表
if system == "Windows":
try:
import winreg
reg_key = winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
)
enabled = winreg.QueryValueEx(reg_key, "ProxyEnable")[0]
if enabled:
server = winreg.QueryValueEx(reg_key, "ProxyServer")[0]
if server:
# 过滤掉 "http=...;https=..." 多协议格式,取第一个可用地址
if "=" in server:
# 例: "http=127.0.0.1:10808;https=127.0.0.1:10808"
for part in server.split(";"):
if "=" in part:
addr = part.split("=", 1)[1].strip()
if addr:
return f"http://{addr}"
else:
return f"http://{server}"
except Exception:
pass
# 3. macOS networksetup
elif system == "Darwin":
try:
import subprocess
for iface in ("Wi-Fi", "Ethernet", "USB 10/100/1000 LAN"):
for flag, proto in (("-getsecurewebproxy", "https"), ("-getwebproxy", "http"),
("-getsocksfirewallproxy", "socks5")):
out = subprocess.run(
["networksetup", flag, iface],
capture_output=True, text=True, timeout=3,
).stdout
if "Enabled: Yes" in out:
lines = {l.split(":")[0].strip(): l.split(":", 1)[1].strip()
for l in out.splitlines() if ":" in l}
host = lines.get("Server", "")
port = lines.get("Port", "")
if host and port and port != "0":
return f"{proto}://{host}:{port}"
except Exception:
pass
return None
# ── 带 TTL 的代理缓存 ──────────────────────────────────────────────────────────
# Windows 注册表读取 < 1msTTL 设短;macOS 需要子进程,TTL 设长一些。
_PROXY_TTL = 3.0 if platform.system() == "Windows" else 10.0
_proxy_cache_value: str | None = None
_proxy_cache_expires: float = 0.0
_proxy_cache_lock = threading.Lock()
def _get_proxy() -> str | None:
"""
返回当前生效的代理地址(带 TTL 缓存)。
缓存过期后重新检测,代理状态变化时自动打印日志。
"""
global _proxy_cache_value, _proxy_cache_expires
now = time.monotonic()
if now < _proxy_cache_expires: # 缓存命中,零开销
return _proxy_cache_value
with _proxy_cache_lock:
if now < _proxy_cache_expires: # 双重检查,防止并发重复检测
return _proxy_cache_value
new_value = _detect_proxy()
if new_value != _proxy_cache_value:
if new_value:
print("[o1key] 检测到代理已开启")
else:
print("[o1key] 代理已关闭,切换为直连模式")
_proxy_cache_value = new_value
_proxy_cache_expires = time.monotonic() + _PROXY_TTL
return _proxy_cache_value
# 启动时打印一次初始状态
_initial = _get_proxy()
print(f"[o1key] 启动代理检测: {'已开启' if _initial else '未开启,直连模式'}")
class BaseAPIClient(ABC): class BaseAPIClient(ABC):
""" """
@@ -201,7 +90,7 @@ class BaseAPIClient(ABC):
避免因客户端系统缺少根证书导致 SSLCertVerificationError。 避免因客户端系统缺少根证书导致 SSLCertVerificationError。
""" """
connector = aiohttp.TCPConnector(ssl=False, limit=0, limit_per_host=0) connector = aiohttp.TCPConnector(ssl=False, limit=0, limit_per_host=0)
return aiohttp.ClientSession(connector=connector) return aiohttp.ClientSession(connector=connector, trust_env=False)
def get_headers(self, use_bearer_token: bool = False) -> Dict[str, str]: def get_headers(self, use_bearer_token: bool = False) -> Dict[str, str]:
""" """
@@ -310,9 +199,8 @@ class BaseAPIClient(ABC):
) )
async def _do_request(): async def _do_request():
_proxy = _get_proxy()
connect_start = time.time() connect_start = time.time()
async with session.post(url, json=request_body, headers=headers, timeout=_aiohttp_timeout, proxy=_proxy) as response: async with session.post(url, json=request_body, headers=headers, timeout=_aiohttp_timeout) as response:
connect_time = time.time() - connect_start connect_time = time.time() - connect_start
if response.status != 200: if response.status != 200:
@@ -424,9 +312,8 @@ class BaseAPIClient(ABC):
close_session = True close_session = True
try: try:
_proxy = _get_proxy()
_get_start = time.time() _get_start = time.time()
async with session.get(url, headers=headers, proxy=_proxy) as response: async with session.get(url, headers=headers) as response:
_get_elapsed = time.time() - _get_start _get_elapsed = time.time() - _get_start
if response.status != 200: if response.status != 200:
error_text = await response.text() error_text = await response.text()
-2
View File
@@ -22,7 +22,6 @@ SIZE_DISPLAY_MAP = {
"4K": "4096", "4K": "4096",
} }
# 轮询直连容器地址,绕过代理层
POLL_BASE_URL = "https://xrrh7tn08tfgwa8w-8188.container.x-gpu.com" POLL_BASE_URL = "https://xrrh7tn08tfgwa8w-8188.container.x-gpu.com"
@@ -75,7 +74,6 @@ class FluxEditClient:
""" """
size_value = SIZE_DISPLAY_MAP.get(size, size) size_value = SIZE_DISPLAY_MAP.get(size, size)
# 1. 提交任务(走代理)
task_id = self._submit_task_sync(image_bytes, mask_bytes, prompt, size_value) task_id = self._submit_task_sync(image_bytes, mask_bytes, prompt, size_value)
if progress_callback: if progress_callback:
progress_callback(f"任务已提交: {task_id[:8]}...") progress_callback(f"任务已提交: {task_id[:8]}...")
+10 -1
View File
@@ -80,6 +80,11 @@ class UniversalLLMChat:
"图片": ("IMAGE",), "图片": ("IMAGE",),
"视频": ("VIDEO",), "视频": ("VIDEO",),
"文件": ("FILE_LIST",), "文件": ("FILE_LIST",),
"令牌": ("STRING", {
"default": "",
"multiline": False,
"placeholder": "留空则使用默认 API Key",
}),
}, },
"hidden": { "hidden": {
"node_id": "UNIQUE_ID", "node_id": "UNIQUE_ID",
@@ -370,6 +375,7 @@ class UniversalLLMChat:
图片: Optional[torch.Tensor] = None, 图片: Optional[torch.Tensor] = None,
视频=None, 视频=None,
文件: Optional[FileList] = None, 文件: Optional[FileList] = None,
令牌: str = "",
node_id: str = "", node_id: str = "",
) -> Tuple[str]: ) -> Tuple[str]:
start_time = time.time() start_time = time.time()
@@ -377,6 +383,9 @@ class UniversalLLMChat:
try: try:
self._ensure_config() self._ensure_config()
# 如果用户传入了自定义令牌,则覆盖默认 API Key
effective_api_key = 令牌.strip() if 令牌 and 令牌.strip() else self._api_key
# 构建 input # 构建 input
input_data = self._build_input(提示词, 图片, "", 文件, 视频) input_data = self._build_input(提示词, 图片, "", 文件, 视频)
@@ -416,7 +425,7 @@ class UniversalLLMChat:
async def _do_request(): async def _do_request():
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": f"Bearer {self._api_key}", "Authorization": f"Bearer {effective_api_key}",
} }
url = f"{self._base_url}/v1/chat/completions" url = f"{self._base_url}/v1/chat/completions"
timeout = aiohttp.ClientTimeout(total=120) timeout = aiohttp.ClientTimeout(total=120)