feat: optimize logging and fix timeout calculation issues
This commit is contained in:
1 parent
b9c67f972a
commit
b5b817ebcd
8 files changed
+52
-118
No files matched your search
+1
-1
@@ -41,7 +41,7 @@ async def chat_completions(
|
||||
HTTPException: 当请求处理失败时
|
||||
"""
|
||||
try:
|
||||
logger.info(f"[Chat] 聊天请求 - 模型: {request.model}")
|
||||
logger.info(f"[Chat] 收到聊天请求")
|
||||
|
||||
# 调用Grok客户端处理请求
|
||||
result = await GrokClient.openai_to_grok(request.model_dump())
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ class AuthManager:
|
||||
}
|
||||
)
|
||||
|
||||
logger.debug("[Auth] 令牌认证成功。")
|
||||
logger.debug("[Auth] 令牌认证成功")
|
||||
return credentials.credentials
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class CacheService:
|
||||
"""下载并缓存文件"""
|
||||
cache_path = self._cache_path(file_path)
|
||||
if cache_path.exists():
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 文件已缓存: {cache_path}")
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 文件缓存成功")
|
||||
return cache_path
|
||||
|
||||
try:
|
||||
@@ -54,7 +54,7 @@ class CacheService:
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 使用缓存代理: {proxy_url.split('@')[-1] if '@' in proxy_url else proxy_url}")
|
||||
|
||||
async with AsyncSession() as session:
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 开始下载: https://assets.grok.com{file_path}")
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 缓存生成视频文件: https://assets.grok.com{file_path}")
|
||||
response = await session.get(
|
||||
f"https://assets.grok.com{file_path}",
|
||||
headers=headers,
|
||||
@@ -65,7 +65,7 @@ class CacheService:
|
||||
)
|
||||
response.raise_for_status()
|
||||
cache_path.write_bytes(response.content)
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 文件已缓存: {cache_path} ({len(response.content)} bytes)")
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 文件缓存成功")
|
||||
asyncio.create_task(self.cleanup_cache())
|
||||
return cache_path
|
||||
except Exception as e:
|
||||
@@ -88,10 +88,10 @@ class CacheService:
|
||||
total_size = sum(size for _, size, _ in files)
|
||||
|
||||
if total_size <= max_size_bytes:
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 缓存大小 {total_size / 1024 / 1024:.2f}MB,未超限")
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 缓存大小统计: {total_size / 1024 / 1024:.2f}MB,未达到限制")
|
||||
return
|
||||
|
||||
logger.info(f"[{self.cache_type.upper()}Cache] 缓存大小 {total_size / 1024 / 1024:.2f}MB 超过限制 {max_size_mb}MB,开始清理")
|
||||
logger.info(f"[{self.cache_type.upper()}Cache] 清理缓存,当前大小 {total_size / 1024 / 1024:.2f}MB 超过限制 {max_size_mb}MB")
|
||||
files.sort(key=lambda x: x[2])
|
||||
|
||||
for file_path, size, _ in files:
|
||||
@@ -99,9 +99,9 @@ class CacheService:
|
||||
break
|
||||
file_path.unlink()
|
||||
total_size -= size
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 已删除缓存文件: {file_path}")
|
||||
logger.debug(f"[{self.cache_type.upper()}Cache] 清理缓存文件: {file_path}")
|
||||
|
||||
logger.info(f"[{self.cache_type.upper()}Cache] 缓存清理完成,当前大小 {total_size / 1024 / 1024:.2f}MB")
|
||||
logger.info(f"[{self.cache_type.upper()}Cache] 清理缓存完成,当前大小 {total_size / 1024 / 1024:.2f}MB")
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.cache_type.upper()}Cache] 清理缓存失败: {e}")
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ class GrokClient:
|
||||
messages = openai_request["messages"]
|
||||
stream = openai_request.get("stream", False)
|
||||
|
||||
logger.debug(f"[Client] 处理请求 - 模型:{model}, 消息数:{len(messages)}, 流式:{stream}")
|
||||
|
||||
# 提取消息内容和图片URL
|
||||
content, image_urls = GrokClient._extract_content(messages)
|
||||
model_name, model_mode = Models.to_grok(model)
|
||||
@@ -45,7 +43,6 @@ class GrokClient:
|
||||
if len(image_urls) > 1:
|
||||
logger.warning(f"[Client] 视频模型只允许一张图片,当前有{len(image_urls)}张,只使用第一张")
|
||||
image_urls = image_urls[:1]
|
||||
logger.debug(f"[Client] 视频模型文本处理: {content}")
|
||||
|
||||
# 重试逻辑
|
||||
return await GrokClient._try(model, content, image_urls, model_name, model_mode, is_video_model, stream)
|
||||
@@ -63,23 +60,20 @@ class GrokClient:
|
||||
# 上传图片
|
||||
imgs, uris = await GrokClient._upload_imgs(image_urls, auth_token)
|
||||
|
||||
# 视频模型需要额外的create操作
|
||||
# 视频模型 - 创建会话
|
||||
post_id = None
|
||||
if is_video and imgs and uris:
|
||||
logger.debug(f"[Client] 检测到视频模型,执行post create操作")
|
||||
try:
|
||||
create_result = await PostCreateManager.create(imgs[0], uris[0], auth_token)
|
||||
if create_result and create_result.get("success"):
|
||||
post_id = create_result.get("post_id")
|
||||
logger.debug(f"[Client] Post创建成功: {post_id}")
|
||||
else:
|
||||
logger.warning(f"[Client] Post创建失败,继续使用原有流程")
|
||||
logger.warning(f"[Client] 创建会话失败,继续使用原有流程")
|
||||
except Exception as e:
|
||||
logger.warning(f"[Client] Post创建异常: {e},继续使用原有流程")
|
||||
logger.warning(f"[Client] 创建会话异常: {e},继续使用原有流程")
|
||||
|
||||
# 构建并发送请求
|
||||
payload = GrokClient._build_payload(content, model_name, model_mode, imgs, uris, is_video, post_id)
|
||||
logger.debug(f"[Client] 请求载荷配置: {payload}")
|
||||
return await GrokClient._send_request(payload, auth_token, model, stream, post_id)
|
||||
|
||||
except GrokApiException as e:
|
||||
@@ -110,7 +104,7 @@ class GrokClient:
|
||||
for msg in messages:
|
||||
msg_content = msg.get("content", "")
|
||||
|
||||
# 处理复杂消息格式(包含文本和图片)
|
||||
# 处理复杂消息格式
|
||||
if isinstance(msg_content, list):
|
||||
for item in msg_content:
|
||||
item_type = item.get("type")
|
||||
@@ -173,17 +167,14 @@ class GrokClient:
|
||||
"isAsyncChat": False
|
||||
}
|
||||
|
||||
# 视频模型特殊配置
|
||||
# 视频模型配置
|
||||
if is_video_model and image_uris:
|
||||
image_url = image_uris[0]
|
||||
logger.debug(f"[Client] 视频模型图片URL: {image_url}")
|
||||
|
||||
# 根据是否有post_id选择不同的URL格式
|
||||
# 构建 URL 消息
|
||||
if post_id:
|
||||
logger.debug(f"[Client] 使用PostID构建URL: {post_id}")
|
||||
image_message = f"https://grok.com/imagine/{post_id} {content} --mode=custom"
|
||||
else:
|
||||
logger.debug(f"[Client] 使用文件URI构建URL: {image_url}")
|
||||
image_message = f"https://assets.grok.com/post/{image_url} {content} --mode=custom"
|
||||
|
||||
payload = {
|
||||
@@ -193,8 +184,6 @@ class GrokClient:
|
||||
"fileAttachments": image_attachments,
|
||||
"toolOverrides": {"videoGen": True}
|
||||
}
|
||||
logger.debug(f"[Client] 视频模型载荷配置: {payload}")
|
||||
logger.debug("[Client] 视频模型载荷配置: toolOverrides.videoGen = True")
|
||||
|
||||
return payload
|
||||
|
||||
@@ -209,19 +198,15 @@ class GrokClient:
|
||||
# 构建请求头
|
||||
headers = GrokClient._build_headers(auth_token)
|
||||
if model == "grok-imagine-0.9":
|
||||
# 优先使用传入的post_id,否则使用fileAttachments中的第一个
|
||||
# 传入会话ID
|
||||
referer_id = post_id if post_id else payload.get("fileAttachments", [""])[0]
|
||||
if referer_id:
|
||||
headers["Referer"] = f"https://grok.com/imagine/{referer_id}"
|
||||
logger.debug(f"[Client] 设置Referer: {headers['Referer']}")
|
||||
|
||||
# 使用服务代理
|
||||
proxy_url = setting.get_service_proxy()
|
||||
proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None
|
||||
|
||||
if proxy_url:
|
||||
logger.debug(f"[Client] 使用服务代理: {proxy_url.split('@')[-1] if '@' in proxy_url else proxy_url}")
|
||||
|
||||
# 构建请求参数
|
||||
request_kwargs = {
|
||||
"headers": headers,
|
||||
@@ -239,8 +224,6 @@ class GrokClient:
|
||||
**request_kwargs
|
||||
)
|
||||
|
||||
logger.debug(f"[Client] API响应状态码: {response.status_code}")
|
||||
|
||||
# 处理非成功响应
|
||||
if response.status_code != 200:
|
||||
GrokClient._handle_error(response, auth_token)
|
||||
|
||||
+11
-46
@@ -17,15 +17,15 @@ IMPERSONATE_BROWSER = "chrome133a"
|
||||
|
||||
class PostCreateManager:
|
||||
"""
|
||||
Grok Post创建管理器
|
||||
Grok 会话创建管理器
|
||||
|
||||
提供图片post创建功能,用于视频生成前的准备工作
|
||||
提供图片会话创建功能,用于视频生成前的准备工作
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
async def create(file_id: str, file_uri: str, auth_token: str) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
创建post记录
|
||||
创建会话记录
|
||||
|
||||
Args:
|
||||
file_id: 上传后的文件ID
|
||||
@@ -33,18 +33,17 @@ class PostCreateManager:
|
||||
auth_token: 认证令牌
|
||||
|
||||
Returns:
|
||||
创建的post信息,包含post_id等
|
||||
创建的会话信息,包含会话ID等
|
||||
"""
|
||||
try:
|
||||
# 验证参数
|
||||
if not file_id or not file_uri:
|
||||
raise GrokApiException("文件ID或URI缺失", "INVALID_PARAMS")
|
||||
raise GrokApiException("会话ID或URI缺失", "INVALID_PARAMS")
|
||||
|
||||
if not auth_token:
|
||||
raise GrokApiException("认证令牌缺失", "NO_AUTH_TOKEN")
|
||||
|
||||
# 构建创建数据
|
||||
# 构建完整的 media_url
|
||||
media_url = f"https://assets.grok.com/{file_uri}"
|
||||
|
||||
create_data = {
|
||||
@@ -52,17 +51,12 @@ class PostCreateManager:
|
||||
"media_type": "MEDIA_POST_TYPE_IMAGE"
|
||||
}
|
||||
|
||||
logger.debug(f"[PostCreate] 创建post请求: fileId={file_id}, uri={file_uri}, media_url={media_url}")
|
||||
|
||||
# 获取认证令牌和cookie
|
||||
cf_clearance = setting.grok_config.get("cf_clearance", "")
|
||||
cookie = f"{auth_token};{cf_clearance}" if cf_clearance else auth_token
|
||||
|
||||
# 获取代理配置
|
||||
proxy_url = setting.grok_config.get("proxy_url", "")
|
||||
if proxy_url:
|
||||
logger.debug(f"[PostCreate] 使用代理: {proxy_url.split('@')[-1] if '@' in proxy_url else proxy_url}")
|
||||
|
||||
proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None
|
||||
|
||||
# 发送异步请求
|
||||
@@ -82,8 +76,8 @@ class PostCreateManager:
|
||||
# 检查响应
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
post_id = result.get("postId", "")
|
||||
logger.debug(f"[PostCreate] Post创建成功,PostID: {post_id}")
|
||||
post_id = result.get("post", {}).get("id", "")
|
||||
logger.debug(f"[PostCreate] 创建会话成功,会话ID: {post_id}")
|
||||
return {
|
||||
"post_id": post_id,
|
||||
"file_id": file_id,
|
||||
@@ -99,40 +93,11 @@ class PostCreateManager:
|
||||
except:
|
||||
error_msg = f"{error_msg}, 详情: {response.text[:200]}"
|
||||
|
||||
logger.error(f"[PostCreate] Post创建失败: {error_msg}")
|
||||
raise GrokApiException(f"Post创建失败: {error_msg}", "CREATE_ERROR")
|
||||
logger.error(f"[PostCreate] 创建会话失败: {error_msg}")
|
||||
raise GrokApiException(f"创建会话失败: {error_msg}", "CREATE_ERROR")
|
||||
|
||||
except GrokApiException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[PostCreate] Post创建异常: {e}")
|
||||
raise GrokApiException(f"Post创建异常: {e}", "CREATE_ERROR") from e
|
||||
|
||||
@staticmethod
|
||||
async def batch_create(file_list: list, auth_token: str) -> list:
|
||||
"""
|
||||
批量创建post记录
|
||||
|
||||
Args:
|
||||
file_list: 文件列表,每个元素包含 (file_id, file_uri)
|
||||
auth_token: 认证令牌
|
||||
|
||||
Returns:
|
||||
创建结果列表
|
||||
"""
|
||||
results = []
|
||||
for file_id, file_uri in file_list:
|
||||
try:
|
||||
result = await PostCreateManager.create(file_id, file_uri, auth_token)
|
||||
results.append(result)
|
||||
except Exception as e:
|
||||
logger.warning(f"[PostCreate] 批量创建失败: fileId={file_id}, 错误={e}")
|
||||
results.append({
|
||||
"file_id": file_id,
|
||||
"file_uri": file_uri,
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
logger.error(f"[PostCreate] 创建会话异常: {e}")
|
||||
raise GrokApiException(f"创建会话异常: {e}", "CREATE_ERROR") from e
|
||||
@@ -269,7 +269,7 @@ class GrokResponseProcessor:
|
||||
yield "data: [DONE]\n\n"
|
||||
return
|
||||
|
||||
logger.debug(f"[Processor] 接收到数据块: {chunk} \n\n")
|
||||
logger.debug(f"[Processor] 接收到数据块, 长度: {len(chunk)} bytes")
|
||||
if not chunk:
|
||||
continue
|
||||
|
||||
@@ -286,9 +286,12 @@ class GrokResponseProcessor:
|
||||
|
||||
# 提取响应数据
|
||||
grok_resp = data.get("result", {}).get("response", {})
|
||||
logger.debug(f"[Processor] 解析响应数据: {grok_resp} \n\n")
|
||||
logger.debug(f"[Processor] 解析响应数据, 长度: {len(grok_resp)} bytes")
|
||||
if not grok_resp:
|
||||
continue
|
||||
|
||||
# 更新超时计时器
|
||||
timeout_manager.mark_chunk_received()
|
||||
|
||||
# 更新模型名称
|
||||
if user_resp := grok_resp.get("userResponse"):
|
||||
@@ -300,27 +303,28 @@ class GrokResponseProcessor:
|
||||
progress = video_resp.get("progress", 0)
|
||||
v_url = video_resp.get("videoUrl")
|
||||
|
||||
# 处理进度更新(仅当进度增加时)
|
||||
# 处理进度更新
|
||||
if progress > last_video_progress:
|
||||
last_video_progress = progress
|
||||
|
||||
# 添加 <think> 标签
|
||||
if not video_progress_started:
|
||||
content = f"<think>视频已生成{progress}%\n"
|
||||
video_progress_started = True
|
||||
elif progress < 100:
|
||||
content = f"视频已生成{progress}%\n"
|
||||
else:
|
||||
# 进度100%时关闭 <think> 标签
|
||||
content = f"视频已生成{progress}%</think>\n"
|
||||
# 思考状态
|
||||
if show_thinking:
|
||||
# 添加 <think> 标签
|
||||
if not video_progress_started:
|
||||
content = f"<think>视频已生成{progress}%\n"
|
||||
video_progress_started = True
|
||||
elif progress < 100:
|
||||
content = f"视频已生成{progress}%\n"
|
||||
else:
|
||||
# 关闭 <think> 标签
|
||||
content = f"视频已生成{progress}%</think>\n"
|
||||
|
||||
yield make_chunk(content)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
yield make_chunk(content)
|
||||
chunk_index += 1
|
||||
|
||||
# 处理视频URL(单独判断,不依赖进度)
|
||||
# 处理视频URL
|
||||
if v_url:
|
||||
logger.debug(f"[Processor] 视频生成完成: {v_url}")
|
||||
logger.debug(f"[Processor] 视频生成完成")
|
||||
full_video_url = f"https://assets.grok.com/{v_url}"
|
||||
|
||||
try:
|
||||
@@ -337,7 +341,6 @@ class GrokResponseProcessor:
|
||||
video_content = f'<video src="{full_video_url}" controls="controls"></video>\n'
|
||||
|
||||
yield make_chunk(video_content)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
|
||||
continue
|
||||
@@ -378,7 +381,6 @@ class GrokResponseProcessor:
|
||||
|
||||
# 发送前缀
|
||||
yield make_chunk(markdown_prefix + mime_part)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
|
||||
# 分块发送 base64 数据
|
||||
@@ -386,24 +388,19 @@ class GrokResponseProcessor:
|
||||
for i in range(0, len(b64_data), chunk_size):
|
||||
chunk_data = b64_data[i:i + chunk_size]
|
||||
yield make_chunk(chunk_data)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
|
||||
# 发送后缀
|
||||
yield make_chunk(markdown_suffix)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
else:
|
||||
yield make_chunk(f"\n")
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
else:
|
||||
yield make_chunk(f"\n")
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
else:
|
||||
yield make_chunk(f"\n")
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
else:
|
||||
# url 模式:缓存并返回链接
|
||||
@@ -419,11 +416,9 @@ class GrokResponseProcessor:
|
||||
|
||||
# 发送内容
|
||||
yield make_chunk(content.strip(), "stop")
|
||||
timeout_manager.mark_chunk_received()
|
||||
return
|
||||
elif token:
|
||||
yield make_chunk(token)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
|
||||
# 提取对话数据
|
||||
@@ -495,7 +490,6 @@ class GrokResponseProcessor:
|
||||
# 只在不需要跳过时才发送
|
||||
if not should_skip:
|
||||
yield make_chunk(content)
|
||||
timeout_manager.mark_chunk_received()
|
||||
chunk_index += 1
|
||||
|
||||
is_thinking = current_is_thinking
|
||||
@@ -517,7 +511,7 @@ class GrokResponseProcessor:
|
||||
logger.info(f"[Processor] 流式响应完成,总耗时: {timeout_manager.get_total_duration():.2f}秒")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[Processor] 流式处理严重错误: {e}")
|
||||
logger.error(f"[Processor] 流式处理发生严重错误: {e}")
|
||||
yield make_chunk(f"处理错误: {e}", "error")
|
||||
# 发送流结束标记
|
||||
yield "data: [DONE]\n\n"
|
||||
|
||||
@@ -272,14 +272,13 @@ class GrokTokenManager:
|
||||
)
|
||||
|
||||
status_text = "未使用" if max_remaining == -1 else f"剩余{max_remaining}次"
|
||||
logger.debug(f"[Token] 为模型 {model} 选择Token ({status_text})")
|
||||
logger.debug(f"[Token] 正在为模型 {model} 分配Token ({status_text})")
|
||||
return max_token_key
|
||||
|
||||
async def check_limits(self, auth_token: str, model: str) -> Optional[Dict[str, Any]]:
|
||||
"""检查并更新模型速率限制"""
|
||||
try:
|
||||
rate_limit_model_name = Models.to_rate_limit(model)
|
||||
logger.debug(f"[Token] 检查模型 {model} (接口模型: {rate_limit_model_name}) 的速率限制")
|
||||
|
||||
# 准备请求
|
||||
payload = {"requestKind": "DEFAULT", "modelName": rate_limit_model_name}
|
||||
@@ -293,9 +292,6 @@ class GrokTokenManager:
|
||||
proxy_url = setting.grok_config.get("proxy_url", "")
|
||||
proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None
|
||||
|
||||
if proxy_url:
|
||||
logger.debug(f"[Token] 使用代理: {proxy_url.split('@')[-1] if '@' in proxy_url else proxy_url}")
|
||||
|
||||
# 发送异步请求
|
||||
async with AsyncSession() as session:
|
||||
response = await session.post(
|
||||
@@ -309,17 +305,16 @@ class GrokTokenManager:
|
||||
|
||||
if response.status_code == 200:
|
||||
rate_limit_data = response.json()
|
||||
logger.debug(f"[Token] 成功获取速率限制信息")
|
||||
|
||||
# 保存速率限制信息
|
||||
sso_value = self._extract_sso(auth_token)
|
||||
if sso_value:
|
||||
if model == "grok-4-heavy":
|
||||
await self.update_limits(sso_value, normal=None, heavy=rate_limit_data.get("remainingQueries", -1))
|
||||
logger.info(f"[Token] 已更新限制: sso={sso_value[:10]}..., heavy={rate_limit_data.get('remainingQueries', -1)}")
|
||||
logger.info(f"[Token] 更新 Token 限制: sso={sso_value[:10]}..., heavy={rate_limit_data.get('remainingQueries', -1)}")
|
||||
else:
|
||||
await self.update_limits(sso_value, normal=rate_limit_data.get("remainingTokens", -1), heavy=None)
|
||||
logger.info(f"[Token] 已更新限制: sso={sso_value[:10]}..., 通用={rate_limit_data.get('remainingTokens', -1)}")
|
||||
logger.info(f"[Token] 更新 Token 限制: sso={sso_value[:10]}..., basic={rate_limit_data.get('remainingTokens', -1)}")
|
||||
|
||||
return rate_limit_data
|
||||
else:
|
||||
|
||||
@@ -63,9 +63,6 @@ class ImageUploadManager:
|
||||
cookie = f"{auth_token};{cf_clearance}" if cf_clearance else auth_token
|
||||
|
||||
proxy_url = setting.grok_config.get("proxy_url", "")
|
||||
if proxy_url:
|
||||
logger.debug(f"[Upload] 使用代理: {proxy_url.split('@')[-1] if '@' in proxy_url else proxy_url}")
|
||||
|
||||
proxies = {"http": proxy_url, "https": proxy_url} if proxy_url else None
|
||||
|
||||
# 发送异步请求
|
||||
|
||||
Reference in new issue
Block a user