Files
PlantAI-rag-new/paper_writer_service/app/main.py
T
admin 91f6a1c242 feat: 新增论文撰写功能 paper-writer(独立 MCP 服务 + BFF 大脑 + 前端三卡)
- paper_writer_service(8097):FastAPI + MCP streamable-http 单端点,9 工具
  (propose_paper_outline/write_paper_section/get_paper_progress/search_refs/
  revise_paper_section/revise_paper/get_paper/complete_paper/cancel_paper),
  SQLite 三表(papers/paper_sections/citations,WAL),GB/T 7714 引用协议
- BFF:services/paper/{mcp_client,agent}.py(裸 JSON-RPC 客户端 + 规则状态机)、
  routers/paper.py、schemas.py 4 个 Paper*Block + ArtifactBlock 加 paper_id/citation_count、
  config.py 开关、main.py 条件注册、docgen.py 抽 insert_artifact 公共 helper
- 前端:intent.js paperIntent 纯函数、Composer docgen 分支 sub-route、sendPaper/轮询、
  PaperBriefCard/PaperOutlineCard/PaperProgressCard、DocPane cite-chip
- deploy/paper_restart.sh + toggle_paper.sh;compile_sdk.sh 白名单追加 paper 文件
- tests/paper 26 条(工具逻辑 + MCP 协议 + BFF 状态机 + 块 schema + 前端 intent 单测)
2026-09-30 22:29:22 +08:00

85 lines
2.8 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.
"""main.py —— paper 服务应用入口(uvicorn 入口,明文白名单文件)。
- 独立部署树 paper_writer_service/(FR:独立进程 8097 + 独立 SQLite paper.db)。
- lifespan:init_db(幂等建表)→ 显式进入 MCP session_manager.run()(Starlette 不自动
运行子 Mount 的 lifespan,streamable-http 处理请求时要求 task group 已初始化)→ 清理。
- 端点:/health(GET,BFF 探活反代)+ /mcp(MCP streamable-http)。
- 不依赖 BFF/生产服务(8090 等绝不探测/连接)。
"""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .config import get_settings
from .db import close_db, db, init_db
from .mcp import paper_mcp
from .services.paper import search
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# MCP streamable-http app(调用即创建 session_manager,供 lifespan 进入 run())。
# 端点地址:/mcp(streamable_http_path="/" + 挂载前缀 /mcp)。
_paper_mcp_app = paper_mcp.streamable_http_app(streamable_http_path="/")
def create_app() -> FastAPI:
settings = get_settings()
app = FastAPI(title=settings.app_name, version=settings.version)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
@asynccontextmanager
async def _lifespan(_app: FastAPI):
# 测试注入:db 已由测试预置(init_db(tmp_path)),跳过启动初始化。
try:
await init_db()
except Exception:
logger.exception("main: 数据库初始化失败(health 将报 db 降级)")
# MCP session manager 的 task group 必须在服务运行期内保持活跃。
async with paper_mcp.session_manager.run():
try:
yield
finally:
try:
await close_db()
except Exception:
pass
await search.close_client()
app.router.lifespan_context = _lifespan
@app.get("/health")
async def health() -> dict[str, object]:
db_ok = True
db_error: str | None = None
try:
await db.fetchone("SELECT 1 AS ok")
except Exception as exc: # noqa: BLE001
db_ok = False
db_error = str(exc)
return {
"ok": True,
"status": "ok" if db_ok else "degraded",
"db": {"connected": db_ok, "error": db_error},
"version": settings.version,
}
# paper MCP 能力外挂(mcp 2.x:MCPServer.streamable_http_app)。
app.mount("/mcp", _paper_mcp_app)
return app
app = create_app()