Initial Commit 🚀 🚀

This commit is contained in:
Abhishek Kumar 2025-09-09 14:37:32 +05:30
commit 4f2a629340
444 changed files with 76863 additions and 0 deletions

111
api/alembic/env.py Normal file
View file

@ -0,0 +1,111 @@
import asyncio
import sys
from logging.config import fileConfig
from pathlib import Path
import alembic_postgresql_enum # noqa: F401 - registers enum handling hooks
from alembic import context
from sqlalchemy import pool
from sqlalchemy.ext.asyncio import create_async_engine
from api.constants import DATABASE_URL
# Add parent directory to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
# Interpret the config file for Python logging.
config = context.config
fileConfig(config.config_file_name)
# Import your model's MetaData object for 'autogenerate' support.
from api.db.models import Base # noqa: E402 ensure this points to your models.py
target_metadata = Base.metadata
def get_url():
"""Get database URL from environment variable or config file."""
return DATABASE_URL or config.get_main_option("sqlalchemy.url")
def run_migrations_offline():
"""Run migrations in 'offline' mode."""
url = get_url()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
include_object=include_object,
render_item=render_item,
compare_type=True,
compare_server_default=True,
)
with context.begin_transaction():
context.run_migrations()
def include_object(object, name, type_, reflected, compare_to):
"""
Custom function to exclude redundant indexes on primary keys.
"""
# Unused parameters are required by Alembic's API
_ = (name, reflected, compare_to)
if type_ == "index":
# Skip indexes on primary key columns that aren't unique
# Primary keys already have implicit unique indexes
if hasattr(object, "columns") and len(object.columns) == 1:
col = list(object.columns)[0]
if col.primary_key and not object.unique:
return False
return True
def render_item(type_, obj, autogen_context):
"""
Custom render function to fix index generation.
"""
# Unused parameter is required by Alembic's API
_ = autogen_context
if type_ == "index":
# For indexes on columns marked as unique in the model,
# ensure the index is also unique
if hasattr(obj, "columns"):
for col in obj.columns:
if hasattr(col, "unique") and col.unique and not obj.unique:
obj.unique = True
break
return False # Let Alembic handle the rendering
def do_run_migrations(connection):
context.configure(
connection=connection,
target_metadata=target_metadata,
include_object=include_object,
render_item=render_item,
compare_type=True,
compare_server_default=True,
)
with context.begin_transaction():
context.run_migrations()
async def run_migrations_online():
"""Run migrations in 'online' mode."""
connectable = create_async_engine(
get_url(),
poolclass=pool.NullPool,
)
async with connectable.connect() as connection:
# Run migrations using the synchronous 'do_run_migrations' function
await connection.run_sync(do_run_migrations)
await connectable.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
asyncio.run(run_migrations_online())

View file

@ -0,0 +1,26 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View file

