wip: 修复了serve的部分bug

This commit is contained in:
朝夕 2026-04-24 12:48:17 +08:00
parent 27a71c9e49
commit dcf53524b2
4 changed files with 108 additions and 95 deletions

View File

@ -10,8 +10,6 @@ services:
POSTGRES_DB: pretor
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d pretor"]
interval: 5s
@ -34,9 +32,6 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=pretor
- SECRET_KEY=changethiskey12345
volumes:
- .:/app
- /app/frontend/dist # Prevent local uncompiled frontend from overriding the built one
volumes:
postgres_data:

12
main.py
View File

@ -10,20 +10,26 @@ from pretor.core.individual.control_node.control_node import ControlNode
from pretor.core.workflow.workflow_runner import WorkflowRunningEngine
from pretor.core.api import PretorGateway
from ray import serve
import os
async def start_system():
# 1. 初始化 Ray
db_host = os.getenv("POSTGRES_HOST", "db")
env_vars = {
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "postgres",
"POSTGRES_HOST": "127.0.0.1",
"POSTGRES_HOST": db_host,
"POSTGRES_PORT": "5432",
"POSTGRES_DB": "postgres",
"SECRET_KEY": "yoursecretkey"
}
ray.init(ignore_reinit_error=True, runtime_env={"env_vars": env_vars})
ray.init(ignore_reinit_error=True,
dashboard_host="0.0.0.0",
dashboard_port=8265,
runtime_env={"env_vars": env_vars})
# 2. 启动数据库组件

View File

@ -26,11 +26,11 @@ async def update_cluster_state(websocket: WebSocket):
nodes = ray.nodes()
payload = [
{
"node_id": node["NodeID"],
"node_name": node["NodeName"],
"alive": node["Alive"],
"resources": node["Resources"],
"remaining": node["RemainingResources"]
"node_id": node.get("NodeID"),
"node_name": node.get("NodeName"),
"alive": node.get("Alive"),
"resources": node.get("Resources", {}),
"remaining": node.get("RemainingResources", {})
}
for node in nodes
]

View File

