mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 00:02:28 +08:00
772 lines
37 KiB
Python
772 lines
37 KiB
Python
"""Baseline migration - all tables consolidated
|
|
|
|
Revision ID: 20251210_baseline
|
|
Revises:
|
|
Create Date: 2024-12-10
|
|
|
|
This is the consolidated baseline migration that creates all tables from scratch.
|
|
Includes all schema changes up to circuit breaker v2.
|
|
"""
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
# revision identifiers
|
|
revision = "20251210_baseline"
|
|
down_revision = None
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
# Create ENUM types
|
|
op.execute("CREATE TYPE userrole AS ENUM ('admin', 'user')")
|
|
op.execute(
|
|
"CREATE TYPE providerbillingtype AS ENUM ('monthly_quota', 'pay_as_you_go', 'free_tier')"
|
|
)
|
|
|
|
# ==================== users ====================
|
|
op.create_table(
|
|
"users",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("email", sa.String(255), unique=True, index=True, nullable=False),
|
|
sa.Column("username", sa.String(100), unique=True, index=True, nullable=False),
|
|
sa.Column("password_hash", sa.String(255), nullable=False),
|
|
sa.Column(
|
|
"role",
|
|
sa.Enum("admin", "user", name="userrole", create_type=False),
|
|
nullable=False,
|
|
server_default="user",
|
|
),
|
|
sa.Column("allowed_providers", sa.JSON, nullable=True),
|
|
sa.Column("allowed_endpoints", sa.JSON, nullable=True),
|
|
sa.Column("allowed_models", sa.JSON, nullable=True),
|
|
sa.Column("model_capability_settings", sa.JSON, nullable=True),
|
|
sa.Column("quota_usd", sa.Float, nullable=True),
|
|
sa.Column("used_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("total_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("is_deleted", sa.Boolean, server_default="false", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column("last_login_at", sa.DateTime(timezone=True), nullable=True),
|
|
)
|
|
|
|
# ==================== providers ====================
|
|
op.create_table(
|
|
"providers",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("name", sa.String(100), unique=True, index=True, nullable=False),
|
|
sa.Column("display_name", sa.String(100), nullable=False),
|
|
sa.Column("description", sa.Text, nullable=True),
|
|
sa.Column("website", sa.String(500), nullable=True),
|
|
sa.Column(
|
|
"billing_type",
|
|
sa.Enum(
|
|
"monthly_quota", "pay_as_you_go", "free_tier", name="providerbillingtype", create_type=False
|
|
),
|
|
nullable=False,
|
|
server_default="pay_as_you_go",
|
|
),
|
|
sa.Column("monthly_quota_usd", sa.Float, nullable=True),
|
|
sa.Column("monthly_used_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("quota_reset_day", sa.Integer, server_default="30"),
|
|
sa.Column("quota_last_reset_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("quota_expires_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("rpm_limit", sa.Integer, nullable=True),
|
|
sa.Column("rpm_used", sa.Integer, server_default="0"),
|
|
sa.Column("rpm_reset_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("provider_priority", sa.Integer, server_default="100"),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("rate_limit", sa.Integer, nullable=True),
|
|
sa.Column("concurrent_limit", sa.Integer, nullable=True),
|
|
sa.Column("config", sa.JSON, nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== global_models ====================
|
|
op.create_table(
|
|
"global_models",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("name", sa.String(100), unique=True, index=True, nullable=False),
|
|
sa.Column("display_name", sa.String(100), nullable=False),
|
|
sa.Column("description", sa.Text, nullable=True),
|
|
sa.Column("icon_url", sa.String(500), nullable=True),
|
|
sa.Column("official_url", sa.String(500), nullable=True),
|
|
sa.Column("default_price_per_request", sa.Float, nullable=True),
|
|
sa.Column("default_tiered_pricing", sa.JSON, nullable=False),
|
|
sa.Column("default_supports_vision", sa.Boolean, server_default="false", nullable=True),
|
|
sa.Column("default_supports_function_calling", sa.Boolean, server_default="false", nullable=True),
|
|
sa.Column("default_supports_streaming", sa.Boolean, server_default="true", nullable=True),
|
|
sa.Column("default_supports_extended_thinking", sa.Boolean, server_default="false", nullable=True),
|
|
sa.Column("default_supports_image_generation", sa.Boolean, server_default="false", nullable=True),
|
|
sa.Column("supported_capabilities", sa.JSON, nullable=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("usage_count", sa.Integer, server_default="0", nullable=False, index=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== api_keys ====================
|
|
op.create_table(
|
|
"api_keys",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"user_id", sa.String(36), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
|
),
|
|
sa.Column("key_hash", sa.String(64), unique=True, index=True, nullable=False),
|
|
sa.Column("key_encrypted", sa.Text, nullable=True),
|
|
sa.Column("name", sa.String(100), nullable=True),
|
|
sa.Column("total_requests", sa.Integer, server_default="0"),
|
|
sa.Column("total_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("balance_used_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("current_balance_usd", sa.Float, nullable=True),
|
|
sa.Column("is_standalone", sa.Boolean, server_default="false", nullable=False),
|
|
sa.Column("allowed_providers", sa.JSON, nullable=True),
|
|
sa.Column("allowed_endpoints", sa.JSON, nullable=True),
|
|
sa.Column("allowed_api_formats", sa.JSON, nullable=True),
|
|
sa.Column("allowed_models", sa.JSON, nullable=True),
|
|
sa.Column("rate_limit", sa.Integer, server_default="100"),
|
|
sa.Column("concurrent_limit", sa.Integer, server_default="5", nullable=True),
|
|
sa.Column("force_capabilities", sa.JSON, nullable=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("auto_delete_on_expiry", sa.Boolean, server_default="false", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== provider_endpoints ====================
|
|
op.create_table(
|
|
"provider_endpoints",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"provider_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("providers.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column("api_format", sa.String(50), nullable=False),
|
|
sa.Column("base_url", sa.String(500), nullable=False),
|
|
sa.Column("headers", sa.JSON, nullable=True),
|
|
sa.Column("timeout", sa.Integer, server_default="300"),
|
|
sa.Column("max_retries", sa.Integer, server_default="3"),
|
|
sa.Column("max_concurrent", sa.Integer, nullable=True),
|
|
sa.Column("rate_limit", sa.Integer, nullable=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("custom_path", sa.String(200), nullable=True),
|
|
sa.Column("config", sa.JSON, nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("provider_id", "api_format", name="uq_provider_api_format"),
|
|
)
|
|
op.create_index(
|
|
"idx_endpoint_format_active", "provider_endpoints", ["api_format", "is_active"]
|
|
)
|
|
|
|
# ==================== models ====================
|
|
op.create_table(
|
|
"models",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"provider_id", sa.String(36), sa.ForeignKey("providers.id"), nullable=False
|
|
),
|
|
sa.Column(
|
|
"global_model_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("global_models.id"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column("provider_model_name", sa.String(200), nullable=False),
|
|
sa.Column("price_per_request", sa.Float, nullable=True),
|
|
sa.Column("tiered_pricing", sa.JSON, nullable=True),
|
|
sa.Column("supports_vision", sa.Boolean, nullable=True),
|
|
sa.Column("supports_function_calling", sa.Boolean, nullable=True),
|
|
sa.Column("supports_streaming", sa.Boolean, nullable=True),
|
|
sa.Column("supports_extended_thinking", sa.Boolean, nullable=True),
|
|
sa.Column("supports_image_generation", sa.Boolean, nullable=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("is_available", sa.Boolean, server_default="true"),
|
|
sa.Column("config", sa.JSON, nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("provider_id", "provider_model_name", name="uq_provider_model"),
|
|
)
|
|
|
|
# ==================== model_mappings ====================
|
|
op.create_table(
|
|
"model_mappings",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("source_model", sa.String(200), nullable=False, index=True),
|
|
sa.Column(
|
|
"target_global_model_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("global_models.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column(
|
|
"provider_id", sa.String(36), sa.ForeignKey("providers.id"), nullable=True, index=True
|
|
),
|
|
sa.Column("mapping_type", sa.String(20), nullable=False, server_default="alias", index=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("source_model", "provider_id", name="uq_model_mapping_source_provider"),
|
|
)
|
|
|
|
# ==================== provider_api_keys ====================
|
|
op.create_table(
|
|
"provider_api_keys",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"endpoint_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("provider_endpoints.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column("api_key", sa.String(500), nullable=False),
|
|
sa.Column("name", sa.String(100), nullable=False),
|
|
sa.Column("note", sa.String(500), nullable=True),
|
|
sa.Column("rate_multiplier", sa.Float, server_default="1.0", nullable=False),
|
|
sa.Column("internal_priority", sa.Integer, server_default="50"),
|
|
sa.Column("global_priority", sa.Integer, nullable=True),
|
|
sa.Column("max_concurrent", sa.Integer, nullable=True),
|
|
sa.Column("rate_limit", sa.Integer, nullable=True),
|
|
sa.Column("daily_limit", sa.Integer, nullable=True),
|
|
sa.Column("monthly_limit", sa.Integer, nullable=True),
|
|
sa.Column("allowed_models", sa.JSON, nullable=True),
|
|
sa.Column("capabilities", sa.JSON, nullable=True),
|
|
sa.Column("learned_max_concurrent", sa.Integer, nullable=True),
|
|
sa.Column("concurrent_429_count", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("rpm_429_count", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("last_429_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("last_429_type", sa.String(50), nullable=True),
|
|
sa.Column("last_concurrent_peak", sa.Integer, nullable=True),
|
|
sa.Column("adjustment_history", sa.JSON, nullable=True),
|
|
# Sliding window fields (replaces high_utilization_start)
|
|
sa.Column("utilization_samples", sa.JSON, nullable=True),
|
|
sa.Column("last_probe_increase_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("health_score", sa.Float, server_default="1.0"),
|
|
sa.Column("consecutive_failures", sa.Integer, server_default="0"),
|
|
sa.Column("last_failure_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("cache_ttl_minutes", sa.Integer, server_default="5", nullable=False),
|
|
sa.Column("max_probe_interval_minutes", sa.Integer, server_default="32", nullable=False),
|
|
sa.Column("circuit_breaker_open", sa.Boolean, server_default="false", nullable=False),
|
|
sa.Column("circuit_breaker_open_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("next_probe_at", sa.DateTime(timezone=True), nullable=True),
|
|
# Circuit breaker v2 fields
|
|
sa.Column("request_results_window", sa.JSON, nullable=True),
|
|
sa.Column("half_open_until", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("half_open_successes", sa.Integer, server_default="0", nullable=True),
|
|
sa.Column("half_open_failures", sa.Integer, server_default="0", nullable=True),
|
|
sa.Column("request_count", sa.Integer, server_default="0"),
|
|
sa.Column("success_count", sa.Integer, server_default="0"),
|
|
sa.Column("error_count", sa.Integer, server_default="0"),
|
|
sa.Column("total_response_time_ms", sa.Integer, server_default="0"),
|
|
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("last_error_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("last_error_msg", sa.Text, nullable=True),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== usage ====================
|
|
op.create_table(
|
|
"usage",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"user_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"api_key_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("api_keys.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("request_id", sa.String(100), unique=True, index=True, nullable=False),
|
|
sa.Column("provider", sa.String(100), nullable=False),
|
|
sa.Column("model", sa.String(100), nullable=False),
|
|
sa.Column("target_model", sa.String(100), nullable=True),
|
|
sa.Column(
|
|
"provider_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("providers.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"provider_endpoint_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("provider_endpoints.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"provider_api_key_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("provider_api_keys.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("input_tokens", sa.Integer, server_default="0"),
|
|
sa.Column("output_tokens", sa.Integer, server_default="0"),
|
|
sa.Column("total_tokens", sa.Integer, server_default="0"),
|
|
sa.Column("cache_creation_input_tokens", sa.Integer, server_default="0"),
|
|
sa.Column("cache_read_input_tokens", sa.Integer, server_default="0"),
|
|
sa.Column("input_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("output_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("cache_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("cache_creation_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("cache_read_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("request_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("total_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_input_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_output_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_cache_creation_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_cache_read_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_request_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("actual_total_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("rate_multiplier", sa.Float, server_default="1.0"),
|
|
sa.Column("input_price_per_1m", sa.Float, nullable=True),
|
|
sa.Column("output_price_per_1m", sa.Float, nullable=True),
|
|
sa.Column("cache_creation_price_per_1m", sa.Float, nullable=True),
|
|
sa.Column("cache_read_price_per_1m", sa.Float, nullable=True),
|
|
sa.Column("price_per_request", sa.Float, nullable=True),
|
|
sa.Column("request_type", sa.String(50), nullable=True),
|
|
sa.Column("api_format", sa.String(50), nullable=True),
|
|
sa.Column("is_stream", sa.Boolean, server_default="false"),
|
|
sa.Column("status_code", sa.Integer, nullable=True),
|
|
sa.Column("error_message", sa.Text, nullable=True),
|
|
sa.Column("response_time_ms", sa.Integer, nullable=True),
|
|
sa.Column("status", sa.String(20), server_default="completed", nullable=False, index=True),
|
|
sa.Column("request_headers", sa.JSON, nullable=True),
|
|
sa.Column("request_body", sa.JSON, nullable=True),
|
|
sa.Column("provider_request_headers", sa.JSON, nullable=True),
|
|
sa.Column("response_headers", sa.JSON, nullable=True),
|
|
sa.Column("response_body", sa.JSON, nullable=True),
|
|
sa.Column("request_body_compressed", sa.LargeBinary, nullable=True),
|
|
sa.Column("response_body_compressed", sa.LargeBinary, nullable=True),
|
|
sa.Column("request_metadata", sa.JSON, nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
)
|
|
|
|
# ==================== user_quotas ====================
|
|
op.create_table(
|
|
"user_quotas",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"user_id", sa.String(36), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
|
),
|
|
sa.Column("quota_type", sa.String(50), nullable=False),
|
|
sa.Column("quota_usd", sa.Float, nullable=False),
|
|
sa.Column("period_start", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("period_end", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("used_usd", sa.Float, server_default="0.0"),
|
|
sa.Column("is_active", sa.Boolean, server_default="true"),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== system_configs ====================
|
|
op.create_table(
|
|
"system_configs",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("key", sa.String(100), unique=True, nullable=False),
|
|
sa.Column("value", sa.JSON, nullable=False),
|
|
sa.Column("description", sa.Text, nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== user_preferences ====================
|
|
op.create_table(
|
|
"user_preferences",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"user_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
|
unique=True,
|
|
nullable=False,
|
|
),
|
|
sa.Column("avatar_url", sa.String(500), nullable=True),
|
|
sa.Column("bio", sa.Text, nullable=True),
|
|
sa.Column(
|
|
"default_provider_id", sa.String(36), sa.ForeignKey("providers.id"), nullable=True
|
|
),
|
|
sa.Column("theme", sa.String(20), server_default="light"),
|
|
sa.Column("language", sa.String(10), server_default="zh-CN"),
|
|
sa.Column("timezone", sa.String(50), server_default="Asia/Shanghai"),
|
|
sa.Column("email_notifications", sa.Boolean, server_default="true"),
|
|
sa.Column("usage_alerts", sa.Boolean, server_default="true"),
|
|
sa.Column("announcement_notifications", sa.Boolean, server_default="true"),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== announcements ====================
|
|
op.create_table(
|
|
"announcements",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("title", sa.String(200), nullable=False),
|
|
sa.Column("content", sa.Text, nullable=False),
|
|
sa.Column("type", sa.String(20), server_default="info"),
|
|
sa.Column("priority", sa.Integer, server_default="0"),
|
|
sa.Column(
|
|
"author_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("is_active", sa.Boolean, server_default="true", index=True),
|
|
sa.Column("is_pinned", sa.Boolean, server_default="false"),
|
|
sa.Column("start_time", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("end_time", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== announcement_reads ====================
|
|
op.create_table(
|
|
"announcement_reads",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"user_id", sa.String(36), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
|
),
|
|
sa.Column(
|
|
"announcement_id", sa.String(36), sa.ForeignKey("announcements.id"), nullable=False
|
|
),
|
|
sa.Column(
|
|
"read_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("user_id", "announcement_id", name="uq_user_announcement"),
|
|
)
|
|
|
|
# ==================== audit_logs ====================
|
|
op.create_table(
|
|
"audit_logs",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column("event_type", sa.String(50), nullable=False, index=True),
|
|
sa.Column(
|
|
"user_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
index=True,
|
|
),
|
|
sa.Column("api_key_id", sa.String(36), nullable=True),
|
|
sa.Column("description", sa.Text, nullable=False),
|
|
sa.Column("ip_address", sa.String(45), nullable=True),
|
|
sa.Column("user_agent", sa.String(500), nullable=True),
|
|
sa.Column("request_id", sa.String(100), nullable=True, index=True),
|
|
sa.Column("event_metadata", sa.JSON, nullable=True),
|
|
sa.Column("status_code", sa.Integer, nullable=True),
|
|
sa.Column("error_message", sa.Text, nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
)
|
|
|
|
# ==================== request_candidates ====================
|
|
op.create_table(
|
|
"request_candidates",
|
|
sa.Column("id", sa.String(36), primary_key=True),
|
|
sa.Column("request_id", sa.String(100), nullable=False, index=True),
|
|
sa.Column(
|
|
"user_id", sa.String(36), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=True
|
|
),
|
|
sa.Column(
|
|
"api_key_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("api_keys.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("candidate_index", sa.Integer, nullable=False),
|
|
sa.Column("retry_index", sa.Integer, nullable=False, server_default="0"),
|
|
sa.Column(
|
|
"provider_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("providers.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"endpoint_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("provider_endpoints.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"key_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("provider_api_keys.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("status", sa.String(20), nullable=False),
|
|
sa.Column("skip_reason", sa.Text, nullable=True),
|
|
sa.Column("is_cached", sa.Boolean, server_default="false"),
|
|
sa.Column("status_code", sa.Integer, nullable=True),
|
|
sa.Column("error_type", sa.String(50), nullable=True),
|
|
sa.Column("error_message", sa.Text, nullable=True),
|
|
sa.Column("latency_ms", sa.Integer, nullable=True),
|
|
sa.Column("concurrent_requests", sa.Integer, nullable=True),
|
|
sa.Column("extra_data", sa.JSON, nullable=True),
|
|
sa.Column("required_capabilities", sa.JSON, nullable=True),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("finished_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.UniqueConstraint(
|
|
"request_id", "candidate_index", "retry_index", name="uq_request_candidate_with_retry"
|
|
),
|
|
)
|
|
op.create_index("idx_request_candidates_request_id", "request_candidates", ["request_id"])
|
|
op.create_index("idx_request_candidates_status", "request_candidates", ["status"])
|
|
op.create_index("idx_request_candidates_provider_id", "request_candidates", ["provider_id"])
|
|
|
|
# ==================== stats_daily ====================
|
|
op.create_table(
|
|
"stats_daily",
|
|
sa.Column("id", sa.String(36), primary_key=True),
|
|
sa.Column("date", sa.DateTime(timezone=True), nullable=False, unique=True, index=True),
|
|
sa.Column("total_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("success_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("error_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("input_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("output_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("cache_creation_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("cache_read_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("total_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("actual_total_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("input_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("output_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("cache_creation_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("cache_read_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("avg_response_time_ms", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("fallback_count", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("unique_models", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("unique_providers", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== stats_summary ====================
|
|
op.create_table(
|
|
"stats_summary",
|
|
sa.Column("id", sa.String(36), primary_key=True),
|
|
sa.Column("cutoff_date", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("all_time_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("all_time_success_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("all_time_error_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("all_time_input_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("all_time_output_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column(
|
|
"all_time_cache_creation_tokens", sa.BigInteger, server_default="0", nullable=False
|
|
),
|
|
sa.Column("all_time_cache_read_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("all_time_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("all_time_actual_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column("total_users", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("active_users", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("total_api_keys", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("active_api_keys", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
|
|
# ==================== stats_user_daily ====================
|
|
op.create_table(
|
|
"stats_user_daily",
|
|
sa.Column("id", sa.String(36), primary_key=True),
|
|
sa.Column(
|
|
"user_id", sa.String(36), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
|
),
|
|
sa.Column("date", sa.DateTime(timezone=True), nullable=False, index=True),
|
|
sa.Column("total_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("success_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("error_requests", sa.Integer, server_default="0", nullable=False),
|
|
sa.Column("input_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("output_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("cache_creation_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("cache_read_tokens", sa.BigInteger, server_default="0", nullable=False),
|
|
sa.Column("total_cost", sa.Float, server_default="0.0", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("user_id", "date", name="uq_stats_user_daily"),
|
|
)
|
|
op.create_index("idx_stats_user_daily_user_date", "stats_user_daily", ["user_id", "date"])
|
|
|
|
# ==================== api_key_provider_mappings ====================
|
|
op.create_table(
|
|
"api_key_provider_mappings",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"api_key_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("api_keys.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column(
|
|
"provider_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("providers.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column("priority_adjustment", sa.Integer, server_default="0"),
|
|
sa.Column("weight_multiplier", sa.Float, server_default="1.0"),
|
|
sa.Column("is_enabled", sa.Boolean, server_default="true", nullable=False),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.UniqueConstraint("api_key_id", "provider_id", name="uq_apikey_provider"),
|
|
)
|
|
op.create_index(
|
|
"idx_apikey_provider_enabled", "api_key_provider_mappings", ["api_key_id", "is_enabled"]
|
|
)
|
|
|
|
# ==================== provider_usage_tracking ====================
|
|
op.create_table(
|
|
"provider_usage_tracking",
|
|
sa.Column("id", sa.String(36), primary_key=True, index=True),
|
|
sa.Column(
|
|
"provider_id",
|
|
sa.String(36),
|
|
sa.ForeignKey("providers.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column("window_start", sa.DateTime(timezone=True), nullable=False, index=True),
|
|
sa.Column("window_end", sa.DateTime(timezone=True), nullable=False),
|
|
sa.Column("total_requests", sa.Integer, server_default="0"),
|
|
sa.Column("successful_requests", sa.Integer, server_default="0"),
|
|
sa.Column("failed_requests", sa.Integer, server_default="0"),
|
|
sa.Column("avg_response_time_ms", sa.Float, server_default="0.0"),
|
|
sa.Column("total_response_time_ms", sa.Float, server_default="0.0"),
|
|
sa.Column("total_cost_usd", sa.Float, server_default="0.0"),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
sa.Column(
|
|
"updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False
|
|
),
|
|
)
|
|
op.create_index(
|
|
"idx_provider_window", "provider_usage_tracking", ["provider_id", "window_start"]
|
|
)
|
|
op.create_index("idx_window_time", "provider_usage_tracking", ["window_start", "window_end"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
# Drop tables in reverse order (respecting foreign key dependencies)
|
|
op.drop_table("provider_usage_tracking")
|
|
op.drop_table("api_key_provider_mappings")
|
|
op.drop_table("stats_user_daily")
|
|
op.drop_table("stats_summary")
|
|
op.drop_table("stats_daily")
|
|
op.drop_table("request_candidates")
|
|
op.drop_table("audit_logs")
|
|
op.drop_table("announcement_reads")
|
|
op.drop_table("announcements")
|
|
op.drop_table("user_preferences")
|
|
op.drop_table("system_configs")
|
|
op.drop_table("user_quotas")
|
|
op.drop_table("usage")
|
|
op.drop_table("provider_api_keys")
|
|
op.drop_table("model_mappings")
|
|
op.drop_table("models")
|
|
op.drop_table("provider_endpoints")
|
|
op.drop_table("api_keys")
|
|
op.drop_table("global_models")
|
|
op.drop_table("providers")
|
|
op.drop_table("users")
|
|
|
|
# Drop ENUM types
|
|
op.execute("DROP TYPE IF EXISTS providerbillingtype")
|
|
op.execute("DROP TYPE IF EXISTS userrole")
|