Files
life-echo/api/app/main.py
Kevin a3f61fcc0f feat(api+app): 对话阶段化、回忆录流水线与客户端会话体验
- DB: segments 用户输入文本(Alembic 0002)
- Chat: 阶段检测/阶段提示/回复限制,编排与访谈/画像 prompts 调整
- Memoir: 忠实度检查 agent,叙事与分类等链路更新
- Core: agent 日志、Alembic 启动、LangChain/日志/配置等
- Story: time_hints;Memory 检索与相关测试
- Expo: 助手头像、会话页与消息拆分、实时会话与文案/i18n
- Docs/scripts/tests: 迁移脚本、LLM JSON/记忆检索文档、新增单测
2026-03-26 12:13:36 +08:00

159 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
FastAPI 应用入口app 内主入口,符合架构计划)
"""
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.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.core.openapi import custom_openapi
from app.features.auth.router import router as auth_router
from app.features.content.router import router as content_router
from app.features.conversation.router import router as conversation_router
from app.features.conversation.ws.router import websocket_endpoint
from app.features.memoir.router import router as memoir_router
from app.features.payment.router import router as payment_router
from app.features.plan.router import router as plan_router
from app.features.quota.router import router as quota_router
from app.features.tasks.router import router as tasks_router
from app.features.user.router import feedback_router as user_feedback_router
from app.features.user.router import router as user_router
# 聚合注册所有 feature 的 model 到 Base.metadata供 Alembic 等使用)
from app.features.auth import models as _auth_models # noqa: F401
from app.features.conversation import models as _conv_models # noqa: F401
from app.features.memory import models as _memory_models # noqa: F401
from app.features.memoir import models as _memoir_models # noqa: F401
from app.features.payment import models as _payment_models # noqa: F401
from app.features.story import models as _story_models # noqa: F401
from app.features.user import models as _user_models # noqa: F401
app = FastAPI(
title="Life Echo API",
version="1.0.0",
docs_url="/docs" if settings.enable_docs else None,
redoc_url="/redoc" if settings.enable_docs else None,
openapi_url="/openapi.json" if settings.enable_docs else None,
)
# OpenAPI 全局增强
app.openapi = lambda: custom_openapi(app) # type: ignore[assignment]
logger = get_logger(__name__)
# Middleware注册顺序LIFO先注册的后执行
app.add_middleware(RequestIdMiddleware)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 全局异常处理
register_exception_handlers(app)
@app.on_event("startup")
async def startup_event():
"""应用启动事件Alembic 迁移(可重试、可配置 fail-fast、Redis、ASR、支付预初始化。"""
import asyncio
from app.core.alembic_startup import run_alembic_upgrade_at_startup
logger.info("Life Echo API 正在启动...")
await asyncio.to_thread(run_alembic_upgrade_at_startup)
try:
from app.core.redis import redis_service
await redis_service.get_client()
logger.info("Redis 连接已建立")
except Exception as e:
logger.warning("Redis 连接失败(会话存储将不可用): {}", e)
try:
from app.core.dependencies import get_asr_provider
provider = get_asr_provider()
ensure_ready = getattr(provider, "ensure_ready", None)
if callable(ensure_ready):
asr_ready = await asyncio.to_thread(ensure_ready)
else:
asr_ready = True
if asr_ready:
from app.core.config import settings
name = (
"腾讯云一句话识别"
if settings.asr_provider == "tencent"
else "本地 Whisper"
)
logger.info("ASR 服务已就绪({}", name)
else:
logger.warning("ASR 服务未就绪,语音转写将不可用")
except Exception as e:
logger.warning("ASR 初始化检查失败: {}", e)
try:
def _init_wechat_pay_client():
from app.features.payment.deps import get_payment_service
svc = get_payment_service()
if svc.is_method_available("wechat"):
_ = svc.wechat_client
await asyncio.to_thread(_init_wechat_pay_client)
logger.info("微信支付客户端已预初始化")
except Exception as e:
logger.warning(f"微信支付预初始化失败(首次下单时再初始化): {e}")
@app.on_event("shutdown")
async def shutdown_event():
"""应用关闭事件"""
logger.info("Life Echo API 正在关闭...")
try:
from app.core.redis import redis_service
await redis_service.close()
logger.info("Redis 连接已关闭")
except Exception as e:
logger.warning("关闭 Redis 连接失败: {}", e)
# ── Feature routers ──────────────────────────────────────────
app.include_router(auth_router)
app.websocket("/ws/conversation/{conversation_id}")(websocket_endpoint)
app.include_router(conversation_router)
app.include_router(memoir_router)
app.include_router(user_router)
app.include_router(user_feedback_router)
app.include_router(plan_router)
app.include_router(payment_router)
app.include_router(quota_router)
app.include_router(tasks_router)
app.include_router(content_router)
# static 在 api/ 下app/main.py 在 api/app/ 下
_static_dir = Path(__file__).resolve().parent.parent / "static"
app.mount("/static", StaticFiles(directory=_static_dir), name="static")
@app.get("/health", include_in_schema=False)
async def health():
return {"status": "ok"}