@ -12,104 +12,116 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Dict
from fastapi import FastAPI,WebSocket
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
from typing import Dict
from fastapi import FastAPI, WebSocket, Request
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from pretor.api.platform.frontend import client_router
from pretor.api.auth import auth_router
from pretor.api.provider import provider_router
from pretor.api.resource import resource_router
from pretor.api.cluster import cluster_router
from pretor.api.agent import agent_router
from fastapi.responses import JSONResponse
from fastapi import Request
from pretor.utils.error import (
DemandError,
ModelNotExistError,
UserError,
UserNotExistError,
UserPasswordError,
ProviderError,
ProviderNotExistError,
WorkflowError,
WorkflowExit
DemandError, ModelNotExistError, UserError,
UserNotExistError, UserPasswordError, ProviderError,
ProviderNotExistError, WorkflowError, WorkflowExit
)
from ray import serve
app = FastAPI()
@serve.deployment
@serve.ingress(app)
class PretorGateway:
gateway: Dict[str, WebSocket]
def __init__(self):
self.gateway = {}
app.include_router(client_router) # 客户端路径
app.include_router(auth_router) # 用户路径
app.include_router(provider_router) # 供应商路径
app.include_router(resource_router) # 资源路径
app.include_router(cluster_router) # 集群信息路径
app.include_router(agent_router) # agent路径
app.include_router(client_router)#客户端路径
app.include_router(auth_router)#用户路径
app.include_router(provider_router)#供应商路径
app.include_router(resource_router)#资源路径
app.include_router(cluster_router)#集群信息路径
app.include_router(agent_router)#agent路径
@app.exception_handler(UserNotExistError)
async def user_not_exist_handler(request: Request, exc: UserNotExistError):
@app.exception_handler(UserNotExistError)
async def user_not_exist_handler(request: Request, exc: UserNotExistError):
return JSONResponse(status_code=404, content={"message": "用户不存在"})
@app.exception_handler(UserPasswordError)
async def user_password_handler(request: Request, exc: UserPasswordError):
@app.exception_handler(UserPasswordError)
async def user_password_handler(request: Request, exc: UserPasswordError):
return JSONResponse(status_code=401, content={"message": "密码错误"})
@app.exception_handler(UserError)
async def user_error_handler(request: Request, exc: UserError):
@app.exception_handler(UserError)
async def user_error_handler(request: Request, exc: UserError):
return JSONResponse(status_code=400, content={"message": "用户相关错误"})
@app.exception_handler(ProviderNotExistError)
async def provider_not_exist_handler(request: Request, exc: ProviderNotExistError):
@app.exception_handler(ProviderNotExistError)
async def provider_not_exist_handler(request: Request, exc: ProviderNotExistError):
return JSONResponse(status_code=404, content={"message": "服务提供商不存在"})
@app.exception_handler(ProviderError)
async def provider_error_handler(request: Request, exc: ProviderError):
@app.exception_handler(ProviderError)
async def provider_error_handler(request: Request, exc: ProviderError):
return JSONResponse(status_code=400, content={"message": "服务提供商错误"})
@app.exception_handler(ModelNotExistError)
async def model_not_exist_handler(request: Request, exc: ModelNotExistError):
@app.exception_handler(ModelNotExistError)
async def model_not_exist_handler(request: Request, exc: ModelNotExistError):
return JSONResponse(status_code=404, content={"message": "模型不存在"})
@app.exception_handler(DemandError)
async def demand_error_handler(request: Request, exc: DemandError):
@app.exception_handler(DemandError)
async def demand_error_handler(request: Request, exc: DemandError):
return JSONResponse(status_code=400, content={"message": "需求格式错误或不满足"})
@app.exception_handler(WorkflowExit)
async def workflow_exit_handler(request: Request, exc: WorkflowExit):
@app.exception_handler(WorkflowExit)
async def workflow_exit_handler(request: Request, exc: WorkflowExit):
return JSONResponse(status_code=400, content={"message": "工作流已退出"})
@app.exception_handler(WorkflowError)
async def workflow_error_handler(request: Request, exc: WorkflowError):
@app.exception_handler(WorkflowError)
async def workflow_error_handler(request: Request, exc: WorkflowError):
return JSONResponse(status_code=500, content={"message": "工作流执行错误"})
frontend_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), "frontend", "dist")
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
frontend_dir = os.path.join(base_dir, "frontend", "dist")
if os.path.exists(frontend_dir):
if os.path.exists(frontend_dir):
app.mount("/assets", StaticFiles(directory=os.path.join(frontend_dir, "assets")), name="assets")
# Serve favicon and other top-level static files if they exist
@app.get("/favicon.svg", include_in_schema=False)
async def serve_favicon():
return FileResponse(os.path.join(frontend_dir, "favicon.svg"))
@app.get("/icons.svg", include_in_schema=False)
async def serve_icons():
return FileResponse(os.path.join(frontend_dir, "icons.svg"))
@app.get("/{full_path:path}", include_in_schema=False)
async def serve_frontend(full_path: str):
# If a path isn't API or assets, fallback to index.html for React Router / SPA handling
# In this specific case, it also fixes any root path reloading issues
return FileResponse(os.path.join(frontend_dir, "index.html"))
# 【重要安全修复】避免拦截不存在的 API 路由。如果是调用了不存在的 /api/ 接口,直接返回 404不返回前端页面
if full_path.startswith("api/"):
return JSONResponse(status_code=404, content={"detail": "API endpoint not found"})
index_path = os.path.join(frontend_dir, "index.html")
if os.path.exists(index_path):
return FileResponse(index_path)
return JSONResponse(status_code=404, content={"detail": "Frontend build not found"})
else:
import logging
logging.getLogger("pretor").warning(f"Frontend dist folder not found at {frontend_dir}. Skipping frontend mount.")
@serve.deployment
@serve.ingress(app)
class PretorGateway:
gateway: Dict[str, WebSocket]
def __init__(self):
self.gateway = {}