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:
co-authored by
Claude Sonnet 4.5
parent
07f0c5ed5f
commit
ba468f5ca0
+3
-116
@@ -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 注册表读取 < 1ms,TTL 设短;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()
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user