easyai-ai-gateway/apps/api/migrations/0001_init.sql
wangbo 6323e70e49 Initial project scaffold
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 14:36:35 +08:00

334 lines
12 KiB
SQL

CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE IF NOT EXISTS model_catalog_providers (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
provider_key text NOT NULL UNIQUE,
display_name text NOT NULL,
provider_type text NOT NULL DEFAULT 'openai_compatible',
capability_schema jsonb NOT NULL DEFAULT '{}'::jsonb,
default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
status text NOT NULL DEFAULT 'active',
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_model_catalog_provider_status
ON model_catalog_providers(status);
CREATE TABLE IF NOT EXISTS base_model_catalog (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
provider_id uuid REFERENCES model_catalog_providers(id) ON DELETE SET NULL,
provider_key text NOT NULL,
canonical_model_key text NOT NULL UNIQUE,
provider_model_name text NOT NULL,
model_type text NOT NULL,
display_name text NOT NULL,
capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
base_billing_config jsonb NOT NULL DEFAULT '{}'::jsonb,
default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
pricing_version integer NOT NULL DEFAULT 1,
status text NOT NULL DEFAULT 'active',
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_base_model_catalog_provider
ON base_model_catalog(provider_key, model_type, status);
CREATE INDEX IF NOT EXISTS idx_base_model_catalog_capabilities
ON base_model_catalog USING gin(capabilities);
CREATE TABLE IF NOT EXISTS integration_platforms (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
provider text NOT NULL,
platform_key text NOT NULL UNIQUE DEFAULT ('platform_' || replace(gen_random_uuid()::text, '-', '')),
name text NOT NULL,
base_url text,
auth_type text NOT NULL DEFAULT 'bearer',
credentials jsonb NOT NULL DEFAULT '{}'::jsonb,
config jsonb NOT NULL DEFAULT '{}'::jsonb,
default_pricing_mode text NOT NULL DEFAULT 'inherit_discount',
default_discount_factor numeric NOT NULL DEFAULT 1,
retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
priority integer NOT NULL DEFAULT 100,
dynamic_priority integer,
status text NOT NULL DEFAULT 'enabled',
disabled_reason text,
cooldown_until timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_provider_status
ON integration_platforms(provider, status);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_status_priority
ON integration_platforms(status, priority, dynamic_priority);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_cooldown
ON integration_platforms(cooldown_until);
CREATE TABLE IF NOT EXISTS model_pricing_rules (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scope_type text NOT NULL,
scope_id uuid,
resource_type text NOT NULL,
unit text NOT NULL,
base_price numeric NOT NULL,
currency text NOT NULL DEFAULT 'resource',
base_weight jsonb NOT NULL DEFAULT '{}'::jsonb,
dynamic_weight jsonb NOT NULL DEFAULT '{}'::jsonb,
effective_from timestamptz,
effective_to timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_model_pricing_scope
ON model_pricing_rules(scope_type, scope_id, resource_type);
CREATE INDEX IF NOT EXISTS idx_model_pricing_effective
ON model_pricing_rules(effective_from, effective_to);
CREATE TABLE IF NOT EXISTS platform_models (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
platform_id uuid NOT NULL REFERENCES integration_platforms(id) ON DELETE CASCADE,
base_model_id uuid REFERENCES base_model_catalog(id) ON DELETE SET NULL,
model_name text NOT NULL,
model_alias text,
model_type text NOT NULL,
display_name text NOT NULL DEFAULT '',
capability_override jsonb NOT NULL DEFAULT '{}'::jsonb,
capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
pricing_mode text NOT NULL DEFAULT 'inherit_discount',
discount_factor numeric,
billing_config_override jsonb NOT NULL DEFAULT '{}'::jsonb,
billing_config jsonb NOT NULL DEFAULT '{}'::jsonb,
permission_config jsonb NOT NULL DEFAULT '{}'::jsonb,
retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
enabled boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(platform_id, model_name, model_type)
);
CREATE INDEX IF NOT EXISTS idx_platform_models_base
ON platform_models(base_model_id);
CREATE INDEX IF NOT EXISTS idx_platform_models_lookup
ON platform_models(model_type, model_name, enabled);
CREATE INDEX IF NOT EXISTS idx_platform_models_alias
ON platform_models(model_alias);
CREATE INDEX IF NOT EXISTS idx_platform_models_capabilities
ON platform_models USING gin(capabilities);
CREATE TABLE IF NOT EXISTS gateway_tasks (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
external_task_id text,
kind text NOT NULL,
run_mode text NOT NULL DEFAULT 'production',
user_id text NOT NULL,
tenant_id text,
api_key_id text,
model text NOT NULL,
model_type text,
request jsonb NOT NULL DEFAULT '{}'::jsonb,
normalized_request jsonb NOT NULL DEFAULT '{}'::jsonb,
status text NOT NULL DEFAULT 'queued',
queue_key text NOT NULL DEFAULT 'default',
priority integer NOT NULL DEFAULT 100,
idempotency_key text,
remote_task_id text,
remote_task_payload jsonb,
simulation_profile jsonb,
simulation_seed text,
locked_by text,
locked_at timestamptz,
heartbeat_at timestamptz,
next_run_at timestamptz NOT NULL DEFAULT now(),
attempt_count integer NOT NULL DEFAULT 0,
max_attempts integer NOT NULL DEFAULT 1,
result jsonb,
billings jsonb,
error text,
error_code text,
error_message text,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
finished_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_queue
ON gateway_tasks(status, next_run_at, priority, created_at);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_lease
ON gateway_tasks(status, heartbeat_at);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_user_created
ON gateway_tasks(user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_external
ON gateway_tasks(external_task_id);
CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_tasks_idempotency
ON gateway_tasks(user_id, idempotency_key)
WHERE idempotency_key IS NOT NULL;
CREATE TABLE IF NOT EXISTS gateway_task_attempts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
attempt_no integer NOT NULL,
platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL,
platform_model_id uuid REFERENCES platform_models(id) ON DELETE SET NULL,
client_id text,
queue_key text NOT NULL,
status text NOT NULL,
retryable boolean NOT NULL DEFAULT false,
simulated boolean NOT NULL DEFAULT false,
remote_task_id text,
request_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
response_snapshot jsonb,
error_code text,
error_message text,
started_at timestamptz NOT NULL DEFAULT now(),
finished_at timestamptz,
UNIQUE(task_id, attempt_no)
);
CREATE INDEX IF NOT EXISTS idx_gateway_attempts_task
ON gateway_task_attempts(task_id);
CREATE INDEX IF NOT EXISTS idx_gateway_attempts_client
ON gateway_task_attempts(client_id, started_at DESC);
CREATE TABLE IF NOT EXISTS gateway_task_events (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
seq bigint NOT NULL,
event_type text NOT NULL,
status text,
phase text,
progress numeric,
message text,
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
simulated boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(task_id, seq)
);
CREATE INDEX IF NOT EXISTS idx_gateway_events_task_created
ON gateway_task_events(task_id, created_at);
CREATE TABLE IF NOT EXISTS runtime_client_states (
client_id text PRIMARY KEY,
platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL,
provider text NOT NULL,
method_name text NOT NULL,
queue_key text NOT NULL,
running_count integer NOT NULL DEFAULT 0,
waiting_count integer NOT NULL DEFAULT 0,
limiter_ratio numeric NOT NULL DEFAULT 0,
cooldown_until timestamptz,
last_assigned_at timestamptz,
last_error text,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_runtime_client_queue
ON runtime_client_states(queue_key, cooldown_until);
CREATE INDEX IF NOT EXISTS idx_runtime_client_platform
ON runtime_client_states(platform_id);
CREATE TABLE IF NOT EXISTS gateway_upload_assets (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid REFERENCES gateway_tasks(id) ON DELETE SET NULL,
source text NOT NULL,
server_main_file_id text,
url text NOT NULL,
object_key text,
content_type text,
size bigint,
checksum text,
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_gateway_upload_task
ON gateway_upload_assets(task_id);
CREATE INDEX IF NOT EXISTS idx_gateway_upload_file
ON gateway_upload_assets(server_main_file_id);
CREATE TABLE IF NOT EXISTS gateway_retry_policies (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scope_type text NOT NULL,
scope_key text NOT NULL,
enabled boolean NOT NULL DEFAULT true,
policy jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(scope_type, scope_key)
);
CREATE TABLE IF NOT EXISTS gateway_rate_limit_policies (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
scope_type text NOT NULL,
scope_key text NOT NULL,
policy jsonb NOT NULL DEFAULT '{}'::jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(scope_type, scope_key)
);
CREATE TABLE IF NOT EXISTS gateway_rate_limit_counters (
scope_type text NOT NULL,
scope_key text NOT NULL,
metric text NOT NULL,
window_start timestamptz NOT NULL,
limit_value numeric NOT NULL,
used_value numeric NOT NULL DEFAULT 0,
reserved_value numeric NOT NULL DEFAULT 0,
reset_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY(scope_type, scope_key, metric, window_start)
);
CREATE TABLE IF NOT EXISTS gateway_concurrency_leases (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
attempt_id uuid REFERENCES gateway_task_attempts(id) ON DELETE SET NULL,
scope_type text NOT NULL,
scope_key text NOT NULL,
lease_value numeric NOT NULL DEFAULT 1,
acquired_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz NOT NULL,
released_at timestamptz
);
CREATE INDEX IF NOT EXISTS idx_concurrency_leases_active
ON gateway_concurrency_leases(scope_type, scope_key, released_at, expires_at);
CREATE INDEX IF NOT EXISTS idx_concurrency_leases_task
ON gateway_concurrency_leases(task_id);
CREATE TABLE IF NOT EXISTS settlement_outbox (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
event_type text NOT NULL DEFAULT 'task.settlement.requested',
payload jsonb NOT NULL,
status text NOT NULL DEFAULT 'pending',
attempts integer NOT NULL DEFAULT 0,
next_attempt_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE(task_id, event_type)
);