Files
wehub-resource-sync fed8b2eed7
Build and push multi-arch DocsGPT Docker image / build (linux/amd64, ubuntu-latest, amd64) (push) Has been cancelled
Backend release / release (push) Has been cancelled
Bandit Security Scan / bandit_scan (push) Has been cancelled
Build and push multi-arch DocsGPT Docker image / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Has been cancelled
Build and push multi-arch DocsGPT Docker image / manifest (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / build (linux/amd64, ubuntu-latest, amd64) (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / manifest (push) Has been cancelled
Python linting / ruff (push) Has been cancelled
Run python tests with pytest / Run tests and count coverage (3.12) (push) Has been cancelled
React Widget Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:29 +08:00

148 lines
5.8 KiB
Python

"""0010 scheduler — schedules + schedule_runs tables.
Revision ID: 0010_schedules
Revises: 0009_tool_preferences
"""
from typing import Sequence, Union
from alembic import op
revision: str = "0010_schedules"
down_revision: Union[str, None] = "0009_tool_preferences"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.execute(
"""
CREATE TABLE schedules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id TEXT NOT NULL,
agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
trigger_type TEXT NOT NULL,
name TEXT,
instruction TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
cron TEXT,
run_at TIMESTAMPTZ,
timezone TEXT NOT NULL DEFAULT 'UTC',
next_run_at TIMESTAMPTZ,
last_run_at TIMESTAMPTZ,
end_at TIMESTAMPTZ,
tool_allowlist JSONB NOT NULL DEFAULT '[]'::jsonb,
model_id TEXT,
token_budget INTEGER,
origin_conversation_id UUID REFERENCES conversations(id) ON DELETE SET NULL,
created_via TEXT NOT NULL DEFAULT 'ui',
consecutive_failure_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT schedules_trigger_type_chk
CHECK (trigger_type IN ('once', 'recurring')),
CONSTRAINT schedules_status_chk
CHECK (status IN ('active', 'paused', 'completed', 'cancelled')),
CONSTRAINT schedules_created_via_chk
CHECK (created_via IN ('chat', 'ui')),
CONSTRAINT schedules_recurring_cron_chk
CHECK (trigger_type <> 'recurring' OR cron IS NOT NULL),
CONSTRAINT schedules_once_run_at_chk
CHECK (trigger_type <> 'once' OR run_at IS NOT NULL)
);
"""
)
op.execute(
"CREATE INDEX schedules_user_idx ON schedules (user_id);"
)
op.execute(
"CREATE INDEX schedules_agent_idx ON schedules (agent_id);"
)
# Dispatcher hot path: status='active' AND next_run_at <= now().
op.execute(
"CREATE INDEX schedules_due_idx "
"ON schedules (status, next_run_at) "
"WHERE status = 'active';"
)
op.execute(
"CREATE TRIGGER schedules_set_updated_at "
"BEFORE UPDATE ON schedules "
"FOR EACH ROW EXECUTE FUNCTION set_updated_at();"
)
op.execute(
"""
CREATE TABLE schedule_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
schedule_id UUID NOT NULL REFERENCES schedules(id) ON DELETE CASCADE,
user_id TEXT NOT NULL,
agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'pending',
scheduled_for TIMESTAMPTZ NOT NULL,
trigger_source TEXT NOT NULL DEFAULT 'cron',
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
output TEXT,
output_truncated BOOLEAN NOT NULL DEFAULT false,
error TEXT,
error_type TEXT,
prompt_tokens INTEGER NOT NULL DEFAULT 0,
generated_tokens INTEGER NOT NULL DEFAULT 0,
conversation_id UUID,
message_id UUID,
celery_task_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT schedule_runs_status_chk
CHECK (status IN (
'pending', 'running', 'success', 'failed', 'skipped', 'timeout'
)),
CONSTRAINT schedule_runs_trigger_source_chk
CHECK (trigger_source IN ('cron', 'manual')),
CONSTRAINT schedule_runs_error_type_chk
CHECK (error_type IS NULL OR error_type IN (
'auth_expired', 'tool_not_allowed', 'budget_exceeded',
'timeout', 'agent_error', 'internal', 'missed', 'overlap'
))
);
"""
)
# Dedup primitive: racing dispatchers hit ON CONFLICT on this index.
op.execute(
"CREATE UNIQUE INDEX schedule_runs_dedup_uidx "
"ON schedule_runs (schedule_id, scheduled_for);"
)
op.execute(
"CREATE INDEX schedule_runs_schedule_recent_idx "
"ON schedule_runs (schedule_id, scheduled_for DESC);"
)
op.execute(
"CREATE INDEX schedule_runs_user_idx ON schedule_runs (user_id);"
)
op.execute(
"CREATE INDEX schedule_runs_running_idx "
"ON schedule_runs (status, started_at) "
"WHERE status = 'running';"
)
op.execute(
"CREATE TRIGGER schedule_runs_set_updated_at "
"BEFORE UPDATE ON schedule_runs "
"FOR EACH ROW EXECUTE FUNCTION set_updated_at();"
)
def downgrade() -> None:
# Drop triggers explicitly (grep-able) before CASCADE-dropping the tables.
op.execute(
"DROP TRIGGER IF EXISTS schedule_runs_set_updated_at ON schedule_runs;"
)
op.execute("DROP TABLE IF EXISTS schedule_runs CASCADE;")
op.execute(
"DROP TRIGGER IF EXISTS schedules_set_updated_at ON schedules;"
)
op.execute("DROP TABLE IF EXISTS schedules CASCADE;")