支持直接审查 PDF 与位图图纸,全链路离线(不连 CAD、不连 8080 端口)。 渲染在内存内完成,复用既有墨迹热力图 + 自适应瓦片规划。 - 新增 dwg_review/sources.py - detect_source / plan_file / collect 统一入口 - PdfReader:pypdfium2 惰性导入,页序即图框序,处理 /Rotate 与 crop 边距 - ImageReader:世界单位 = 源像素,snap_box 整数对齐保证窗口与像素严格一致 - ink_grid:纯 PIL 灰度阈值 → 占用网格(不引入 numpy) - ReviewContext.create(connect_cad=False):离线时 client/tools 均为 None - 全链路 ctx.tools is None 守卫:review 只读子循环 / inspect_region / annotator / checks 的窗口类检查 / 绘图区最大化 - checklist.build_system_prompt(allow_tools=False, offline=True):离线提示词 - __main__:位置参数 source,按扩展名自动判定源类型并决定是否连 CAD - config 新增 8 个常量(PDF_MAX_PAGES / 文本最小字数 / 各源缩放上限 / 墨迹阈值等) - tiling.content_predicate 与 heatmap.cell_box 提升为公开函数 验证: - pytest 457 passed - verify_pdf_source.py:PDF 23/23、图片 9/9(不需 CAD),含实测瓦片字高像素与 旋转映射自检(文本区墨迹 0.311 vs 忽略 /Rotate 反向对照 0.145) - 真实 LLM 端到端:20 页 HVAC 图纸前 3 页跑通,结论具体可核验且未编造 handle 已知局限:页边线在墨迹网格里会把整页连成单簇,瓦片常退化为整页象限(CAD 侧 靠 drop_oversized 丢图框块,离线侧暂无等价手段),留待后续打磨。
48 lines
1.5 KiB
Python
48 lines
1.5 KiB
Python
"""阶段 1:全局规划。
|
|
|
|
收集图纸概览 + 识别图框,按阅读顺序排序,产出审查计划。
|
|
|
|
按来源分发:CAD 源用 `detect_drawing_frames` 识别图框;PDF / 图片源直接由文件
|
|
结构给出图框(PDF 每页一个、图片整图一个),**保持原始页序**(阅读顺序已由文档
|
|
自身决定,不能再按 bbox 重排)。
|
|
"""
|
|
|
|
from .. import sources
|
|
from ..mcp_client import result_json
|
|
|
|
|
|
def _sort_frames(frames):
|
|
"""按阅读顺序排序:先上后下(max_y 降序)、再左后右(min_x 升序)。"""
|
|
|
|
def key(frame):
|
|
bbox = frame.get("bbox") or [0, 0, 0, 0]
|
|
return (-round(float(bbox[3]), 3), round(float(bbox[0]), 3))
|
|
|
|
return sorted(frames, key=key)
|
|
|
|
|
|
def plan(ctx):
|
|
"""返回 {drawing_info, layers, layouts, frames}。"""
|
|
if sources.is_file_source(ctx.source):
|
|
return sources.plan_file(ctx)
|
|
|
|
drawing_info = result_json(ctx.tools.call("get_drawing_info", {}), {})
|
|
layers = result_json(ctx.tools.call("get_layers", {"count_entities": True}), {})
|
|
layouts = result_json(ctx.tools.call("get_layouts", {}), {})
|
|
|
|
frames_data = result_json(
|
|
ctx.tools.call("detect_drawing_frames", {"min_confidence": 0.3}), {}
|
|
)
|
|
frames = frames_data.get("items", []) if isinstance(frames_data, dict) else []
|
|
|
|
frames = _sort_frames(frames)
|
|
if ctx.max_frames > 0:
|
|
frames = frames[: ctx.max_frames]
|
|
|
|
return {
|
|
"drawing_info": drawing_info,
|
|
"layers": layers,
|
|
"layouts": layouts,
|
|
"frames": frames,
|
|
}
|