Files
hunjing/backend/alembic/versions/5cf6d328c10a_run_事件流与当前分支.py
T
chenxin 37c53862c4 ✨ feat: WP-03 主体——ContentBlock 全类型、Run 事件重放、分页与分支(WP-03)
- ContentBlock 全 21 类型 Pydantic 判别联合(与 openapi 契约一一对齐,extra=forbid);系统产生类 block(tool_call/tool_result/agent_status/approval_request/artifact)拒绝客户端伪造(契约 3.5)
- 迁移 4:run_event 表((run_id, sequence) 唯一、事件类型检查约束)+ conversation.current_branch_id
- Run 事件持久化:状态事件、每 block completed、message.completed 与权威状态同事务写入;sequence 单调从 0;SSE 改为重放持久化事件(不再现场构造),delta/progress 留待真实模型流(WP-05),不伪造
- GET messages:opaque cursor 分页((created_at,id) 复合键、base64 编码、limit 1-200、has_more)
- branches:列表 + 当前路径切换(采用≠归真);跨会话/不存在分支 422 fail-closed
- 测试 36 项全绿:21 类型接受、runtime block 拒绝、事件重放序号单调、分页无重叠、cursor 非法拒绝、分支切换与跨域
2026-09-10 13:17:34 +08:00

50 lines
2.2 KiB
Python

"""Run 事件流与当前分支
Revision ID: 5cf6d328c10a
Revises: 4cc407ec8268
Create Date: 2026-09-10 13:13:16.305296
"""
from collections.abc import Sequence
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from alembic import op
# revision identifiers, used by Alembic.
revision: str = '5cf6d328c10a'
down_revision: str | Sequence[str] | None = '4cc407ec8268'
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('run_event',
sa.Column('id', sa.String(), nullable=False),
sa.Column('run_id', sa.String(), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False),
sa.Column('type', sa.String(), nullable=False),
sa.Column('data', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.CheckConstraint("type IN ('content.block.started', 'content.block.delta', 'content.block.progress', 'content.block.completed', 'content.block.failed', 'content.block.cancelled', 'message.completed', 'run.status.changed')", name=op.f('ck_run_event_type_values')),
sa.ForeignKeyConstraint(['run_id'], ['run.id'], name=op.f('fk_run_event_run'), ondelete='cascade'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_run_event'))
)
op.create_index('uq_run_event_run_sequence', 'run_event', ['run_id', 'sequence'], unique=True)
op.add_column('conversation', sa.Column('current_branch_id', sa.String(), nullable=True))
op.create_foreign_key(op.f('fk_conversation_branch'), 'conversation', 'branch', ['current_branch_id'], ['id'], ondelete='set null', use_alter=True)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('fk_conversation_branch'), 'conversation', type_='foreignkey')
op.drop_column('conversation', 'current_branch_id')
op.drop_index('uq_run_event_run_sequence', table_name='run_event')
op.drop_table('run_event')
# ### end Alembic commands ###