Files
董宏凯andClaude Opus 4.8 cd33aaeded feat(定时任务): 内置「模板数据转换器」按 HTML 模板填充 JSON 生成大屏
- 模板填充引擎 template_fill.py:模板注册表 + schema 骨架 + 结构校验 + 行级安全填充;
  模板自带 DATA 即 schema 来源。no.1 可转换填充(含 MANIFEST 更新时间刷新),
  no.2 暂为「只展示」(convertible=False) 原样输出已有数据
- 内置 agent template-json-builder(seed + SOUL),随 lifespan 注入,自动出现在
  定时任务「执行智能体」下拉
- 调度器新增模板填充执行分支 _run_template_fill_attempt:源 JSON→大模型转换→
  结构校验→失败回喂重试;空源/只展示模板直接用模板默认数据,绝不失败
- API:GET /api/scheduled-tasks/html-templates
- 前端:选中该 agent 后条件渲染「HTML模板」下拉 + .json 上传,只展示型模板隐藏 JSON 输入
- 文档同步(前后端 CLAUDE.md)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 18:00:00 +08:00

64 lines
2.7 KiB
Python

"""内置「模板数据转换器」agent 的「缺失自动补建」工具。
``template-json-builder`` 是定时任务「JSON → HTML 模板填充」功能的执行大脑:当用户在
定时任务表单里选中它、再选一个 HTML 模板时,到点执行会用它的 ``SOUL.md`` 把源 JSON
智能映射成模板需要的 ``DATA`` 结构(详见
:mod:`deerflow.runtime.scheduler.template_fill` 与调度器 ``service.py``)。
与 ``_ai_writing_seed.py`` / ``_roundtable_seed.py`` 完全同构:验证版 ``SOUL.md`` +
``config.yaml`` 随 git 发布于 ``_template_builder_seed_assets/<id>/``,seeder 只在
``.deer-flow/agents/<id>/`` 目录**缺失**时字节级复制(绝不覆盖线上手改)。lifespan 在
``_sync_legacy_agents`` 之前调用本 seeder,后者扫描 agents 目录把它 upsert 进 agents 表
(``user_id IS NULL`` 内置、``published=True``),于是 ``/api/agents`` 列表、进而定时任务
「执行智能体」下拉框里第一次启动就有它,admin 也能在智能体管理页编辑 SOUL/模型。
"""
from __future__ import annotations
import logging
from pathlib import Path
from deerflow.config.paths import get_paths
logger = logging.getLogger(__name__)
TEMPLATE_BUILDER_AGENT_ID = "template-json-builder"
# 验证版 SOUL.md + config.yaml 的源码资源目录(随 git 发布)。子目录名即 agent id。
_ASSETS_DIR = Path(__file__).parent / "_template_builder_seed_assets"
# seeder 复制的文件(存在即复制,缺失则跳过)。
_SEED_FILES: tuple[str, ...] = ("config.yaml", "SOUL.md")
def ensure_template_builder_agent() -> bool:
"""若 ``.deer-flow/agents/template-json-builder/`` 缺失,从资源补建。
返回 True 表示本次刚创建;目录已存在(尊重本地手改)或资源缺失则返回 False。
错误只记日志、绝不抛出——seeding 失败不能拖垮启动。
"""
try:
agent_dir = get_paths().agent_dir(TEMPLATE_BUILDER_AGENT_ID)
if agent_dir.exists():
return False
src_dir = _ASSETS_DIR / TEMPLATE_BUILDER_AGENT_ID
if not src_dir.is_dir():
logger.error(
"Missing seed assets for agent '%s' (expected at %s)",
TEMPLATE_BUILDER_AGENT_ID,
src_dir,
)
return False
agent_dir.mkdir(parents=True, exist_ok=True)
for fname in _SEED_FILES:
src = src_dir / fname
if src.exists():
(agent_dir / fname).write_bytes(src.read_bytes())
logger.info("Seeded built-in agent '%s'", TEMPLATE_BUILDER_AGENT_ID)
return True
except Exception:
logger.exception("Failed to seed built-in agent '%s'", TEMPLATE_BUILDER_AGENT_ID)
return False