Files
life-echo/api/app/main.py
Kevin e4bf0710c7 feat(memory,conversation): 记忆富化/证据包、时间线幂等字段与对话分段全链路
数据库
- 新增迁移 0003:timeline_events.memory_source_id 外键 → memory_sources,便于按 ingest 源做时间线幂等

后端 - 记忆
- 新增 ingest 后 LLM 富化(摘要/事实/时间线),可配置开关与最大字符数
- 新增证据包组装:合并 chunk、摘要、事实、时间线、故事等检索结果;支持空 query 时是否仍带 rolling 等开关
- repo/retriever/service/router/schemas/summarizer/timeline/extractor 等扩展;文档 memory-retrieval.md 更新

后端 - 对话 WS
- 增加 PING/PONG;分段 ASR 日志与空音频处理;转写失败与「无助手回复」错误提示更明确
- 助手多段回复持久化使用统一分隔符,与分段逻辑一致

后端 - Agent
- reply_limits:按 [SPLIT] 与段落拆段,并保证非空 fallback,供 WS 与 TTS 多段下发

后端 - 回忆录任务
- transcript ingest 记录 source_id;任务成功结?
2026-03-27 16:24:43 +08:00

161 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.memory.router import router as memory_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(memory_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"}