Files

218 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""ORM models for the external-task multi-agent scheduled analysis (任务分析定时).
Feature summary (design doc in the frontend ``official-task-analysis`` module):
an external system pushes tasks via our create API; each task binds 1:1 to a
business-chain orchestration plus a cron schedule; the scheduler runs the chain
stage-by-stage (seats within a stage in parallel) and persists per-seat process
messages plus a content "timeline" version per run. Manual runs wrap the
roundtable Step-2 coordinator Q&A and are stored with the same message table
(``seat_agent_id`` distinguishes seats; the coordinator uses ``__coordinator__``).
Tables:
- ``task_analysis_tasks`` — external tasks (source_task_id unique)
- ``task_analysis_bindings`` — 1:1 binding (chain snapshot + cron + notes)
- ``task_analysis_runs`` — executions (trigger: schedule | manual)
- ``task_analysis_run_messages`` — per-seat process messages for a run
- ``task_analysis_content_versions`` — one timeline version per run
- ``task_data_block_defs`` — the 5 fixed data blocks per task (title/sort editable)
- ``task_data_periods`` — data-delivery timeline ticks (period_index per task)
- ``task_data_blocks`` — merged payload per (task, period, block)
- ``task_data_submissions`` — append-only submission log feeding the merge
JSON payloads are stored as LONGTEXT-backed JSON strings (PortableLongText),
mirroring the sibling roundtable persistence modules for headroom.
"""
from __future__ import annotations
from datetime import UTC, datetime
from sqlalchemy import Boolean, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from deerflow.persistence.base import Base
from deerflow.persistence.types import BeijingDateTime, PortableLongText
def _utcnow() -> datetime:
return datetime.now(UTC)
class TaskAnalysisTaskRow(Base):
__tablename__ = "task_analysis_tasks"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
# 对方系统任务号(创建接口传入;唯一)。
source_task_id: Mapped[str] = mapped_column(String(128), nullable=False, unique=True, index=True)
name: Mapped[str] = mapped_column(String(512), nullable=False, default="")
# 任务简介:创建时落库的存量即执行时用的版本(不回拉对方接口)。
description: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)
class TaskAnalysisBindingRow(Base):
__tablename__ = "task_analysis_bindings"
__table_args__ = (UniqueConstraint("external_task_id", name="uq_task_analysis_binding_task"),)
id: Mapped[str] = mapped_column(String(64), primary_key=True)
external_task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
# 引用 roundtable_chains.id;执行前校验,链缺失/停用 → 跳过并标记 chain_missing。
chain_id: Mapped[str] = mapped_column(String(64), nullable=False)
# 执行编排快照 JSON:{seats, stages, stage_goals, coordinator_prompt}。
# 每次执行前刷新(跟随链最新版),历史 run 用自己的 stages_snapshot 回放。
chain_snapshot: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
cron_expr: Mapped[str] = mapped_column(String(64), nullable=False, default="")
timezone: Mapped[str] = mapped_column(String(64), nullable=False, default="Asia/Shanghai")
# 执行方式:cron = 按频率定时;once = 只执行一次(创建即执行,next_run_at 恒空)。
schedule_type: Mapped[str] = mapped_column(String(16), nullable=False, default="cron", server_default="cron")
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default="1")
next_run_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True, index=True)
# 绑定级总控提示(与链内 coordinator_prompt 拼接使用)。
coordinator_prompt: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
# 问答沉淀的「执行方向备注」JSON 数组(逐条追加,注入后续每次定时执行)。
direction_notes: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
last_run_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True)
last_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
# 执行模型(席位 run 的 model_name;NULL = 系统/席位默认)。
model: Mapped[str | None] = mapped_column(String(128), nullable=True)
created_by: Mapped[str | None] = mapped_column(String(64), nullable=True)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)
class TaskAnalysisRunRow(Base):
__tablename__ = "task_analysis_runs"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
binding_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
external_task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
# schedule = cron 定时执行;manual = 总控问答(对方详情页发起)。
trigger: Mapped[str] = mapped_column(String(16), nullable=False, default="schedule")
# 幂等键:同 binding + scheduled_for 只建一条(对齐 scheduled_tasks.create_run_once)。
scheduled_for: Mapped[datetime] = mapped_column(BeijingDateTime(), nullable=False)
started_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
finished_at: Mapped[datetime | None] = mapped_column(BeijingDateTime(), nullable=True)
# pending | running | succeeded | failed | partial
status: Mapped[str] = mapped_column(String(16), nullable=False, default="pending", index=True)
# 本次编排快照 JSON:[[seat_id,...],...](回放不受链后续修改影响)。
stages_snapshot: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
stage_goals_snapshot: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
seat_names_snapshot: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
# 各席位结果 JSON:{agent_id: {state, duration_ms, thread_id, error}}。
seat_results: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
error: Mapped[str | None] = mapped_column(Text, nullable=True)
retry_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)
class TaskAnalysisRunMessageRow(Base):
__tablename__ = "task_analysis_run_messages"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
run_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
seq: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
# 消息归属席位(agent_id);总控消息用固定标识 __coordinator__。
seat_agent_id: Mapped[str] = mapped_column(String(128), nullable=False, default="")
stage_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
# input | llm.ai | tool.result | artifact(对齐现有 run messages 事件分类)。
event_type: Mapped[str] = mapped_column(String(32), nullable=False, default="llm.ai")
content: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
class TaskAnalysisContentVersionRow(Base):
__tablename__ = "task_analysis_content_versions"
__table_args__ = (UniqueConstraint("run_id", name="uq_task_analysis_version_run"),)
id: Mapped[str] = mapped_column(String(64), primary_key=True)
external_task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
# 一次执行 = 一期时间线版本(1:1)。
run_id: Mapped[str] = mapped_column(String(64), nullable=False)
# JSON:{key: {name, seat_agent_id, content, format}} —— 对方详情的各块内容。
blocks: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
class TaskDataBlockDefRow(Base):
"""数据块定义:块 key 定死 block_1~block_6,标题默认「数据块一~六」可改。"""
__tablename__ = "task_data_block_defs"
__table_args__ = (UniqueConstraint("task_id", "block_key", name="uq_task_data_block_def_key"),)
id: Mapped[str] = mapped_column(String(64), primary_key=True)
# 关联 task_analysis_tasks.id(内部任务 id)。
task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
block_key: Mapped[str] = mapped_column(String(32), nullable=False)
title: Mapped[str] = mapped_column(String(128), nullable=False, default="")
sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)
class TaskDataPeriodRow(Base):
"""数据交付时间轴刻度:每个任务 period_index 从 1 递增;run_id 作幂等锚点。"""
__tablename__ = "task_data_periods"
__table_args__ = (
UniqueConstraint("task_id", "period_index", name="uq_task_data_period_index"),
UniqueConstraint("run_id", name="uq_task_data_period_run"),
)
id: Mapped[str] = mapped_column(String(64), primary_key=True)
task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_index: Mapped[int] = mapped_column(Integer, nullable=False)
opened_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
# cron = 定时执行开期;manual = 手动触发/总控问答开期;external = 首次提交兜底开期。
trigger: Mapped[str] = mapped_column(String(16), nullable=False, default="cron")
# 触发本期的执行记录(问答/定时执行共用 run 表);提交兜底开的期没有 run。
run_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
class TaskDataBlockRow(Base):
"""每期每块的合并结果(由 task_data_submissions 流水按序合并得出)。"""
__tablename__ = "task_data_blocks"
__table_args__ = (
UniqueConstraint("task_id", "period_index", "block_key", name="uq_task_data_block_tpb"),
)
id: Mapped[str] = mapped_column(String(64), primary_key=True)
task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_index: Mapped[int] = mapped_column(Integer, nullable=False)
block_key: Mapped[str] = mapped_column(String(32), nullable=False)
# JSON 对象(顶层 key 合并、同 key 后写覆盖)。
payload: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
class TaskDataSubmissionRow(Base):
"""入库流水(追加只读):块 payload 的一致性来源。"""
__tablename__ = "task_data_submissions"
id: Mapped[str] = mapped_column(String(64), primary_key=True)
task_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_index: Mapped[int] = mapped_column(Integer, nullable=False)
block_key: Mapped[str] = mapped_column(String(32), nullable=False)
# 本次提交的 JSON 对象片段(合并语义:顶层 key 合并、同 key 后写覆盖)。
payload: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
# 提交方(席位 agent id / 用户 id,仅审计)。
submitted_by: Mapped[str | None] = mapped_column(String(128), nullable=True)
# 同 (task, period, block) 内的提交序号,从 0 递增 —— 合并顺序依据。
seq: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
created_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow)
class TaskAnalysisSettingRow(Base):
"""模块级 KV 设置(当前仅默认业务链)。"""
__tablename__ = "task_analysis_settings"
key: Mapped[str] = mapped_column(String(64), primary_key=True)
value: Mapped[str] = mapped_column(PortableLongText, nullable=False, default="")
updated_at: Mapped[datetime] = mapped_column(BeijingDateTime(), default=_utcnow, onupdate=_utcnow)