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) );