feat/export-service
main
针对「函数数量没有限制,可能生成数百 MB 的 SVG」,为导出链路增加资源上限:
app/plot/parser.py
_MAX_EXPRESSIONS = 16
FUNCTION_PLOT_TOO_MANY_EXPRESSIONS
app/export/exporters/html.py
_MAX_FUNCTION_PLOTS = 16
app/export/service.py
MAX_MARKDOWN_CHARS
MAX_EXPORT_BYTES
EXPORT_OPTIONS_INVALID
EXPORT_OUTPUT_TOO_LARGE
Export开发说明.md
🤖 Generated with Claude Code
实现 Export Service 完整生命周期:mistune AST → Document AST → HtmlExporter 渲染完整 HTML5,异步任务注册表 + 取消 + 24h 产物过期。新增 5 个 /api/exports 端点与 15 项测试;pdf/docx 与函数图像静态渲染留待后续 PR。
- P1 链接/图片 URL 协议白名单校验,危险协议降级为纯文本 + warning - P2 图片 AST 字段映射(src=attrs.url,alt 取 children 文本) - P2 原始 HTML 块转义保留,正文不丢失 + warning - P2 过期/淘汰/重启清理导出产物文件 - P2 解析与渲染移入 asyncio.to_thread,运行中取消生效 - P2 function-plot 围栏别名补全 - 回归测试覆盖全部修复 Co-Authored-By: Claude Code <noreply@anthropic.com>
- 新增 app/plot 包:白名单表达式解析(ast 无 eval)+ FunctionPlot 模型 + 静态 SVG 渲染 - HtmlExporter 的 function_plot 节点解析并内嵌 SVG,解析失败回退占位并转诊断 - 新增 test_plot.py(13 个测试)覆盖表达式安全、指令解析、SVG 输出与导出链路集成 Co-Authored-By: Claude Code <noreply@anthropic.com>
- P1 浮点刻度死循环:_ticks 改为有上限的整数索引推进并校验步长推进 - P2 求值异常:白名单函数校验参数数量;负数底非整数指数按断点处理;采样容错复数 - P2 无效纵轴范围:退化/非有限 range 丢弃并自动采样重算;渲染异常回退占位不阻断导出 - 补 6 个回归测试 Co-Authored-By: Claude Code <noreply@anthropic.com>
同步 main(054f704),解决 contracts.py / main.py / README.md / 技术栈说明 的合并冲突。 - contracts.py:保留 pydantic 多行导入并新增 RequestOverride - main.py:合并 lifespan(导出孤儿清理 + 转写/本地模型生命周期) - README.md / 技术栈说明:文档取 main 最新版本 Co-Authored-By: Claude Code <noreply@anthropic.com>
- P2 复杂表达式绕过异常回退:解析与渲染共同纳入局部异常回退; AST 深度/节点数上限拦截 RecursionError - P2 极端有限范围生成 nan SVG:校验坐标跨度有限且 >0,回退安全范围; _polyline 拒绝非有限像素坐标 - P3 更新接口契约文档:function-plot 静态 SVG 已实现 Co-Authored-By: Claude Code <noreply@anthropic.com>
针对 PR 审阅「函数数量没有限制,可能生成数百 MB 的 SVG」: - parser: 单块 function-plot 表达式上限 _MAX_EXPRESSIONS=16,超限整块回退 - html: 单篇文档函数图像上限 _MAX_FUNCTION_PLOTS=16,超出回退源码占位 - service: 输入源 MAX_MARKDOWN_CHARS、产物 MAX_EXPORT_BYTES,超限分别 拒绝创建或标记 failed(EXPORT_OUTPUT_TOO_LARGE) - 补充 4 条回归测试与文档说明 Co-Authored-By: Claude Code <noreply@anthropic.com>
审阅提交:124024a
124024a
上一轮提出的函数数量、输入大小和产物大小问题已经分别增加限制。本轮复审发现 1 个 P1、1 个 P3。
位置:backend/app/plot/parser.py:365–374
backend/app/plot/parser.py:365–374
当前分别限制了:
但是没有限制整篇文档的累计 AST 节点数或采样工作量,因此这些上限组合后仍然允许高开销输入。
实测一个包含 16 条表达式、每条由 150 项加法组成的图块:
包含 16 个此类图块的文档源码约 77 KB,仍低于输入上限,预计渲染约 13.7 秒,最终 SVG 也不会触发 20 MB 产物限制。
渲染运行在 asyncio.to_thread 中,期间无法响应取消信号。最多 100 个活动任务可同时进入队列,可能持续占满工作线程和 CPU。
asyncio.to_thread
建议:
位置:docs/development/Export开发说明.md:68–79
docs/development/Export开发说明.md:68–79
产物超限时:
POST /api/exports
202
failed
EXPORT_OUTPUT_TOO_LARGE`` 写入
GET
200
因此该错误不会通过 HTTP 413 返回。文档将它标记为 413,可能导致前端错误地按 HTTP 状态码处理。
413
建议将同步 HTTP 错误和异步任务错误分开记录,并注明该错误通过:
{ "status": "failed", "error_code": "EXPORT_OUTPUT_TOO_LARGE" }
返回,而不是 HTTP 413。
git diff --check
请修复组合资源预算问题并更新文档后再提交复审。
No dependencies set.
The note is not visible to the blocked user.
修复内容(PR #29 审阅 P1)
针对「函数数量没有限制,可能生成数百 MB 的 SVG」,为导出链路增加资源上限:
代码
app/plot/parser.py:单块 function-plot 表达式数量上限_MAX_EXPRESSIONS = 16,超限整块回退并记FUNCTION_PLOT_TOO_MANY_EXPRESSIONS诊断。app/export/exporters/html.py:单篇文档函数图像数量上限_MAX_FUNCTION_PLOTS = 16,超出部分回退为源码占位并记 warning。app/export/service.py:输入源MAX_MARKDOWN_CHARS(200k)与产物MAX_EXPORT_BYTES(20MB)上限,超限分别拒绝创建(EXPORT_OPTIONS_INVALID)或标记 failed(EXPORT_OUTPUT_TOO_LARGE)。测试
文档
Export开发说明.md资源上限章节与错误码表、契约 §10.5。🤖 Generated with Claude Code
审阅结论:请求修改,暂不建议合并
审阅提交:
124024a上一轮提出的函数数量、输入大小和产物大小问题已经分别增加限制。本轮复审发现 1 个 P1、1 个 P3。
[P1] 组合复杂度仍可长时间占满导出线程
位置:
backend/app/plot/parser.py:365–374当前分别限制了:
但是没有限制整篇文档的累计 AST 节点数或采样工作量,因此这些上限组合后仍然允许高开销输入。
实测一个包含 16 条表达式、每条由 150 项加法组成的图块:
包含 16 个此类图块的文档源码约 77 KB,仍低于输入上限,预计渲染约 13.7 秒,最终 SVG 也不会触发 20 MB 产物限制。
渲染运行在
asyncio.to_thread中,期间无法响应取消信号。最多 100 个活动任务可同时进入队列,可能持续占满工作线程和 CPU。建议:
[P3] `EXPORT_OUTPUT_TOO_LARGE`` 不会对应 HTTP 413
位置:
docs/development/Export开发说明.md:68–79产物超限时:
POST /api/exports已经返回202;failed;EXPORT_OUTPUT_TOO_LARGE`` 写入ExportJob.error_code`;GET请求取得任务状态,该请求仍返回200。因此该错误不会通过 HTTP
413返回。文档将它标记为413,可能导致前端错误地按 HTTP 状态码处理。建议将同步 HTTP 错误和异步任务错误分开记录,并注明该错误通过:
返回,而不是 HTTP 413。
验证结果
git diff --check:通过main:无冲突请修复组合资源预算问题并更新文档后再提交复审。
Pull request closed