feat(server): Web 服务化(FastAPI + SQLite + 内嵌零构建前端)

参赛成果物 03「交互界面 + 数据存储」落地:
- src/genesis/server/: store.py(SQLite 会话持久化)、service.py(会话化服务层:
  上传→解析→确认→影响→确认→生成→QA)、app.py(api-design §2 核心端点 9 组)、
  static/index.html(内嵌单页,零构建无 node_modules 依赖)
- scripts/serve.py 启动入口(--fake 离线引擎 / 默认真实 LLM)
- pyproject 加 fastapi/uvicorn/python-multipart
- 修复 qa_loop._build meta={} 导致真实模板 {{doc_title}} 等占位符残留 DocxInjectError
- README Web 服务说明 + service_url 登记指引;design.md §12.5 记录(含同步执行/
  zip 既有系统/无 WebSocket 的诚实偏差标注)
- 测试:test_server_store/service/api 共 34 用例(TestClient 全链路 + zip 影响流程 + 错误分支)
全量 pytest 473 passed / 99.20%
This commit is contained in:
lhl
2026-08-26 22:13:19 +08:00
parent a1fefd43eb
commit 215c605650
14 changed files with 1474 additions and 1 deletions
+39
View File
@@ -0,0 +1,39 @@
"""Web 服务启动入口(S5)。
用法:
python scripts/serve.py [--host 0.0.0.0] [--port 8000] [--fake]
- 默认真实 LLM 模式(需 .env 配置 GENESIS_INFERENCE__API_KEYgenerate 时按需加载)
- --fake 为离线确定性引擎(无需 key,适合评审演示环境)
- 会话/文件/结果存储于 data/server/SQLite + 上传快照,不入库)
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT / "src") not in sys.path:
sys.path.insert(0, str(ROOT / "src"))
import uvicorn # noqa: E402
from genesis.server.app import create_app # noqa: E402
def main() -> None:
p = argparse.ArgumentParser(description="Genesis Web 服务")
p.add_argument("--host", default="127.0.0.1")
p.add_argument("--port", type=int, default=8000)
p.add_argument("--fake", action="store_true", help="离线 Fake 引擎(无需 API key")
p.add_argument("--data-root", default=str(ROOT / "data" / "server"), help="会话/文件/结果存储目录")
args = p.parse_args()
app = create_app(data_root=args.data_root, engine="fake" if args.fake else None)
print(f"Genesis Web 服务启动: http://{args.host}:{args.port} (mode={'fake' if args.fake else 'real'})")
uvicorn.run(app, host=args.host, port=args.port)
if __name__ == "__main__":
main()