修复bug

This commit is contained in:
dekun
2026-05-19 02:16:43 +08:00
parent 809a7cc38f
commit b454cf134f
4 changed files with 27 additions and 13 deletions
+5 -1
View File
@@ -54,7 +54,7 @@ apt install -y python3 python3-venv python3-pip git curl
| 组件 | 版本建议 | 用途 |
|------|----------|------|
| Python | 3.10+`python3 -V` 确认) | 运行网关 |
| Python | **3.8+**(推荐 3.10+`python3 -V` 确认) | 运行网关 |
| Node.js | LTS | 安装 PM2 |
| PM2 | 最新 | 进程守护 |
| 宝塔 | 7.x+ | Nginx 反代、SSL |
@@ -98,6 +98,8 @@ cd /opt/openai_node
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt -U
# 若曾装过新版 bcrypt 导致 passlib 报错,可强制重装:
# pip install 'bcrypt>=4.0.0,<4.1.0' -U --force-reinstall
```
### 3.3 配置文件
@@ -364,6 +366,8 @@ Web 保存节点配置会写入 `nodes.json` 并热加载;仅改 `ecosystem.co
| 统计 IP 不对 | 宝塔是否传递 `X-Forwarded-For`(§5.2 |
| Token 为 0 | 上游 `usage`;流式 `include_usage` |
| PM2 找不到模块 | 确认使用 `/opt/openai_node/venv` 内 Python |
| `asyncio` 无 `to_thread` | Python 3.8:拉最新代码(已用 `run_in_thread` 兼容) |
| bcrypt `__about__` 报错 | `pip install 'bcrypt>=4.0.0,<4.1.0' -U --force-reinstall` 后重启 PM2 |
---
Binary file not shown.
+21 -11
View File
@@ -27,7 +27,7 @@ import threading
from contextlib import asynccontextmanager
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from typing import Any, AsyncIterator, Dict, List, Optional, Tuple
from typing import Any, AsyncIterator, Callable, Dict, List, Optional, Tuple, TypeVar
try:
from typing import Annotated # Python 3.9+
@@ -35,6 +35,16 @@ except ImportError: # Python 3.8
from typing_extensions import Annotated
import httpx
_T = TypeVar("_T")
async def run_in_thread(func: Callable[..., _T], /, *args: Any) -> _T:
"""在线程池执行阻塞函数(兼容 Python 3.8,无 asyncio.to_thread)。"""
if hasattr(asyncio, "to_thread"):
return await run_in_thread(func, *args) # type: ignore[attr-defined]
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, func, *args)
from fastapi import Depends, FastAPI, HTTPException, Query, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse, JSONResponse, StreamingResponse
@@ -634,7 +644,7 @@ async def record_api_log(
usage: Dict[str, int],
node_id: Optional[str] = None,
) -> None:
await asyncio.to_thread(
await run_in_thread(
_record_api_log_sync,
client_ip,
model,
@@ -1541,14 +1551,14 @@ async def api_me(
@app.get("/api/models/cards")
async def api_model_cards() -> List[Dict[str, Any]]:
return await asyncio.to_thread(build_model_cards)
return await run_in_thread(build_model_cards)
@app.get("/api/nodes")
async def api_nodes(
_: Annotated[GateSessionUser, Depends(get_current_web_user)],
) -> List[Dict[str, Any]]:
return await asyncio.to_thread(list_nodes_with_status)
return await run_in_thread(list_nodes_with_status)
@app.post("/api/nodes")
@@ -1566,7 +1576,7 @@ async def api_nodes_create(
"models": [{"id": m.id.strip(), "label": (m.label or m.id).strip()} for m in body.models],
}
nodes = list(_NODES.get("nodes", [])) + [node]
await asyncio.to_thread(save_nodes_config, {"nodes": nodes})
await run_in_thread(save_nodes_config, {"nodes": nodes})
return node
@@ -1589,7 +1599,7 @@ async def api_nodes_update(
"max_concurrent": body.max_concurrent,
"models": [{"id": m.id.strip(), "label": (m.label or m.id).strip()} for m in body.models],
}
await asyncio.to_thread(save_nodes_config, {"nodes": nodes})
await run_in_thread(save_nodes_config, {"nodes": nodes})
return nodes[idx]
@@ -1601,7 +1611,7 @@ async def api_nodes_delete(
nodes = [n for n in _NODES.get("nodes", []) if str(n.get("id")) != node_id]
if len(nodes) == len(_NODES.get("nodes", [])):
raise HTTPException(status_code=404, detail="节点不存在")
await asyncio.to_thread(save_nodes_config, {"nodes": nodes})
await run_in_thread(save_nodes_config, {"nodes": nodes})
return JSONResponse({"ok": True})
@@ -1628,14 +1638,14 @@ async def api_nodes_test(
async def api_stats_summary(
_: Annotated[GateSessionUser, Depends(get_current_web_user)],
) -> Dict[str, Any]:
return await asyncio.to_thread(_query_stats_summary)
return await run_in_thread(_query_stats_summary)
@app.get("/api/stats/ips")
async def api_stats_ips(
_: Annotated[GateSessionUser, Depends(get_current_web_user)],
) -> List[Dict[str, Any]]:
return await asyncio.to_thread(_query_stats_ips)
return await run_in_thread(_query_stats_ips)
@app.get("/api/stats/billing")
@@ -1643,7 +1653,7 @@ async def api_stats_billing(
_: Annotated[GateSessionUser, Depends(get_current_web_user)],
days: int = Query(30, ge=1, le=365),
) -> Dict[str, Any]:
return await asyncio.to_thread(_query_stats_billing, days)
return await run_in_thread(_query_stats_billing, days)
@app.get("/api/stats/logs")
@@ -1652,7 +1662,7 @@ async def api_stats_logs(
limit: int = Query(50, ge=1, le=500),
offset: int = Query(0, ge=0),
) -> Dict[str, Any]:
items, total = await asyncio.to_thread(_query_stats_logs, limit, offset)
items, total = await run_in_thread(_query_stats_logs, limit, offset)
return {"items": items, "total": total, "limit": limit, "offset": offset}
+1 -1
View File
@@ -2,6 +2,6 @@ typing_extensions>=4.5.0
fastapi>=0.110.0
uvicorn[standard]>=0.27.0
passlib[bcrypt]>=1.7.4
bcrypt>=4.0.0,<5.0.0
bcrypt>=4.0.0,<4.1.0
python-jose[cryptography]>=3.3.0
httpx>=0.27.0