后端:roundtable_chains 加 is_public 列;list_chains 支持 scope=mine|public(public=所有已发布,任何账号可查看/选入研讨);get_chain 放开公共可见;响应加 is_public/is_mine;update 支持 is_public(发布/取消,owner 限定,编辑/删除仍仅 owner)。迁移 20260608_03 接在合并 head 20260608_02 之后,保证单一 alembic head(多 head 会让启动自动迁移失败、静默降级 create_all → 列缺失 500)。 前端:listChains(scope) + setChainPublic;BusinessChain 加 isPublic/isMine;列表弹窗与研讨选择器加「我的/公共」筛选 tab;列表卡片发布/取消发布 + 公共徽标(别人的只读);配置页头部加发布按钮(已保存且本人)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
2.7 KiB
Python
56 lines
2.7 KiB
Python
"""ORM model for roundtable-planning business chains.
|
|
|
|
A "business chain" (业务链条) is a named, reusable, *ordered* list of agents the
|
|
user assembles in the chain editor. In Step 2 the coordinator dispatches to the
|
|
seats following this order (see ``multi-agent-business-chain-dev.md``).
|
|
|
|
Persisted per user (like ``roundtable_drafts``) so it follows the account across
|
|
devices/sessions. ``seats`` is stored as a JSON array serialized into
|
|
``PortableLongText`` (MySQL ``LONGTEXT``) rather than ``PortableJSON`` for
|
|
headroom, mirroring the sibling roundtable persistence modules.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import Boolean, String
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from deerflow.persistence.base import Base
|
|
from deerflow.persistence.types import BeijingDateTime, PortableLongText
|
|
|
|
|
|
class RoundtableChainRow(Base):
|
|
__tablename__ = "roundtable_chains"
|
|
|
|
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
user_id: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
|
|
# Human-readable chain title shown on the picker card.
|
|
title: Mapped[str] = mapped_column(String(512), nullable=False, default="")
|
|
# Optional free-text description.
|
|
description: Mapped[str | None] = mapped_column(String(2048), nullable=True)
|
|
# JSON: ordered [{"agent_id": str, "name": str, "description": str}, ...].
|
|
# seats[0] is the first seat to speak. Stored as LONGTEXT-backed JSON string.
|
|
seats: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
|
|
# Optional JSON: layered orchestration grouping over ``seats`` — a list of
|
|
# stages, each a list of agent_ids run in parallel; stages run serially in
|
|
# order. e.g. [["a","b"],["c"]] = (a‖b) → c. NULL/absent = linear chain
|
|
# (each seat its own stage). Drives Step 2 dag-mode orchestration; see
|
|
# frontend ``lib/dag-types.ts``. Stored as LONGTEXT-backed JSON string.
|
|
stages: Mapped[str | None] = mapped_column(PortableLongText, nullable=True)
|
|
# 发布开关:True = 公共(所有账号可在「公共」筛选里查看 / 选用);False = 仅本人可见。
|
|
# 编辑 / 删除始终只限 owner;发布只是放开「查看 + 选入研讨」。索引以支持「公共」列表筛选。
|
|
is_public: Mapped[bool] = mapped_column(
|
|
Boolean, nullable=False, default=False, server_default="0", index=True
|
|
)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
BeijingDateTime(), nullable=False, default=lambda: datetime.now(UTC)
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
BeijingDateTime(),
|
|
nullable=False,
|
|
default=lambda: datetime.now(UTC),
|
|
onupdate=lambda: datetime.now(UTC),
|
|
)
|