fix(compose): 标书docx生成两连修(2026-09-16甘肃项目实测)——①pyproject声明python-docx依赖(原dependencies=[]:测试机无docx模块→_write_docx ImportError诚实降级只出md,但标书最终交付必须docx;与pricing漏声明pyyaml同款教训) ②_fetch_image_bytes对/idfile|/download网关URL直接映射本机filesroot读盘(合成进程无登录态,HTTP拉logined-only的/idfile必401→配图全变'下载失败'文字占位;防穿越realpath校验)
This commit is contained in:
parent
842023772d
commit
9a7dfef204
@ -40,27 +40,63 @@ def _fetch_image_bytes(url, timeout=30):
|
||||
|
||||
URL 是 invoke_model 产物经 downloadfile2url 落地的本地持久地址
|
||||
(/idfile 静态路径,上游 24h 时效 URL 已在推理层落地,这里拿到的是
|
||||
平台自身地址)。http(s) 下载;本地绝对路径直读。失败返回 None(诚实
|
||||
降级:docx 里保留文字说明,不假装成功)。
|
||||
平台自身地址)。
|
||||
|
||||
解析优先级(2026-09-16 修复):
|
||||
1. /idfile|/download 网关 URL → 直接映射本机 filesroot 读盘——合成进程无
|
||||
登录态,HTTP 拉 logined-only 的 /idfile 必 401,图全变「下载失败」占位
|
||||
(甘肃项目 bid_v1 docx 实测根因)。
|
||||
2. 本地绝对路径直读。
|
||||
3. 其余 http(s) 下载。
|
||||
失败返回 None(诚实降级:docx 里保留文字说明,不假装成功)。
|
||||
"""
|
||||
try:
|
||||
u = (url or '').strip()
|
||||
if not u:
|
||||
return None
|
||||
# 网关 URL → filesroot 本地映射
|
||||
if u.startswith('http://') or u.startswith('https://'):
|
||||
from urllib.parse import urlparse, unquote
|
||||
path = unquote(urlparse(u).path or '')
|
||||
for lead in ('/idfile/', '/download/'):
|
||||
if path.startswith(lead):
|
||||
local = _filesroot_path(path[len(lead):])
|
||||
if local and os.path.isfile(local):
|
||||
with open(local, 'rb') as f:
|
||||
return f.read()
|
||||
break
|
||||
if u.startswith('/') and os.path.isfile(u):
|
||||
with open(u, 'rb') as f:
|
||||
return f.read()
|
||||
if u.startswith('http://') or u.startswith('https://'):
|
||||
import urllib.request
|
||||
req = urllib.request.Request(u, headers={'User-Agent': 'pipeline-bidding'})
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
data = resp.read()
|
||||
return data if data else None
|
||||
if u.startswith('/') and os.path.isfile(u):
|
||||
with open(u, 'rb') as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
logger.warning("compose: 配图下载失败 url=%s: %s", (url or '')[:120], e)
|
||||
return None
|
||||
|
||||
|
||||
def _filesroot_path(rel):
|
||||
"""/idfile 网关相对路径 → 本机 filesroot 绝对路径(防穿越:realpath 校验)。"""
|
||||
try:
|
||||
from appPublic.jsonConfig import getConfig
|
||||
root = getattr(getConfig(), 'filesroot', '') or ''
|
||||
if not root:
|
||||
return ''
|
||||
rel = (rel or '').lstrip('/')
|
||||
if not rel or '..' in rel.split('/'):
|
||||
return ''
|
||||
full = os.path.realpath(os.path.join(root, rel))
|
||||
if full.startswith(os.path.realpath(root) + os.sep):
|
||||
return full
|
||||
except Exception:
|
||||
pass
|
||||
return ''
|
||||
|
||||
|
||||
# markdown 图片行:(章节写作者按角色 prompt 规范嵌入 invoke_model 产物)
|
||||
_MD_IMG_RE = None
|
||||
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
name = "pipeline_bidding"
|
||||
version = "0.1.0"
|
||||
description = "投标产线:招标信息采集→审批→立项→解析→资质准备→分章节编写→评审→合成→评分→交付"
|
||||
dependencies = []
|
||||
# ⚠ python-docx 必须声明:compose_bid 合成标书 docx 依赖它,缺了走 ImportError
|
||||
# 诚实降级只出 md(2026-09-16 甘肃项目实测:标书最终要 docx 交付,静默降级不达标;
|
||||
# 与 pricing 漏声明 pyyaml 同款教训——非 editable 环境 pip install 不带依赖必炸)
|
||||
dependencies = ["python-docx"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user