F1: add reusable task backend resources and migrate task tools
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""Move task tools into task backend resources without replaying existing tasks."""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
|
||||
revision = "20261023_0027"
|
||||
down_revision = "20261023_0026"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
"task_backend_resources",
|
||||
sa.Column("id", sa.String(40), primary_key=True),
|
||||
sa.Column("name", sa.String(128), nullable=False),
|
||||
sa.Column("type", sa.String(16), nullable=False),
|
||||
sa.Column("description", sa.String(2048), nullable=False),
|
||||
sa.Column("config", postgresql.JSONB(), nullable=False),
|
||||
sa.Column("secrets", postgresql.JSONB(), nullable=False),
|
||||
sa.Column("enabled", sa.Boolean(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.CheckConstraint("type IN ('reasoner', 'opencode')", name="ck_task_backend_type"),
|
||||
)
|
||||
op.add_column("assistants", sa.Column("task_backend", postgresql.JSONB(), nullable=True))
|
||||
op.add_column("background_tasks", sa.Column("backend_id", sa.String(40), nullable=False, server_default=""))
|
||||
op.add_column("background_tasks", sa.Column("backend_type", sa.String(16), nullable=False, server_default="opencode"))
|
||||
# NULL preserves pre-migration observation-window semantics for historical tasks.
|
||||
op.add_column("background_tasks", sa.Column("deadline_at", sa.String(40), nullable=True))
|
||||
op.alter_column("background_tasks", "tool_id", server_default="")
|
||||
op.execute("""
|
||||
DO $$ BEGIN
|
||||
IF EXISTS (
|
||||
SELECT b.assistant_id FROM assistant_tool_bindings b JOIN tools t ON t.id=b.tool_id
|
||||
WHERE t.type='task' GROUP BY b.assistant_id HAVING count(*) > 1
|
||||
) THEN RAISE EXCEPTION 'Task backend migration: an assistant has multiple task bindings; resolve before retrying'; END IF;
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM assistant_tool_bindings b JOIN tools t ON t.id=b.tool_id
|
||||
JOIN assistants a ON a.id=b.assistant_id
|
||||
WHERE t.type='task' AND (a.type != 'prompt' OR a.runtime_mode != 'pipeline')
|
||||
) THEN RAISE EXCEPTION 'Task backend migration: task bindings require Prompt Pipeline'; END IF;
|
||||
END $$
|
||||
""")
|
||||
# Resource IDs may retain the old tool prefix: IDs are local to each resource table.
|
||||
op.execute("""
|
||||
INSERT INTO task_backend_resources (id,name,type,description,config,secrets,enabled,created_at,updated_at)
|
||||
SELECT id,name,'opencode',description,
|
||||
jsonb_build_object(
|
||||
'url',definition->'config'->'url',
|
||||
'directory',definition->'config'->'directory',
|
||||
'username',COALESCE(definition->'config'->'username','\"opencode\"'::jsonb),
|
||||
'timeout_seconds',COALESCE(definition->'config'->'timeout_seconds',definition->'config'->'timeoutSeconds','600'::jsonb),
|
||||
'max_concurrency',1,'instructions','','agent','','model_provider_id','','model_id','','skills_note',''
|
||||
), secrets,status='active',created_at,updated_at
|
||||
FROM tools WHERE type='task'
|
||||
""")
|
||||
op.execute("""
|
||||
UPDATE assistants a SET task_backend=jsonb_build_object(
|
||||
'backend_id', b.tool_id,
|
||||
'instruction_source',COALESCE(
|
||||
b.config->'argument_bindings'->'instruction',
|
||||
b.config->'argumentBindings'->'instruction',
|
||||
'{"source":"model"}'::jsonb
|
||||
),
|
||||
'instructions',''
|
||||
)
|
||||
FROM assistant_tool_bindings b JOIN tools t ON t.id=b.tool_id
|
||||
WHERE a.id=b.assistant_id AND t.type='task'
|
||||
""")
|
||||
op.execute("UPDATE background_tasks SET backend_id=tool_id, backend_type='opencode'")
|
||||
op.execute("DELETE FROM assistant_tool_bindings WHERE tool_id IN (SELECT id FROM tools WHERE type='task')")
|
||||
op.execute("DELETE FROM tools WHERE type='task'")
|
||||
op.drop_constraint("ck_tools_external_type", "tools", type_="check")
|
||||
op.create_check_constraint("ck_tools_external_type", "tools", "type IN ('http', 'mcp', 'client')")
|
||||
|
||||
|
||||
def downgrade():
|
||||
raise RuntimeError(
|
||||
"Task backend resources cannot be downgraded safely to task tools. "
|
||||
"Restore a pre-migration database backup to roll back this resource migration."
|
||||
)
|
||||
Reference in New Issue
Block a user