feat: agent versioning and model configurations override (#227)

* feat: add tests and migrations

* feat: workflow versioning among published and draft

* feat: add a new settings page to simplify workflow detail page

* fix: fix tsclient generation
This commit is contained in:
Abhishek 2026-04-08 19:20:31 +05:30 committed by GitHub
parent f5fa9ce717
commit 38d1d928b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 10158 additions and 3131 deletions

View file

@ -0,0 +1,132 @@
"""add versioning in workflow definitions
Revision ID: a399b39479fe
Revises: c71db647d354
Create Date: 2026-04-07 14:43:50.042973
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a399b39479fe"
down_revision: Union[str, None] = "c71db647d354"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_api_keys_key_hash"), table_name="api_keys")
op.create_index(op.f("ix_api_keys_key_hash"), "api_keys", ["key_hash"], unique=True)
op.add_column(
"workflow_definitions",
sa.Column(
"status", sa.String(), server_default=sa.text("'published'"), nullable=False
),
)
op.add_column(
"workflow_definitions", sa.Column("version_number", sa.Integer(), nullable=True)
)
op.add_column(
"workflow_definitions",
sa.Column("published_at", sa.DateTime(timezone=True), nullable=True),
)
op.add_column(
"workflow_definitions",
sa.Column(
"workflow_configurations",
sa.JSON(),
server_default=sa.text("'{}'::json"),
nullable=False,
),
)
op.add_column(
"workflow_definitions",
sa.Column(
"template_context_variables",
sa.JSON(),
server_default=sa.text("'{}'::json"),
nullable=False,
),
)
op.add_column(
"workflow_definitions",
sa.Column(
"call_disposition_codes",
sa.JSON(),
server_default=sa.text("'{}'::json"),
nullable=False,
),
)
op.alter_column(
"workflow_definitions",
"workflow_hash",
existing_type=sa.VARCHAR(),
nullable=True,
)
op.drop_index(
op.f("ix_workflow_hash_workflow_id"), table_name="workflow_definitions"
)
op.drop_constraint(
op.f("uq_workflow_hash_workflow_id"), "workflow_definitions", type_="unique"
)
op.create_index(
"ix_workflow_definitions_workflow_status",
"workflow_definitions",
["workflow_id", "status"],
unique=False,
)
op.add_column(
"workflows", sa.Column("released_definition_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None,
"workflows",
"workflow_definitions",
["released_definition_id"],
["id"],
use_alter=True,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflows", type_="foreignkey")
op.drop_column("workflows", "released_definition_id")
op.drop_index(
"ix_workflow_definitions_workflow_status", table_name="workflow_definitions"
)
op.create_unique_constraint(
op.f("uq_workflow_hash_workflow_id"),
"workflow_definitions",
["workflow_hash", "workflow_id"],
postgresql_nulls_not_distinct=False,
)
op.create_index(
op.f("ix_workflow_hash_workflow_id"),
"workflow_definitions",
["workflow_hash", "workflow_id"],
unique=False,
)
op.alter_column(
"workflow_definitions",
"workflow_hash",
existing_type=sa.VARCHAR(),
nullable=False,
)
op.drop_column("workflow_definitions", "call_disposition_codes")
op.drop_column("workflow_definitions", "template_context_variables")
op.drop_column("workflow_definitions", "workflow_configurations")
op.drop_column("workflow_definitions", "published_at")
op.drop_column("workflow_definitions", "version_number")
op.drop_column("workflow_definitions", "status")
op.drop_index(op.f("ix_api_keys_key_hash"), table_name="api_keys")
op.create_index(
op.f("ix_api_keys_key_hash"), "api_keys", ["key_hash"], unique=False
)
# ### end Alembic commands ###

View file

@ -0,0 +1,89 @@
"""backfill workflow definition versioning
Copy workflow_configurations, template_context_variables, call_disposition_codes
from the workflows table into the is_current=True definition for each workflow.
Set that definition as status='published', version_number=1.
Set all other definitions to status='archived'.
Point workflows.released_definition_id to the published definition.
Revision ID: d688d0da1123
Revises: a399b39479fe
Create Date: 2026-04-07 15:00:00.000000
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "d688d0da1123"
down_revision: Union[str, None] = "a399b39479fe"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
conn = op.get_bind()
# Step 1: For each workflow's is_current=True definition, copy configs from
# the workflow table and mark as published with version_number=1.
conn.execute(
sa.text("""
UPDATE workflow_definitions wd
SET
workflow_configurations = w.workflow_configurations,
template_context_variables = w.template_context_variables,
status = 'published',
version_number = 1,
published_at = wd.created_at
FROM workflows w
WHERE wd.workflow_id = w.id
AND wd.is_current = true
""")
)
# Step 2: Mark all pre-versioning non-current definitions as legacy.
conn.execute(
sa.text("""
UPDATE workflow_definitions
SET status = 'legacy'
WHERE is_current = false
""")
)
# Step 3: Set released_definition_id on workflows to their published definition.
conn.execute(
sa.text("""
UPDATE workflows w
SET released_definition_id = wd.id
FROM workflow_definitions wd
WHERE wd.workflow_id = w.id
AND wd.is_current = true
""")
)
def downgrade() -> None:
conn = op.get_bind()
# Clear the released pointer
conn.execute(
sa.text("""
UPDATE workflows SET released_definition_id = NULL
""")
)
# Reset all definitions back to server defaults
conn.execute(
sa.text("""
UPDATE workflow_definitions
SET
status = 'published',
version_number = NULL,
published_at = NULL,
workflow_configurations = '{}',
template_context_variables = '{}'
""")
)