@ -0,0 +1,91 @@
"""added campaign table
Revision ID: 08bb6e7f1397
Revises: 0c1223cc266f
Create Date: 2025-07-23 18:46:38.955381
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "08bb6e7f1397"
down_revision: Union[str, None] = "0c1223cc266f"
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.create_table(
"campaigns",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.Column("workflow_id", sa.Integer(), nullable=False),
sa.Column("created_by", sa.Integer(), nullable=False),
sa.Column("source_type", sa.String(), nullable=False),
sa.Column("source_id", sa.String(), nullable=False),
sa.Column(
"state",
sa.Enum(
"created",
"running",
"paused",
"completed",
"failed",
name="campaign_state",
),
nullable=False,
),
sa.Column("total_rows", sa.Integer(), nullable=True),
sa.Column("processed_rows", sa.Integer(), nullable=False),
sa.Column("failed_rows", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["created_by"],
["users.id"],
),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.id"],
),
sa.ForeignKeyConstraint(
["workflow_id"],
["workflows.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_campaigns_id"), "campaigns", ["id"], unique=False)
op.create_index(op.f("ix_campaigns_name"), "campaigns", ["name"], unique=False)
op.create_index(
"ix_campaigns_org_id", "campaigns", ["organization_id"], unique=False
)
op.create_index("ix_campaigns_state", "campaigns", ["state"], unique=False)
op.create_index(
"ix_campaigns_workflow_id", "campaigns", ["workflow_id"], unique=False
)
op.add_column(
"workflow_runs", sa.Column("campaign_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(None, "workflow_runs", "campaigns", ["campaign_id"], ["id"])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflow_runs", type_="foreignkey")
op.drop_column("workflow_runs", "campaign_id")
op.drop_index("ix_campaigns_workflow_id", table_name="campaigns")
op.drop_index("ix_campaigns_state", table_name="campaigns")
op.drop_index("ix_campaigns_org_id", table_name="campaigns")
op.drop_index(op.f("ix_campaigns_name"), table_name="campaigns")
op.drop_index(op.f("ix_campaigns_id"), table_name="campaigns")
op.drop_table("campaigns")
# ### end Alembic commands ###

View file

@ -0,0 +1,207 @@
"""make json not nullable
Revision ID: 0c1223cc266f
Revises: 0fe708f2acb9
Create Date: 2025-07-21 14:36:31.182969
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "0c1223cc266f"
down_revision: Union[str, None] = "0fe708f2acb9"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Update existing NULL values to empty dict for workflows table
op.execute("""
UPDATE workflows
SET template_context_variables = '{}'::jsonb
WHERE template_context_variables IS NULL
""")
op.execute("""
UPDATE workflows
SET call_disposition_codes = '{}'::jsonb
WHERE call_disposition_codes IS NULL
""")
# Update existing NULL values to empty dict for workflow_runs table
op.execute("""
UPDATE workflow_runs
SET usage_info = '{}'::jsonb
WHERE usage_info IS NULL
""")
op.execute("""
UPDATE workflow_runs
SET cost_info = '{}'::jsonb
WHERE cost_info IS NULL
""")
op.execute("""
UPDATE workflow_runs
SET initial_context = '{}'::jsonb
WHERE initial_context IS NULL
""")
op.execute("""
UPDATE workflow_runs
SET gathered_context = '{}'::jsonb
WHERE gathered_context IS NULL
""")
op.execute("""
UPDATE workflow_runs
SET annotations = '{}'::jsonb
WHERE annotations IS NULL
""")
# Update existing NULL values to empty dict for looptalk_test_sessions table
op.execute("""
UPDATE looptalk_test_sessions
SET results = '{}'::jsonb
WHERE results IS NULL
""")
# Update existing NULL values to empty dict for looptalk_conversations table
op.execute("""
UPDATE looptalk_conversations
SET transcript = '{}'::jsonb
WHERE transcript IS NULL
""")
op.execute("""
UPDATE looptalk_conversations
SET metrics = '{}'::jsonb
WHERE metrics IS NULL
""")
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"looptalk_conversations",
"transcript",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"looptalk_conversations",
"metrics",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"looptalk_test_sessions",
"results",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflow_runs",
"usage_info",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflow_runs",
"cost_info",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflow_runs",
"initial_context",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflow_runs",
"gathered_context",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflow_runs",
"annotations",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflows",
"template_context_variables",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
op.alter_column(
"workflows",
"call_disposition_codes",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"workflows",
"call_disposition_codes",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflows",
"template_context_variables",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflow_runs",
"annotations",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflow_runs",
"gathered_context",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflow_runs",
"initial_context",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflow_runs",
"cost_info",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"workflow_runs",
"usage_info",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"looptalk_test_sessions",
"results",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"looptalk_conversations",
"metrics",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
op.alter_column(
"looptalk_conversations",
"transcript",
existing_type=postgresql.JSON(astext_type=sa.Text()),
nullable=True,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,51 @@
"""fix unique constraint on workflow definition
Revision ID: 0c1bbc83fe9e
Revises: 9f5f2d35f6fb
Create Date: 2025-06-04 15:56:02.979658
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "0c1bbc83fe9e"
down_revision: Union[str, None] = "9f5f2d35f6fb"
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(
"ix_workflow_definitions_workflow_hash", table_name="workflow_definitions"
)
op.create_index(
"ix_workflow_hash_workflow_id",
"workflow_definitions",
["workflow_hash", "workflow_id"],
unique=False,
)
op.create_unique_constraint(
"uq_workflow_hash_workflow_id",
"workflow_definitions",
["workflow_hash", "workflow_id"],
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(
"uq_workflow_hash_workflow_id", "workflow_definitions", type_="unique"
)
op.drop_index("ix_workflow_hash_workflow_id", table_name="workflow_definitions")
op.create_index(
"ix_workflow_definitions_workflow_hash",
"workflow_definitions",
["workflow_hash"],
unique=True,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,32 @@
"""add call disposition codes
Revision ID: 0fe708f2acb9
Revises: 1225ac786848
Create Date: 2025-07-21 14:27:42.942689
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "0fe708f2acb9"
down_revision: Union[str, None] = "1225ac786848"
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.add_column(
"workflows", sa.Column("call_disposition_codes", sa.JSON(), nullable=True)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflows", "call_disposition_codes")
# ### end Alembic commands ###

View file

@ -0,0 +1,63 @@
"""add organization configurations
Revision ID: 1225ac786848
Revises: 1da1d650c0e4
Create Date: 2025-07-12 16:32:04.725072
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "1225ac786848"
down_revision: Union[str, None] = "1da1d650c0e4"
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.create_table(
"organization_configurations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.Column("key", sa.String(), nullable=False),
sa.Column("value", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["organization_id"], ["organizations.id"], ondelete="CASCADE"
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("organization_id", "key", name="_organization_key_uc"),
)
op.create_index(
op.f("ix_organization_configurations_id"),
"organization_configurations",
["id"],
unique=False,
)
op.create_index(
"ix_organization_configurations_organization_id",
"organization_configurations",
["organization_id"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
"ix_organization_configurations_organization_id",
table_name="organization_configurations",
)
op.drop_index(
op.f("ix_organization_configurations_id"),
table_name="organization_configurations",
)
op.drop_table("organization_configurations")
# ### end Alembic commands ###

View file

@ -0,0 +1,39 @@
"""add workflow run mode
Revision ID: 13ccd6e1f5ad
Revises: 4735a1f0cdb3
Create Date: 2025-07-28 17:44:10.827671
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "13ccd6e1f5ad"
down_revision: Union[str, None] = "4735a1f0cdb3"
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.execute(
"ALTER TYPE workflow_run_mode ADD VALUE IF NOT EXISTS 'webrtc' AFTER 'CHAT';"
)
op.execute(
"ALTER TYPE workflow_run_mode ADD VALUE IF NOT EXISTS 'smallwebrtc' AFTER 'webrtc';"
)
op.execute(
"ALTER TYPE workflow_run_mode ADD VALUE IF NOT EXISTS 'twilio' AFTER 'smallwebrtc';"
)
op.execute(
"ALTER TYPE workflow_run_mode ADD VALUE IF NOT EXISTS 'stasis' AFTER 'twilio';"
)
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -0,0 +1,48 @@
"""Add user model
Revision ID: 1d441e79db94
Revises: 2d6e2f41caa2
Create Date: 2025-04-16 17:40:04.699129
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "1d441e79db94"
down_revision: Union[str, None] = "2d6e2f41caa2"
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.create_table(
"users",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("provider_id", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_users_id"), "users", ["id"], unique=False)
op.create_index(op.f("ix_users_provider_id"), "users", ["provider_id"], unique=True)
op.add_column("livekit_rooms", sa.Column("user_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "livekit_rooms", "users", ["user_id"], ["id"])
op.add_column("workflows", sa.Column("user_id", sa.Integer(), nullable=True))
op.create_foreign_key(None, "workflows", "users", ["user_id"], ["id"])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflows", type_="foreignkey")
op.drop_column("workflows", "user_id")
op.drop_constraint(None, "livekit_rooms", type_="foreignkey")
op.drop_column("livekit_rooms", "user_id")
op.drop_index(op.f("ix_users_provider_id"), table_name="users")
op.drop_index(op.f("ix_users_id"), table_name="users")
op.drop_table("users")
# ### end Alembic commands ###

View file

@ -0,0 +1,38 @@
"""add total call duration to usage
Revision ID: 1da1d650c0e4
Revises: ec010596a0b4
Create Date: 2025-07-08 15:41:15.039999
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "1da1d650c0e4"
down_revision: Union[str, None] = "ec010596a0b4"
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.add_column(
"organization_usage_cycles",
sa.Column(
"total_duration_seconds",
sa.Integer(),
nullable=False,
server_default=sa.text("0"),
),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("organization_usage_cycles", "total_duration_seconds")
# ### end Alembic commands ###

View file

@ -0,0 +1,30 @@
"""add provider
Revision ID: 20c780c2a218
Revises: ac6da37c5034
Create Date: 2025-06-09 19:23:00.111377
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "20c780c2a218"
down_revision: Union[str, None] = "ac6da37c5034"
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.add_column("integrations", sa.Column("provider", sa.String(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("integrations", "provider")
# ### end Alembic commands ###

View file

@ -0,0 +1,118 @@
"""added quota tables
Revision ID: 2159d4ac431a
Revises: e0d1a9b9f6c4
Create Date: 2025-07-08 14:38:38.542486
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "2159d4ac431a"
down_revision: Union[str, None] = "e0d1a9b9f6c4"
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! ###
# 1) Create the `quota_type` enum *before* we add the column that references it.
# Alembic does **not** automatically emit a CREATE TYPE statement for Enum
# objects when they are used inside `op.add_column()`. If we skip this step
# Postgres raises `type "quota_type" does not exist` when applying the
# migration.
quota_type_enum = sa.Enum("monthly", "annual", name="quota_type")
quota_type_enum.create(op.get_bind(), checkfirst=True)
op.create_table(
"organization_usage_cycles",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.Column("period_start", sa.DateTime(), nullable=False),
sa.Column("period_end", sa.DateTime(), nullable=False),
sa.Column("quota_dograh_tokens", sa.Integer(), nullable=False),
sa.Column("used_dograh_tokens", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"organization_id", "period_start", "period_end", name="unique_org_period"
),
)
op.create_index(
"idx_usage_cycles_org_period",
"organization_usage_cycles",
["organization_id", "period_end"],
unique=False,
)
op.create_index(
op.f("ix_organization_usage_cycles_id"),
"organization_usage_cycles",
["id"],
unique=False,
)
# Add the column now that the enum type exists.
op.add_column(
"organizations",
sa.Column(
"quota_type", quota_type_enum, nullable=False, server_default="monthly"
),
)
op.add_column(
"organizations",
sa.Column(
"quota_dograh_tokens",
sa.Integer(),
nullable=False,
server_default=sa.text("0"),
),
)
op.add_column(
"organizations",
sa.Column(
"quota_reset_day",
sa.Integer(),
nullable=False,
server_default=sa.text("LEAST(EXTRACT(DAY FROM CURRENT_DATE)::int, 28)"),
),
)
op.add_column(
"organizations", sa.Column("quota_start_date", sa.DateTime(), nullable=True)
)
op.add_column(
"organizations",
sa.Column(
"quota_enabled",
sa.Boolean(),
nullable=False,
server_default=sa.text("false"),
),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("organizations", "quota_enabled")
op.drop_column("organizations", "quota_start_date")
op.drop_column("organizations", "quota_reset_day")
op.drop_column("organizations", "quota_dograh_tokens")
op.drop_column("organizations", "quota_type")
op.drop_index(
op.f("ix_organization_usage_cycles_id"), table_name="organization_usage_cycles"
)
op.drop_index("idx_usage_cycles_org_period", table_name="organization_usage_cycles")
op.drop_table("organization_usage_cycles")
# Drop the enum type now that no column depends on it.
quota_type_enum = sa.Enum(name="quota_type")
quota_type_enum.drop(op.get_bind(), checkfirst=True)
# ### end Alembic commands ###

View file

@ -0,0 +1,46 @@
"""Add workflow run
Revision ID: 2d6e2f41caa2
Revises: bee2a9fcc6a6
Create Date: 2025-04-11 17:02:58.461460
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "2d6e2f41caa2"
down_revision: Union[str, None] = "bee2a9fcc6a6"
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.create_table(
"workflow_runs",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("workflow_id", sa.Integer(), nullable=False),
sa.Column(
"mode", sa.Enum("VOICE", "CHAT", name="workflow_run_mode"), nullable=False
),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["workflow_id"],
["workflows.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_workflow_runs_id"), "workflow_runs", ["id"], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_workflow_runs_id"), table_name="workflow_runs")
op.drop_table("workflow_runs")
# ### end Alembic commands ###

View file

@ -0,0 +1,27 @@
"""fix defaults
Revision ID: 2dfee251117b
Revises: 2159d4ac431a
Create Date: 2025-07-08 14:47:40.747689
"""
from typing import Sequence, Union
# revision identifiers, used by Alembic.
revision: str = "2dfee251117b"
down_revision: Union[str, None] = "2159d4ac431a"
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! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -0,0 +1,30 @@
"""add annotations in workflow run model
Revision ID: 2ed4baa89f15
Revises: cdc80a4fd2dd
Create Date: 2025-06-17 13:30:43.784644
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "2ed4baa89f15"
down_revision: Union[str, None] = "cdc80a4fd2dd"
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.add_column("workflow_runs", sa.Column("annotations", sa.JSON(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflow_runs", "annotations")
# ### end Alembic commands ###

View file

@ -0,0 +1,38 @@
"""add workflow configurations
Revision ID: 3717ae6146e2
Revises: f952c9c1105a
Create Date: 2025-08-19 14:41:44.852794
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "3717ae6146e2"
down_revision: Union[str, None] = "f952c9c1105a"
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.add_column(
"workflows",
sa.Column(
"workflow_configurations",
sa.JSON(),
nullable=False,
server_default=sa.text("'{}'"),
),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflows", "workflow_configurations")
# ### end Alembic commands ###

View file

@ -0,0 +1,60 @@
"""Add API Keys model
Revision ID: 37d0a90fccba
Revises: c425d3445750
Create Date: 2025-09-06 14:19:35.300761
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "37d0a90fccba"
down_revision: Union[str, None] = "c425d3445750"
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.create_table(
"api_keys",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("key_hash", sa.String(), nullable=False),
sa.Column("key_prefix", sa.String(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("created_by", sa.Integer(), nullable=True),
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("archived_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["created_by"],
["users.id"],
),
sa.ForeignKeyConstraint(
["organization_id"], ["organizations.id"], ondelete="CASCADE"
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index("ix_api_keys_active", "api_keys", ["is_active"], unique=False)
op.create_index(op.f("ix_api_keys_id"), "api_keys", ["id"], unique=False)
op.create_index("ix_api_keys_key_hash", "api_keys", ["key_hash"], unique=True)
op.create_index(
"ix_api_keys_organization_id", "api_keys", ["organization_id"], unique=False
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index("ix_api_keys_organization_id", table_name="api_keys")
op.drop_index("ix_api_keys_key_hash", table_name="api_keys")
op.drop_index(op.f("ix_api_keys_id"), table_name="api_keys")
op.drop_index("ix_api_keys_active", table_name="api_keys")
op.drop_table("api_keys")
# ### end Alembic commands ###

View file

@ -0,0 +1,44 @@
"""add user configuration
Revision ID: 3a0384c5ab2e
Revises: 1d441e79db94
Create Date: 2025-04-24 12:26:33.635090
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "3a0384c5ab2e"
down_revision: Union[str, None] = "1d441e79db94"
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.create_table(
"user_configurations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=True),
sa.Column("configuration", sa.JSON(), nullable=False),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_user_configurations_id"), "user_configurations", ["id"], unique=False
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_user_configurations_id"), table_name="user_configurations")
op.drop_table("user_configurations")
# ### end Alembic commands ###

View file

@ -0,0 +1,64 @@
"""Added livekit room
Revision ID: 3a30110d7cd7
Revises: 93a1ddbb6ffd
Create Date: 2025-04-05 15:03:00.749510
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "3a30110d7cd7"
down_revision: Union[str, None] = "93a1ddbb6ffd"
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.create_table(
"livekit_rooms",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column(
"type", sa.Enum("VOICE", "TEXT", "VIDEO", name="room_type"), nullable=False
),
sa.Column(
"state",
sa.Enum(
"CONNECTED",
"DISCONNECTED",
"RECONNECTING",
"RECONNECTED",
name="room_state",
),
nullable=False,
),
sa.Column("name", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_livekit_rooms_id"), "livekit_rooms", ["id"], unique=False)
op.create_index(
op.f("ix_livekit_rooms_name"), "livekit_rooms", ["name"], unique=False
)
op.create_index(
op.f("ix_livekit_rooms_state"), "livekit_rooms", ["state"], unique=False
)
op.create_index(
op.f("ix_livekit_rooms_type"), "livekit_rooms", ["type"], unique=False
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_livekit_rooms_type"), table_name="livekit_rooms")
op.drop_index(op.f("ix_livekit_rooms_state"), table_name="livekit_rooms")
op.drop_index(op.f("ix_livekit_rooms_name"), table_name="livekit_rooms")
op.drop_index(op.f("ix_livekit_rooms_id"), table_name="livekit_rooms")
op.drop_table("livekit_rooms")
# ### end Alembic commands ###

View file

@ -0,0 +1,36 @@
"""add selected organisation id field in user model
Revision ID: 45fa7fec2993
Revises: f6f19156bcb7
Create Date: 2025-06-09 16:00:52.209779
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "45fa7fec2993"
down_revision: Union[str, None] = "f6f19156bcb7"
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.add_column(
"users", sa.Column("selected_organization_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "users", "organizations", ["selected_organization_id"], ["id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "users", type_="foreignkey")
op.drop_column("users", "selected_organization_id")
# ### end Alembic commands ###

View file

@ -0,0 +1,94 @@
"""add queued runs table
Revision ID: 4735a1f0cdb3
Revises: 08bb6e7f1397
Create Date: 2025-07-24 16:04:07.899092
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "4735a1f0cdb3"
down_revision: Union[str, None] = "08bb6e7f1397"
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.create_table(
"queued_runs",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("campaign_id", sa.Integer(), nullable=False),
sa.Column("source_uuid", sa.String(), nullable=False),
sa.Column("context_variables", sa.JSON(), nullable=False),
sa.Column(
"state",
sa.Enum("queued", "processed", name="queued_run_state"),
nullable=False,
),
sa.Column("workflow_run_id", sa.Integer(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("processed_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(["campaign_id"], ["campaigns.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(
["workflow_run_id"],
["workflow_runs.id"],
),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint(
"campaign_id", "source_uuid", name="unique_campaign_source_uuid"
),
)
op.create_index(
"idx_queued_runs_campaign_state",
"queued_runs",
["campaign_id", "state"],
unique=False,
)
op.create_index(
"idx_queued_runs_created", "queued_runs", ["created_at"], unique=False
)
op.create_index(
"idx_queued_runs_source_uuid", "queued_runs", ["source_uuid"], unique=False
)
op.create_index(op.f("ix_queued_runs_id"), "queued_runs", ["id"], unique=False)
op.add_column(
"campaigns", sa.Column("rate_limit_per_second", sa.Integer(), nullable=False)
)
op.add_column("campaigns", sa.Column("max_retries", sa.Integer(), nullable=False))
op.add_column(
"campaigns", sa.Column("source_sync_status", sa.String(), nullable=False)
)
op.add_column(
"campaigns",
sa.Column("source_last_synced_at", sa.DateTime(timezone=True), nullable=True),
)
op.add_column(
"campaigns", sa.Column("source_sync_error", sa.String(), nullable=True)
)
# Add syncing in the campaign_state
op.execute(
"ALTER TYPE campaign_state ADD VALUE IF NOT EXISTS 'syncing' AFTER 'created';"
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("campaigns", "source_sync_error")
op.drop_column("campaigns", "source_last_synced_at")
op.drop_column("campaigns", "source_sync_status")
op.drop_column("campaigns", "max_retries")
op.drop_column("campaigns", "rate_limit_per_second")
op.drop_index(op.f("ix_queued_runs_id"), table_name="queued_runs")
op.drop_index("idx_queued_runs_source_uuid", table_name="queued_runs")
op.drop_index("idx_queued_runs_created", table_name="queued_runs")
op.drop_index("idx_queued_runs_campaign_state", table_name="queued_runs")
op.drop_table("queued_runs")
# ### end Alembic commands ###

View file

@ -0,0 +1,36 @@
"""add organisation_id in workflow
Revision ID: 477b47ce346b
Revises: fec0fb9a8db7
Create Date: 2025-08-15 14:11:28.088841
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "477b47ce346b"
down_revision: Union[str, None] = "fec0fb9a8db7"
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.add_column(
"workflows", sa.Column("organization_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "workflows", "organizations", ["organization_id"], ["id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflows", type_="foreignkey")
op.drop_column("workflows", "organization_id")
# ### end Alembic commands ###

View file

@ -0,0 +1,32 @@
"""add teplate context variables in workflow
Revision ID: 5253971e3f03
Revises: 0c1bbc83fe9e
Create Date: 2025-06-05 14:06:51.059664
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "5253971e3f03"
down_revision: Union[str, None] = "0c1bbc83fe9e"
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.add_column(
"workflows", sa.Column("template_context_variables", sa.JSON(), nullable=True)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflows", "template_context_variables")
# ### end Alembic commands ###

View file

@ -0,0 +1,66 @@
"""add workflow definition
Revision ID: 58f17b468b3c
Revises: a2b092ff7282
Create Date: 2025-06-03 13:08:23.709070
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "58f17b468b3c"
down_revision: Union[str, None] = "a2b092ff7282"
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.create_table(
"workflow_definitions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("workflow_hash", sa.String(), nullable=False),
sa.Column("workflow_json", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_workflow_definitions_id"), "workflow_definitions", ["id"], unique=False
)
op.create_index(
op.f("ix_workflow_definitions_workflow_hash"),
"workflow_definitions",
["workflow_hash"],
unique=True,
)
op.add_column(
"workflow_runs", sa.Column("definition_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "workflow_runs", "workflow_definitions", ["definition_id"], ["id"]
)
op.add_column(
"workflows", sa.Column("current_definition_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "workflows", "workflow_definitions", ["current_definition_id"], ["id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflows", type_="foreignkey")
op.drop_column("workflows", "current_definition_id")
op.drop_constraint(None, "workflow_runs", type_="foreignkey")
op.drop_column("workflow_runs", "definition_id")
op.drop_index(
op.f("ix_workflow_definitions_workflow_hash"), table_name="workflow_definitions"
)
op.drop_index(op.f("ix_workflow_definitions_id"), table_name="workflow_definitions")
op.drop_table("workflow_definitions")
# ### end Alembic commands ###

View file

@ -0,0 +1,71 @@
"""add timezone info on tables
Revision ID: 594f16adf97c
Revises: 2dfee251117b
Create Date: 2025-07-08 15:01:26.642043
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "594f16adf97c"
down_revision: Union[str, None] = "2dfee251117b"
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.alter_column(
"organization_usage_cycles",
"period_start",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
)
op.alter_column(
"organization_usage_cycles",
"period_end",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=False,
)
op.alter_column(
"organizations",
"quota_start_date",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=True,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"organizations",
"quota_start_date",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=True,
)
op.alter_column(
"organization_usage_cycles",
"period_end",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
)
op.alter_column(
"organization_usage_cycles",
"period_start",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=False,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,54 @@
"""add status in workflow
Revision ID: 693a865c011f
Revises: 477b47ce346b
Create Date: 2025-08-15 14:27:46.748114
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "693a865c011f"
down_revision: Union[str, None] = "477b47ce346b"
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! ###
# First create the enum type
workflow_status_enum = sa.Enum("active", "archived", name="workflow_status")
workflow_status_enum.create(op.get_bind(), checkfirst=True)
# Then add the column with the enum type
op.add_column(
"workflows",
sa.Column(
"status",
workflow_status_enum,
nullable=False,
server_default=sa.text("'active'"),
),
)
# Create index for better query performance
op.create_index("ix_workflows_status", "workflows", ["status"])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# Drop index first
op.drop_index("ix_workflows_status", table_name="workflows")
# Drop the column
op.drop_column("workflows", "status")
# Drop the enum type
workflow_status_enum = sa.Enum("active", "archived", name="workflow_status")
workflow_status_enum.drop(op.get_bind(), checkfirst=True)
# ### end Alembic commands ###

View file

@ -0,0 +1,50 @@
"""add workflow template
Revision ID: 7e90cc8d025b
Revises: 9f25ff8f3cbd
Create Date: 2025-06-10 18:17:55.080973
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "7e90cc8d025b"
down_revision: Union[str, None] = "9f25ff8f3cbd"
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.create_table(
"workflow_templates",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("template_name", sa.String(), nullable=False),
sa.Column("template_json", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_workflow_templates_id"), "workflow_templates", ["id"], unique=False
)
op.create_index(
op.f("ix_workflow_templates_template_name"),
"workflow_templates",
["template_name"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
op.f("ix_workflow_templates_template_name"), table_name="workflow_templates"
)
op.drop_index(op.f("ix_workflow_templates_id"), table_name="workflow_templates")
op.drop_table("workflow_templates")
# ### end Alembic commands ###

View file

@ -0,0 +1,32 @@
"""add price_per_second_usd
Revision ID: 7feef09d7cc6
Revises: 3717ae6146e2
Create Date: 2025-08-25 13:04:13.436156
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "7feef09d7cc6"
down_revision: Union[str, None] = "3717ae6146e2"
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.add_column(
"organizations", sa.Column("price_per_second_usd", sa.Float(), nullable=True)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("organizations", "price_per_second_usd")
# ### end Alembic commands ###

View file

@ -0,0 +1,41 @@
"""Add workflow model
Revision ID: 93a1ddbb6ffd
Revises:
Create Date: 2025-04-03 20:06:47.970117
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "93a1ddbb6ffd"
down_revision: Union[str, None] = None
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.create_table(
"workflows",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column("workflow_definition", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_workflows_id"), "workflows", ["id"], unique=False)
op.create_index(op.f("ix_workflows_name"), "workflows", ["name"], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_workflows_name"), table_name="workflows")
op.drop_index(op.f("ix_workflows_id"), table_name="workflows")
op.drop_table("workflows")
# ### end Alembic commands ###

View file

@ -0,0 +1,30 @@
"""add is_superuser to user model
Revision ID: 9641b4f306cd
Revises: 3a0384c5ab2e
Create Date: 2025-05-07 11:37:44.295774
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "9641b4f306cd"
down_revision: Union[str, None] = "3a0384c5ab2e"
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.add_column("users", sa.Column("is_superuser", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("users", "is_superuser")
# ### end Alembic commands ###

View file

@ -0,0 +1,87 @@
"""add storage backend in workflow
Revision ID: 982ec8e434be
Revises: d0060de90c18
Create Date: 2025-09-09 10:39:12.894626
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from alembic_postgresql_enum import TableReference
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "982ec8e434be"
down_revision: Union[str, None] = "d0060de90c18"
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! ###
sa.Enum("s3", "minio", name="storage_backend").create(op.get_bind())
op.drop_constraint(
"queued_runs_workflow_run_id_fkey", "queued_runs", type_="foreignkey"
)
op.drop_column("queued_runs", "workflow_run_id")
op.add_column(
"workflow_runs",
sa.Column(
"storage_backend",
postgresql.ENUM("s3", "minio", name="storage_backend", create_type=False),
server_default=sa.text("'s3'::storage_backend"),
nullable=False,
),
)
sa.Enum("VOICE", "TEXT", "VIDEO", name="room_type").drop(op.get_bind())
sa.Enum(
"CONNECTED", "DISCONNECTED", "RECONNECTING", "RECONNECTED", name="room_state"
).drop(op.get_bind())
op.sync_enum_values(
enum_schema="public",
enum_name="workflow_run_mode",
new_values=["twilio", "stasis", "webrtc", "smallwebrtc", "VOICE", "CHAT"],
affected_columns=[
TableReference(
table_schema="public", table_name="workflow_runs", column_name="mode"
)
],
enum_values_to_rename=[],
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.sync_enum_values(
enum_schema="public",
enum_name="workflow_run_mode",
new_values=["VOICE", "CHAT", "webrtc", "smallwebrtc", "twilio", "stasis"],
affected_columns=[
TableReference(
table_schema="public", table_name="workflow_runs", column_name="mode"
)
],
enum_values_to_rename=[],
)
sa.Enum(
"CONNECTED", "DISCONNECTED", "RECONNECTING", "RECONNECTED", name="room_state"
).create(op.get_bind())
sa.Enum("VOICE", "TEXT", "VIDEO", name="room_type").create(op.get_bind())
op.drop_column("workflow_runs", "storage_backend")
op.add_column(
"queued_runs",
sa.Column("workflow_run_id", sa.INTEGER(), autoincrement=False, nullable=True),
)
op.create_foreign_key(
"queued_runs_workflow_run_id_fkey",
"queued_runs",
"workflow_runs",
["workflow_run_id"],
["id"],
)
sa.Enum("s3", "minio", name="storage_backend").drop(op.get_bind())
# ### end Alembic commands ###

View file

@ -0,0 +1,40 @@
"""add recording and transcript urls in workflow run
Revision ID: 9be6240baa00
Revises: 9641b4f306cd
Create Date: 2025-05-09 21:50:03.369283
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "9be6240baa00"
down_revision: Union[str, None] = "9641b4f306cd"
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.add_column(
"workflow_runs", sa.Column("is_completed", sa.Boolean(), nullable=True)
)
op.add_column(
"workflow_runs", sa.Column("recording_url", sa.String(), nullable=True)
)
op.add_column(
"workflow_runs", sa.Column("transcript_url", sa.String(), nullable=True)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflow_runs", "transcript_url")
op.drop_column("workflow_runs", "recording_url")
op.drop_column("workflow_runs", "is_completed")
# ### end Alembic commands ###

View file

@ -0,0 +1,36 @@
"""add workflow id in workflow definition
Revision ID: 9ef49df72862
Revises: 58f17b468b3c
Create Date: 2025-06-04 10:54:01.714149
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "9ef49df72862"
down_revision: Union[str, None] = "58f17b468b3c"
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.add_column(
"workflow_definitions", sa.Column("workflow_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "workflow_definitions", "workflows", ["workflow_id"], ["id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflow_definitions", type_="foreignkey")
op.drop_column("workflow_definitions", "workflow_id")
# ### end Alembic commands ###

View file

@ -0,0 +1,30 @@
"""add actions
Revision ID: 9f25ff8f3cbd
Revises: d11fbd083a55
Create Date: 2025-06-09 20:16:19.418495
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "9f25ff8f3cbd"
down_revision: Union[str, None] = "d11fbd083a55"
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.add_column("integrations", sa.Column("action", sa.String(), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("integrations", "action")
# ### end Alembic commands ###

View file

@ -0,0 +1,52 @@
"""remove workflow definition id from workflow
Revision ID: 9f5f2d35f6fb
Revises: 9ef49df72862
Create Date: 2025-06-04 13:27:45.776741
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "9f5f2d35f6fb"
down_revision: Union[str, None] = "9ef49df72862"
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.add_column(
"workflow_definitions",
sa.Column(
"is_current", sa.Boolean(), nullable=False, server_default=sa.false()
),
)
op.drop_constraint(
"workflows_current_definition_id_fkey", "workflows", type_="foreignkey"
)
op.drop_column("workflows", "current_definition_id")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"workflows",
sa.Column(
"current_definition_id", sa.INTEGER(), autoincrement=False, nullable=True
),
)
op.create_foreign_key(
"workflows_current_definition_id_fkey",
"workflows",
"workflow_definitions",
["current_definition_id"],
["id"],
)
op.drop_column("workflow_definitions", "is_current")
# ### end Alembic commands ###

View file

@ -0,0 +1,33 @@
"""add last validated at
Revision ID: a29b05f31ddf
Revises: 9be6240baa00
Create Date: 2025-05-21 22:59:27.008241
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a29b05f31ddf"
down_revision: Union[str, None] = "9be6240baa00"
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.add_column(
"user_configurations",
sa.Column("last_validated_at", sa.DateTime(timezone=True), nullable=True),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("user_configurations", "last_validated_at")
# ### end Alembic commands ###

View file

@ -0,0 +1,40 @@
"""add usage info
Revision ID: a2b092ff7282
Revises: a29b05f31ddf
Create Date: 2025-05-29 20:25:37.110818
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a2b092ff7282"
down_revision: Union[str, None] = "a29b05f31ddf"
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.add_column("workflow_runs", sa.Column("usage_info", sa.JSON(), nullable=True))
op.add_column("workflow_runs", sa.Column("cost_info", sa.JSON(), nullable=True))
op.add_column(
"workflow_runs", sa.Column("initial_context", sa.JSON(), nullable=True)
)
op.add_column(
"workflow_runs", sa.Column("gathered_context", sa.JSON(), nullable=True)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflow_runs", "gathered_context")
op.drop_column("workflow_runs", "initial_context")
op.drop_column("workflow_runs", "cost_info")
op.drop_column("workflow_runs", "usage_info")
# ### end Alembic commands ###

View file

@ -0,0 +1,38 @@
"""add log in workflow run model
Revision ID: a75ae71af479
Revises: 13ccd6e1f5ad
Create Date: 2025-08-05 22:37:06.248630
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "a75ae71af479"
down_revision: Union[str, None] = "13ccd6e1f5ad"
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.add_column(
"workflow_runs",
sa.Column(
"logs",
sa.JSON(),
nullable=False,
server_default=sa.text("'{}'"),
),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("workflow_runs", "logs")
# ### end Alembic commands ###

View file

@ -0,0 +1,32 @@
"""add created by in integration
Revision ID: ac6da37c5034
Revises: d666f3244648
Create Date: 2025-06-09 17:27:37.088950
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "ac6da37c5034"
down_revision: Union[str, None] = "d666f3244648"
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.add_column("integrations", sa.Column("created_by", sa.Integer(), nullable=True))
op.create_foreign_key(None, "integrations", "users", ["created_by"], ["id"])
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "integrations", type_="foreignkey")
op.drop_column("integrations", "created_by")
# ### end Alembic commands ###

View file

@ -0,0 +1,57 @@
"""fix datetime to be in utc
Revision ID: bee2a9fcc6a6
Revises: 3a30110d7cd7
Create Date: 2025-04-05 15:40:00.807226
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "bee2a9fcc6a6"
down_revision: Union[str, None] = "3a30110d7cd7"
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.alter_column(
"livekit_rooms",
"created_at",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=True,
)
op.alter_column(
"workflows",
"created_at",
existing_type=postgresql.TIMESTAMP(),
type_=sa.DateTime(timezone=True),
existing_nullable=True,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"workflows",
"created_at",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=True,
)
op.alter_column(
"livekit_rooms",
"created_at",
existing_type=sa.DateTime(timezone=True),
type_=postgresql.TIMESTAMP(),
existing_nullable=True,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,38 @@
"""add columns in usage table
Revision ID: c425d3445750
Revises: 7feef09d7cc6
Create Date: 2025-08-25 13:37:15.524965
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "c425d3445750"
down_revision: Union[str, None] = "7feef09d7cc6"
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.add_column(
"organization_usage_cycles",
sa.Column("used_amount_usd", sa.Float(), nullable=True),
)
op.add_column(
"organization_usage_cycles",
sa.Column("quota_amount_usd", sa.Float(), nullable=True),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("organization_usage_cycles", "quota_amount_usd")
op.drop_column("organization_usage_cycles", "used_amount_usd")
# ### end Alembic commands ###

View file

@ -0,0 +1,43 @@
"""add template description
Revision ID: cdc80a4fd2dd
Revises: 7e90cc8d025b
Create Date: 2025-06-10 18:31:30.952698
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "cdc80a4fd2dd"
down_revision: Union[str, None] = "7e90cc8d025b"
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.add_column(
"workflow_templates",
sa.Column("template_description", sa.String(), nullable=False),
)
op.create_index(
op.f("ix_workflow_templates_template_description"),
"workflow_templates",
["template_description"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
op.f("ix_workflow_templates_template_description"),
table_name="workflow_templates",
)
op.drop_column("workflow_templates", "template_description")
# ### end Alembic commands ###

View file

@ -0,0 +1,63 @@
"""Fix Migrations
Revision ID: d0060de90c18
Revises: 37d0a90fccba
Create Date: 2025-09-08 13:19:03.854682
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "d0060de90c18"
down_revision: Union[str, None] = "37d0a90fccba"
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("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)
# remove redundant id indexes that got created
op.drop_index("ix_api_keys_id", table_name="api_keys")
op.drop_index("ix_users_id", table_name="users")
op.drop_index("ix_organizations_id", table_name="organizations")
op.drop_index("ix_workflows_id", table_name="workflows")
op.drop_index("ix_workflow_definitions_id", table_name="workflow_definitions")
op.drop_index("ix_workflow_templates_id", table_name="workflow_templates")
op.drop_index("ix_workflow_runs_id", table_name="workflow_runs")
op.drop_index("ix_campaigns_id", table_name="campaigns")
op.drop_index("ix_integrations_id", table_name="integrations")
op.drop_index("ix_queued_runs_id", table_name="queued_runs")
op.drop_index("ix_user_configurations_id", table_name="user_configurations")
# ### end Alembic commands ###
def downgrade() -> 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("ix_api_keys_key_hash", "api_keys", ["key_hash"], unique=False)
# Recreate all the redundant id indexes (reverting to the incorrect state)
op.create_index(
"ix_user_configurations_id", "user_configurations", ["id"], unique=False
)
op.create_index("ix_queued_runs_id", "queued_runs", ["id"], unique=False)
op.create_index("ix_integrations_id", "integrations", ["id"], unique=False)
op.create_index("ix_campaigns_id", "campaigns", ["id"], unique=False)
op.create_index("ix_workflow_runs_id", "workflow_runs", ["id"], unique=False)
op.create_index(
"ix_workflow_templates_id", "workflow_templates", ["id"], unique=False
)
op.create_index(
"ix_workflow_definitions_id", "workflow_definitions", ["id"], unique=False
)
op.create_index("ix_workflows_id", "workflows", ["id"], unique=False)
op.create_index("ix_organizations_id", "organizations", ["id"], unique=False)
op.create_index("ix_users_id", "users", ["id"], unique=False)
op.create_index("ix_api_keys_id", "api_keys", ["id"], unique=False)
# ### end Alembic commands ###

View file

@ -0,0 +1,32 @@
"""add connection details
Revision ID: d11fbd083a55
Revises: 20c780c2a218
Create Date: 2025-06-09 19:49:24.999108
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "d11fbd083a55"
down_revision: Union[str, None] = "20c780c2a218"
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.add_column(
"integrations", sa.Column("connection_details", sa.JSON(), nullable=False)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("integrations", "connection_details")
# ### end Alembic commands ###

View file

@ -0,0 +1,51 @@
"""add integrations
Revision ID: d666f3244648
Revises: 45fa7fec2993
Create Date: 2025-06-09 16:50:56.647020
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "d666f3244648"
down_revision: Union[str, None] = "45fa7fec2993"
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.create_table(
"integrations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("integration_id", sa.String(), nullable=False),
sa.Column("organisation_id", sa.Integer(), nullable=False),
sa.Column("is_active", sa.Boolean(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["organisation_id"],
["organizations.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_integrations_id"), "integrations", ["id"], unique=False)
op.create_index(
op.f("ix_integrations_integration_id"),
"integrations",
["integration_id"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_integrations_integration_id"), table_name="integrations")
op.drop_index(op.f("ix_integrations_id"), table_name="integrations")
op.drop_table("integrations")
# ### end Alembic commands ###

View file

@ -0,0 +1,148 @@
"""add_looptalk_testing_tables_without_turns
Revision ID: e0d1a9b9f6c4
Revises: 2ed4baa89f15
Create Date: 2025-07-01 14:29:12.081935
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "e0d1a9b9f6c4"
down_revision: Union[str, None] = "2ed4baa89f15"
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.create_table(
"looptalk_test_sessions",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.Column("name", sa.String(), nullable=False),
sa.Column(
"status",
sa.Enum(
"pending", "running", "completed", "failed", name="test_session_status"
),
nullable=False,
),
sa.Column("actor_workflow_id", sa.Integer(), nullable=False),
sa.Column("adversary_workflow_id", sa.Integer(), nullable=False),
sa.Column("load_test_group_id", sa.String(), nullable=True),
sa.Column("test_index", sa.Integer(), nullable=True),
sa.Column("config", sa.JSON(), nullable=False),
sa.Column("results", sa.JSON(), nullable=True),
sa.Column("error", sa.String(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["actor_workflow_id"],
["workflows.id"],
),
sa.ForeignKeyConstraint(
["adversary_workflow_id"],
["workflows.id"],
),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
"ix_looptalk_test_sessions_group_id",
"looptalk_test_sessions",
["load_test_group_id"],
unique=False,
)
op.create_index(
op.f("ix_looptalk_test_sessions_id"),
"looptalk_test_sessions",
["id"],
unique=False,
)
op.create_index(
op.f("ix_looptalk_test_sessions_load_test_group_id"),
"looptalk_test_sessions",
["load_test_group_id"],
unique=False,
)
op.create_index(
"ix_looptalk_test_sessions_org_id",
"looptalk_test_sessions",
["organization_id"],
unique=False,
)
op.create_index(
"ix_looptalk_test_sessions_status",
"looptalk_test_sessions",
["status"],
unique=False,
)
op.create_table(
"looptalk_conversations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("test_session_id", sa.Integer(), nullable=False),
sa.Column("duration_seconds", sa.Integer(), nullable=True),
sa.Column("actor_recording_url", sa.String(), nullable=True),
sa.Column("adversary_recording_url", sa.String(), nullable=True),
sa.Column("combined_recording_url", sa.String(), nullable=True),
sa.Column("transcript", sa.JSON(), nullable=True),
sa.Column("metrics", sa.JSON(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("ended_at", sa.DateTime(timezone=True), nullable=True),
sa.ForeignKeyConstraint(
["test_session_id"],
["looptalk_test_sessions.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(
op.f("ix_looptalk_conversations_id"),
"looptalk_conversations",
["id"],
unique=False,
)
op.create_index(
"ix_looptalk_conversations_session_id",
"looptalk_conversations",
["test_session_id"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
"ix_looptalk_conversations_session_id", table_name="looptalk_conversations"
)
op.drop_index(
op.f("ix_looptalk_conversations_id"), table_name="looptalk_conversations"
)
op.drop_table("looptalk_conversations")
op.drop_index(
"ix_looptalk_test_sessions_status", table_name="looptalk_test_sessions"
)
op.drop_index(
"ix_looptalk_test_sessions_org_id", table_name="looptalk_test_sessions"
)
op.drop_index(
op.f("ix_looptalk_test_sessions_load_test_group_id"),
table_name="looptalk_test_sessions",
)
op.drop_index(
op.f("ix_looptalk_test_sessions_id"), table_name="looptalk_test_sessions"
)
op.drop_index(
"ix_looptalk_test_sessions_group_id", table_name="looptalk_test_sessions"
)
op.drop_table("looptalk_test_sessions")
# ### end Alembic commands ###

View file

@ -0,0 +1,42 @@
"""change datatype of usage to float
Revision ID: ec010596a0b4
Revises: 594f16adf97c
Create Date: 2025-07-08 15:25:49.387567
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "ec010596a0b4"
down_revision: Union[str, None] = "594f16adf97c"
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.alter_column(
"organization_usage_cycles",
"used_dograh_tokens",
existing_type=sa.INTEGER(),
type_=sa.Float(),
existing_nullable=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"organization_usage_cycles",
"used_dograh_tokens",
existing_type=sa.Float(),
type_=sa.INTEGER(),
existing_nullable=False,
)
# ### end Alembic commands ###

View file

@ -0,0 +1,104 @@
"""add organisation table
Revision ID: f6f19156bcb7
Revises: 5253971e3f03
Create Date: 2025-06-09 15:12:14.636802
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = "f6f19156bcb7"
down_revision: Union[str, None] = "5253971e3f03"
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.create_table(
"organizations",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("provider_id", sa.String(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("id"),
)
op.create_index(op.f("ix_organizations_id"), "organizations", ["id"], unique=False)
op.create_index(
op.f("ix_organizations_provider_id"),
"organizations",
["provider_id"],
unique=True,
)
op.create_table(
"organization_users",
sa.Column("user_id", sa.Integer(), nullable=False),
sa.Column("organization_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(
["organization_id"],
["organizations.id"],
),
sa.ForeignKeyConstraint(
["user_id"],
["users.id"],
),
sa.PrimaryKeyConstraint("user_id", "organization_id"),
)
op.drop_index("ix_livekit_rooms_id", table_name="livekit_rooms")
op.drop_index("ix_livekit_rooms_name", table_name="livekit_rooms")
op.drop_index("ix_livekit_rooms_state", table_name="livekit_rooms")
op.drop_index("ix_livekit_rooms_type", table_name="livekit_rooms")
op.drop_table("livekit_rooms")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"livekit_rooms",
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column(
"type",
postgresql.ENUM("VOICE", "TEXT", "VIDEO", name="room_type"),
autoincrement=False,
nullable=False,
),
sa.Column(
"state",
postgresql.ENUM(
"CONNECTED",
"DISCONNECTED",
"RECONNECTING",
"RECONNECTED",
name="room_state",
),
autoincrement=False,
nullable=False,
),
sa.Column("name", sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column(
"created_at",
postgresql.TIMESTAMP(timezone=True),
autoincrement=False,
nullable=True,
),
sa.Column("user_id", sa.INTEGER(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(
["user_id"], ["users.id"], name="livekit_rooms_user_id_fkey"
),
sa.PrimaryKeyConstraint("id", name="livekit_rooms_pkey"),
)
op.create_index("ix_livekit_rooms_type", "livekit_rooms", ["type"], unique=False)
op.create_index("ix_livekit_rooms_state", "livekit_rooms", ["state"], unique=False)
op.create_index("ix_livekit_rooms_name", "livekit_rooms", ["name"], unique=False)
op.create_index("ix_livekit_rooms_id", "livekit_rooms", ["id"], unique=False)
op.drop_table("organization_users")
op.drop_index(op.f("ix_organizations_provider_id"), table_name="organizations")
op.drop_index(op.f("ix_organizations_id"), table_name="organizations")
op.drop_table("organizations")
# ### end Alembic commands ###

View file

@ -0,0 +1,30 @@
"""add failed state to queued runs
Revision ID: f952c9c1105a
Revises: 693a865c011f
Create Date: 2025-08-16 17:32:47.877340
"""
from typing import Sequence, Union
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "f952c9c1105a"
down_revision: Union[str, None] = "693a865c011f"
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("ix_workflows_status", table_name="workflows")
op.execute("ALTER TYPE queued_run_state ADD VALUE IF NOT EXISTS 'failed'")
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_index("ix_workflows_status", "workflows", ["status"], unique=False)
# ### end Alembic commands ###

View file

@ -0,0 +1,84 @@
"""add index
Revision ID: fec0fb9a8db7
Revises: fefdd1835b7d
Create Date: 2025-08-06 16:21:56.450309
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "fec0fb9a8db7"
down_revision: Union[str, None] = "fefdd1835b7d"
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.add_column(
"campaigns",
sa.Column("last_batch_scheduled_at", sa.DateTime(timezone=True), nullable=True),
)
op.add_column(
"campaigns",
sa.Column("last_activity_at", sa.DateTime(timezone=True), nullable=True),
)
op.add_column(
"campaigns",
sa.Column(
"orchestrator_metadata",
sa.JSON(),
nullable=False,
server_default=sa.text("'{}'"),
),
)
op.create_index(
"idx_campaigns_active_status",
"campaigns",
["state"],
unique=False,
postgresql_where=sa.text("state IN ('syncing', 'running', 'paused')"),
)
op.create_index(
"idx_queued_runs_campaign_state_optimized",
"queued_runs",
["campaign_id", "state"],
unique=False,
postgresql_where=sa.text("state = 'queued'"),
)
op.create_index(
"idx_queued_runs_scheduled_optimized",
"queued_runs",
["campaign_id", "scheduled_for"],
unique=False,
postgresql_where=sa.text("scheduled_for IS NOT NULL"),
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
"idx_queued_runs_scheduled_optimized",
table_name="queued_runs",
postgresql_where=sa.text("scheduled_for IS NOT NULL"),
)
op.drop_index(
"idx_queued_runs_campaign_state_optimized",
table_name="queued_runs",
postgresql_where=sa.text("state = 'queued'"),
)
op.drop_index(
"idx_campaigns_active_status",
table_name="campaigns",
postgresql_where=sa.text("state IN ('syncing', 'running', 'paused')"),
)
op.drop_column("campaigns", "orchestrator_metadata")
op.drop_column("campaigns", "last_activity_at")
op.drop_column("campaigns", "last_batch_scheduled_at")
# ### end Alembic commands ###

View file

@ -0,0 +1,84 @@
"""retry outbound calls for campaigns
Revision ID: fefdd1835b7d
Revises: a75ae71af479
Create Date: 2025-08-06 13:37:54.151453
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "fefdd1835b7d"
down_revision: Union[str, None] = "a75ae71af479"
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.add_column(
"campaigns",
sa.Column(
"retry_config",
sa.JSON(),
nullable=False,
server_default=sa.text(
"""'{\"enabled\": true, \"max_retries\": 2, \"retry_delay_seconds\": 120, \"retry_on_busy\": true, \"retry_on_no_answer\": true, \"retry_on_voicemail\": true}'::jsonb"""
),
),
)
op.add_column(
"queued_runs",
sa.Column(
"retry_count", sa.Integer(), nullable=False, server_default=sa.text("0")
),
)
op.add_column(
"queued_runs", sa.Column("parent_queued_run_id", sa.Integer(), nullable=True)
)
op.add_column(
"queued_runs",
sa.Column("scheduled_for", sa.DateTime(timezone=True), nullable=True),
)
op.add_column("queued_runs", sa.Column("retry_reason", sa.String(), nullable=True))
op.drop_constraint("unique_campaign_source_uuid", "queued_runs", type_="unique")
op.create_index(
"idx_queued_runs_scheduled", "queued_runs", ["scheduled_for"], unique=False
)
op.create_unique_constraint(
"unique_campaign_source_retry",
"queued_runs",
["campaign_id", "source_uuid", "retry_count"],
)
op.create_foreign_key(
None, "queued_runs", "queued_runs", ["parent_queued_run_id"], ["id"]
)
op.add_column(
"workflow_runs", sa.Column("queued_run_id", sa.Integer(), nullable=True)
)
op.create_foreign_key(
None, "workflow_runs", "queued_runs", ["queued_run_id"], ["id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, "workflow_runs", type_="foreignkey")
op.drop_column("workflow_runs", "queued_run_id")
op.drop_constraint(None, "queued_runs", type_="foreignkey")
op.drop_constraint("unique_campaign_source_retry", "queued_runs", type_="unique")
op.drop_index("idx_queued_runs_scheduled", table_name="queued_runs")
op.create_unique_constraint(
"unique_campaign_source_uuid", "queued_runs", ["campaign_id", "source_uuid"]
)
op.drop_column("queued_runs", "retry_reason")
op.drop_column("queued_runs", "scheduled_for")
op.drop_column("queued_runs", "parent_queued_run_id")
op.drop_column("queued_runs", "retry_count")
op.drop_column("campaigns", "retry_config")
# ### end Alembic commands ###