后端 - check 模块:检查预览、发起、进度、取消、记录;六个阶段(准备投标文件、响应匹配、规则、证据校验、 状态沿用、汇总);问题列表、处理、撤销、批量处理(只限提示级)。 - 每条清单项的检查结论 check_item_result(C9,迁移 0005):「已响应」也带投标原文出处; 没有校验通过的证据不能判已响应(C3)。级别从清单项算,不让模型定;说明不写整改建议。 - 响应匹配先只有第三层:内存 BM25 检索候选句 + check.match v1 提示词,检索不到不调模型, 漏答记 missing_answer,模型临时错误只影响那一批。 - 计费:免费版不记流水;专业版 24 小时内复查免扣(记一行 0);expected_charge 不一致时拒绝不扣点; 失败、取消退回;投标文件读不出文字按输入问题失败、不扣点。 - 投标分册:类型(volume_kind)和版本;电子投标平台加密格式在上传时拦下。 - 解析流水线前几个阶段拆到 document/pipeline/normalize.py,解析和检查共用。 - ops 模块:反馈接口;致命问题确认无误的说明自动进误报反馈(U4)。 - SK1:Valkey 客户端短超时、SSE 连接池上限。SK2:CI 加 Gotenberg、直接跑 init-roles.sh。 前端 - 上传投标文件并检查页、查标页(进度、覆盖范围、按级别筛选、逐条处理和行内说明、撤销、原文抽屉、 已处理、本次已解决);项目头抽成公共组件,加「查标」标签页;工作台「下一步」覆盖查标阶段。 契约、文档:openapi.yaml 加 item-results、volume_kind、coverage 字段;data-model、api-design §10、 架构 §8 / §13(SK14)、AGENTS.md 同步。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
94 lines
2.6 KiB
TOML
94 lines
2.6 KiB
TOML
[project]
|
||
name = "bid-doctor"
|
||
version = "0.1.0"
|
||
description = "标博士服务端:业务接口(api)和后台任务(worker)是同一套代码、两种进程"
|
||
requires-python = ">=3.12"
|
||
dependencies = [
|
||
"fastapi>=0.115",
|
||
"uvicorn[standard]>=0.32",
|
||
"pydantic>=2.9",
|
||
"pydantic-settings>=2.6",
|
||
"sqlalchemy[asyncio]>=2.0.36",
|
||
"psycopg[binary,pool]>=3.2",
|
||
"alembic>=1.14",
|
||
"procrastinate>=3.10,<4", # core/queue.py 直接调用它的 SQL 函数,升级大版本前要核对
|
||
"redis>=5.2",
|
||
"httpx>=0.28",
|
||
"pypdfium2>=4.30",
|
||
"pdfplumber>=0.11",
|
||
"python-multipart>=0.0.18",
|
||
"cryptography>=44",
|
||
]
|
||
|
||
[dependency-groups]
|
||
dev = [
|
||
"pytest>=8.3",
|
||
"pytest-asyncio>=0.25",
|
||
"ruff>=0.8",
|
||
"pyright>=1.1.390",
|
||
"import-linter>=2.1",
|
||
"pyyaml>=6",
|
||
]
|
||
|
||
[project.scripts]
|
||
bid-doctor = "bid_doctor.cli:main"
|
||
|
||
[build-system]
|
||
requires = ["hatchling>=1.26"]
|
||
build-backend = "hatchling.build"
|
||
|
||
[tool.hatch.build.targets.wheel]
|
||
packages = ["src/bid_doctor"]
|
||
|
||
[tool.ruff]
|
||
line-length = 120
|
||
target-version = "py312"
|
||
src = ["src", "tests"]
|
||
|
||
[tool.ruff.lint]
|
||
select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "ASYNC", "S", "BLE", "PT"]
|
||
ignore = [
|
||
"RUF001", "RUF002", "RUF003", # 中文全角标点是有意的
|
||
"S101", # assert 用来表达不变量
|
||
]
|
||
|
||
[tool.ruff.lint.per-file-ignores]
|
||
"tests/**" = ["S", "PT011", "PT018"]
|
||
"migrations/**" = ["E402"]
|
||
|
||
[tool.pyright]
|
||
include = ["src"]
|
||
pythonVersion = "3.12"
|
||
venvPath = ".."
|
||
venv = ".venv"
|
||
typeCheckingMode = "strict"
|
||
reportMissingTypeStubs = false
|
||
reportUnknownMemberType = false
|
||
reportUnknownVariableType = false
|
||
reportUnknownArgumentType = false
|
||
|
||
[tool.pytest.ini_options]
|
||
asyncio_mode = "auto"
|
||
asyncio_default_fixture_loop_scope = "session"
|
||
asyncio_default_test_loop_scope = "session"
|
||
testpaths = ["tests"]
|
||
pythonpath = ["."]
|
||
|
||
[tool.importlinter]
|
||
root_package = "bid_doctor"
|
||
|
||
[[tool.importlinter.contracts]]
|
||
name = "core 不依赖业务模块"
|
||
type = "forbidden"
|
||
source_modules = ["bid_doctor.core"]
|
||
forbidden_modules = ["bid_doctor.identity", "bid_doctor.billing", "bid_doctor.project", "bid_doctor.document",
|
||
"bid_doctor.checklist", "bid_doctor.check", "bid_doctor.ai", "bid_doctor.audit",
|
||
"bid_doctor.notification", "bid_doctor.ops"]
|
||
|
||
[[tool.importlinter.contracts]]
|
||
name = "业务模块不依赖 api 层的组合模块"
|
||
type = "forbidden"
|
||
source_modules = ["bid_doctor.document", "bid_doctor.checklist", "bid_doctor.check", "bid_doctor.billing",
|
||
"bid_doctor.identity.service", "bid_doctor.ops"]
|
||
forbidden_modules = ["bid_doctor.project.workbench", "bid_doctor.app"]
|