Files
life-echo/api/app/internal_main.py

119 lines
4.2 KiB
Python
Raw Normal View History

"""
内部回归评测 API 入口 app.main 进程隔离部署
启动示例 api/ 目录端口与 INTERNAL_EVAL_PORT 一致development.sh 默认 7999::
uv run uvicorn app.internal_main:internal_app --host 0.0.0.0 --port 7999
"""
from __future__ import annotations
from pathlib import Path
from app.core.logging import get_logger, setup_logging
setup_logging()
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from app.core.config import settings
from app.core.errors import register_exception_handlers
from app.core.middleware import RequestIdMiddleware
from app.features.evaluation import models as _eval_models # noqa: F401
from app.features.evaluation.router import router as eval_router
logger = get_logger(__name__)
internal_app = FastAPI(
title="Life Echo Internal Evaluation API",
version="0.1.0",
docs_url="/docs" if settings.internal_eval_enable_docs else None,
redoc_url="/redoc" if settings.internal_eval_enable_docs else None,
openapi_url="/openapi.json" if settings.internal_eval_enable_docs else None,
)
internal_app.add_middleware(RequestIdMiddleware)
_origins = [
o.strip()
for o in (settings.internal_eval_cors_origins or "").split(",")
if o.strip()
]
# 浏览器不允许 Origin=* 与 credentials 同时出现;未配置显式白名单时关闭 credentials。
_allow_creds = bool(_origins)
internal_app.add_middleware(
CORSMiddleware,
allow_origins=_origins if _origins else ["*"],
allow_credentials=_allow_creds,
allow_methods=["*"],
allow_headers=["*"],
)
register_exception_handlers(internal_app)
@internal_app.get("/", include_in_schema=False, response_class=HTMLResponse)
async def internal_eval_landing():
"""浏览器打开内部评测 API 根路径时提示:界面在 Vite默认 5174本进程仅为 API。"""
docs_hint = (
'<p><a href="/docs">OpenAPI 文档 /docs</a></p>'
if settings.internal_eval_enable_docs
else "<p>(未开启文档;设置 INTERNAL_EVAL_ENABLE_DOCS=1 后可访问 /docs</p>"
)
return f"""<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"/><title>内部评测 API</title></head>
<body style="font-family:system-ui,sans-serif;max-width:44rem;margin:2rem auto;line-height:1.5">
<h1>Life Echo · 内部回归评测 API</h1>
<p>这里是 <strong>HTTP API</strong>端口由启动命令决定<strong>没有内置网页</strong>
浏览回归评测台请在仓库执行 <code>./internal-eval.sh</code> <code>cd app-eval-web && npm run dev</code>
在终端里打开 Vite 给出的地址一般为 <strong>http://127.0.0.1:5174/</strong></p>
<p>健康检查<a href="/health">/health</a></p>
{docs_hint}
<p>会话与对比接口前缀<code>/internal/api/evaluation/</code></p>
</body></html>"""
@internal_app.on_event("startup")
async def _startup():
import asyncio
from app.core.alembic_startup import run_alembic_upgrade_at_startup
logger.info("内部评测 API 启动中…")
await asyncio.to_thread(run_alembic_upgrade_at_startup)
try:
from app.core.celery_broker_dev import maybe_purge_celery_broker_on_startup
from app.core.redis import redis_service
_redis = await redis_service.get_client()
logger.info("Redis 已连接(评测任务可用)")
await maybe_purge_celery_broker_on_startup(_redis)
except Exception as e:
logger.warning("Redis 连接失败: {}", e)
@internal_app.on_event("shutdown")
async def _shutdown():
logger.info("内部评测 API 关闭中…")
try:
from app.core.redis import redis_service
await redis_service.close()
except Exception as e:
logger.warning("关闭 Redis 失败: {}", e)
internal_app.include_router(eval_router, prefix="/internal/api/evaluation")
_static_dir = Path(__file__).resolve().parent.parent / "static"
if _static_dir.is_dir():
internal_app.mount(
"/static", StaticFiles(directory=str(_static_dir)), name="static"
)
@internal_app.get("/health", include_in_schema=False)
async def health():
return {"status": "ok", "service": "internal-eval"}