"""init_db_table

Revision ID: fc9664b8e4a1
Revises: 
Create Date: 2025-12-04 10:46:18.612574

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = 'fc9664b8e4a1'
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('apps',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('workspace_id', sa.UUID(), nullable=False, comment='workspaces.id'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('description', sa.String(), nullable=True),
    sa.Column('icon', sa.String(), nullable=True),
    sa.Column('icon_type', sa.String(), nullable=True),
    sa.Column('type', sa.String(), nullable=False),
    sa.Column('visibility', sa.String(), nullable=True),
    sa.Column('status', sa.String(), nullable=True),
    sa.Column('tags', postgresql.JSON(astext_type=sa.Text()), nullable=True),
    sa.Column('current_release_id', sa.UUID(), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['current_release_id'], ['app_releases.id'], name='fk_apps_current_release_id', use_alter=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_apps_current_release_id'), 'apps', ['current_release_id'], unique=False)
    op.create_index(op.f('ix_apps_id'), 'apps', ['id'], unique=False)
    op.create_index(op.f('ix_apps_name'), 'apps', ['name'], unique=False)
    op.create_index(op.f('ix_apps_type'), 'apps', ['type'], unique=False)
    op.create_table('data_config',
    sa.Column('config_id', sa.Integer(), autoincrement=True, nullable=False, comment='配置ID'),
    sa.Column('config_name', sa.String(), nullable=False, comment='配置名称'),
    sa.Column('config_desc', sa.String(), nullable=True, comment='配置描述'),
    sa.Column('workspace_id', sa.UUID(), nullable=True, comment='工作空间ID'),
    sa.Column('group_id', sa.String(), nullable=True, comment='组ID'),
    sa.Column('user_id', sa.String(), nullable=True, comment='用户ID'),
    sa.Column('apply_id', sa.String(), nullable=True, comment='应用ID'),
    sa.Column('llm_id', sa.String(), nullable=True, comment='LLM模型配置ID'),
    sa.Column('embedding_id', sa.String(), nullable=True, comment='嵌入模型配置ID'),
    sa.Column('rerank_id', sa.String(), nullable=True, comment='重排序模型配置ID'),
    sa.Column('llm', sa.String(), nullable=True, comment='LLM模型配置ID'),
    sa.Column('enable_llm_dedup_blockwise', sa.Boolean(), nullable=True, comment='启用LLM决策去重'),
    sa.Column('enable_llm_disambiguation', sa.Boolean(), nullable=True, comment='启用LLM决策消歧'),
    sa.Column('deep_retrieval', sa.Boolean(), nullable=True, comment='深度检索开关'),
    sa.Column('t_type_strict', sa.Float(), nullable=True, comment='类型严格阈值'),
    sa.Column('t_name_strict', sa.Float(), nullable=True, comment='名称严格阈值'),
    sa.Column('t_overall', sa.Float(), nullable=True, comment='综合阈值'),
    sa.Column('state', sa.Boolean(), nullable=True, comment='配置使用状态'),
    sa.Column('chunker_strategy', sa.String(), nullable=True, comment='分块策略'),
    sa.Column('pruning_enabled', sa.Boolean(), nullable=True, comment='是否启动智能语义剪枝'),
    sa.Column('pruning_scene', sa.String(), nullable=True, comment='智能剪枝场景：education/online_service/outbound'),
    sa.Column('pruning_threshold', sa.Float(), nullable=True, comment='智能语义剪枝阈值（0-0.9）'),
    sa.Column('enable_self_reflexion', sa.Boolean(), nullable=True, comment='是否启用自我反思'),
    sa.Column('iteration_period', sa.String(), nullable=True, comment='反思迭代周期'),
    sa.Column('reflexion_range', sa.String(), nullable=True, comment='反思范围：部分/全部'),
    sa.Column('baseline', sa.String(), nullable=True, comment='基线：时间/事实/时间和事实'),
    sa.Column('statement_granularity', sa.Integer(), nullable=True, comment='陈述提取颗粒度，挡位 1/2/3'),
    sa.Column('include_dialogue_context', sa.Boolean(), nullable=True, comment='是否包含对话上下文'),
    sa.Column('max_context', sa.Integer(), nullable=True, comment='对话语境中包含字符的最大数量'),
    sa.Column('lambda_time', sa.Float(), nullable=True, comment='最低保持度，0-1 小数'),
    sa.Column('lambda_mem', sa.Float(), nullable=True, comment='遗忘率，0-1 小数'),
    sa.Column('offset', sa.Float(), nullable=True, comment='偏移度，0-1 小数'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
    sa.PrimaryKeyConstraint('config_id')
    )
    op.create_table('documents',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('kb_id', sa.UUID(), nullable=False, comment='knowledges.id'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
    sa.Column('file_id', sa.UUID(), nullable=False, comment='files.id'),
    sa.Column('file_name', sa.String(), nullable=False, comment='file name'),
    sa.Column('file_ext', sa.String(), nullable=False, comment='file extension'),
    sa.Column('file_size', sa.Integer(), nullable=True, comment='file size(byte)'),
    sa.Column('file_meta', sa.JSON(), nullable=False),
    sa.Column('parser_id', sa.String(), nullable=False, comment='default parser ID'),
    sa.Column('parser_config', sa.JSON(), nullable=False, comment='default parser config'),
    sa.Column('chunk_num', sa.Integer(), nullable=True, comment='chunk num'),
    sa.Column('progress', sa.Float(), nullable=True),
    sa.Column('progress_msg', sa.String(), nullable=True, comment='process message'),
    sa.Column('process_begin_at', sa.DateTime(), nullable=True),
    sa.Column('process_duration', sa.Float(), nullable=True),
    sa.Column('run', sa.Integer(), nullable=True, comment='start to run processing or cancel.(1: run it; 2: cancel)'),
    sa.Column('status', sa.Integer(), nullable=True, comment='is it validate(0: wasted, 1: validate)'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_documents_file_ext'), 'documents', ['file_ext'], unique=False)
    op.create_index(op.f('ix_documents_file_name'), 'documents', ['file_name'], unique=False)
    op.create_index(op.f('ix_documents_id'), 'documents', ['id'], unique=False)
    op.create_index(op.f('ix_documents_parser_id'), 'documents', ['parser_id'], unique=False)
    op.create_table('files',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('kb_id', sa.UUID(), nullable=False, comment='knowledges.id'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
    sa.Column('parent_id', sa.UUID(), nullable=True, comment='parent folder id'),
    sa.Column('file_name', sa.String(), nullable=False, comment='file name or folder name,default folder name is /'),
    sa.Column('file_ext', sa.String(), nullable=False, comment='file extension:folder|pdf'),
    sa.Column('file_size', sa.Integer(), nullable=True, comment='file size(byte)'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_files_file_ext'), 'files', ['file_ext'], unique=False)
    op.create_index(op.f('ix_files_file_name'), 'files', ['file_name'], unique=False)
    op.create_index(op.f('ix_files_id'), 'files', ['id'], unique=False)
    op.create_table('generic_files',
    sa.Column('id', sa.UUID(), nullable=False, comment='文件唯一标识'),
    sa.Column('tenant_id', sa.UUID(), nullable=False, comment='租户ID'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='创建者用户ID'),
    sa.Column('file_name', sa.String(), nullable=False, comment='原始文件名'),
    sa.Column('file_ext', sa.String(), nullable=False, comment='文件扩展名'),
    sa.Column('file_size', sa.Integer(), nullable=False, comment='文件大小（字节）'),
    sa.Column('mime_type', sa.String(), nullable=True, comment='MIME类型'),
    sa.Column('context', sa.String(), nullable=False, comment='上传上下文（avatar/app_icon/knowledge_base/temp/attachment）'),
    sa.Column('storage_path', sa.String(), nullable=False, comment='文件存储路径'),
    sa.Column('file_metadata', sa.JSON(), nullable=True, comment='业务元数据'),
    sa.Column('status', sa.String(), nullable=True, comment='文件状态（active/processing/deleted）'),
    sa.Column('is_public', sa.Boolean(), nullable=True, comment='是否公开访问'),
    sa.Column('access_url', sa.String(), nullable=True, comment='访问URL'),
    sa.Column('reference_count', sa.Integer(), nullable=True, comment='引用计数'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
    sa.Column('deleted_at', sa.DateTime(), nullable=True, comment='删除时间（软删除）'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('idx_created_at', 'generic_files', ['created_at'], unique=False)
    op.create_index('idx_tenant_context', 'generic_files', ['tenant_id', 'context'], unique=False)
    op.create_index('idx_tenant_status', 'generic_files', ['tenant_id', 'status'], unique=False)
    op.create_index(op.f('ix_generic_files_context'), 'generic_files', ['context'], unique=False)
    op.create_index(op.f('ix_generic_files_created_by'), 'generic_files', ['created_by'], unique=False)
    op.create_index(op.f('ix_generic_files_file_ext'), 'generic_files', ['file_ext'], unique=False)
    op.create_index(op.f('ix_generic_files_id'), 'generic_files', ['id'], unique=False)
    op.create_index(op.f('ix_generic_files_status'), 'generic_files', ['status'], unique=False)
    op.create_index(op.f('ix_generic_files_tenant_id'), 'generic_files', ['tenant_id'], unique=False)
    op.create_table('model_configs',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('name', sa.String(), nullable=False, comment='模型显示名称'),
    sa.Column('type', sa.String(), nullable=False, comment='模型类型'),
    sa.Column('description', sa.String(), nullable=True, comment='模型描述'),
    sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='模型配置参数'),
    sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否激活'),
    sa.Column('is_public', sa.Boolean(), nullable=False, comment='是否公开'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_model_configs_id'), 'model_configs', ['id'], unique=False)
    op.create_index(op.f('ix_model_configs_type'), 'model_configs', ['type'], unique=False)
    op.create_table('retrieval_info',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('host_id', sa.UUID(), nullable=False),
    sa.Column('retrieve_info', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_retrieval_info_id'), 'retrieval_info', ['id'], unique=False)
    op.create_table('tenants',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('description', sa.String(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_tenants_id'), 'tenants', ['id'], unique=False)
    op.create_index(op.f('ix_tenants_name'), 'tenants', ['name'], unique=False)
    op.create_table('agent_configs',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False),
    sa.Column('system_prompt', sa.Text(), nullable=True, comment='系统提示词'),
    sa.Column('default_model_config_id', sa.UUID(), nullable=True, comment='默认模型配置ID'),
    sa.Column('model_parameters', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='模型参数配置（temperature、max_tokens等）'),
    sa.Column('knowledge_retrieval', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='知识库检索配置'),
    sa.Column('memory', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='记忆配置'),
    sa.Column('variables', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='变量配置'),
    sa.Column('tools', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='工具配置'),
    sa.Column('agent_role', sa.String(length=20), nullable=True, comment='Agent 角色: master|sub|standalone'),
    sa.Column('agent_domain', sa.String(length=50), nullable=True, comment='专业领域: customer_service|technical_support|sales 等'),
    sa.Column('parent_agent_id', sa.UUID(), nullable=True, comment='父 Agent ID'),
    sa.Column('capabilities', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='Agent 能力列表'),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
    sa.ForeignKeyConstraint(['default_model_config_id'], ['model_configs.id'], ),
    sa.ForeignKeyConstraint(['parent_agent_id'], ['agent_configs.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_agent_configs_app_id'), 'agent_configs', ['app_id'], unique=True)
    op.create_index(op.f('ix_agent_configs_default_model_config_id'), 'agent_configs', ['default_model_config_id'], unique=False)
    op.create_index(op.f('ix_agent_configs_id'), 'agent_configs', ['id'], unique=False)
    op.create_table('app_releases',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False),
    sa.Column('version', sa.Integer(), nullable=False),
    sa.Column('version_name', sa.String(), nullable=False),
    sa.Column('release_notes', sa.String(), nullable=True, comment='版本说明'),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('description', sa.String(), nullable=True),
    sa.Column('icon', sa.String(), nullable=True),
    sa.Column('icon_type', sa.String(), nullable=True),
    sa.Column('type', sa.String(), nullable=False),
    sa.Column('visibility', sa.String(), nullable=True),
    sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True),
    sa.Column('default_model_config_id', sa.UUID(), nullable=True),
    sa.Column('published_by', sa.UUID(), nullable=False, comment='users.id'),
    sa.Column('published_at', sa.DateTime(), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
    sa.ForeignKeyConstraint(['default_model_config_id'], ['model_configs.id'], ),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('app_id', 'version', name='uq_app_release_app_version')
    )
    op.create_index(op.f('ix_app_releases_app_id'), 'app_releases', ['app_id'], unique=False)
    op.create_index(op.f('ix_app_releases_default_model_config_id'), 'app_releases', ['default_model_config_id'], unique=False)
    op.create_index(op.f('ix_app_releases_id'), 'app_releases', ['id'], unique=False)
    op.create_index(op.f('ix_app_releases_version'), 'app_releases', ['version'], unique=False)
    op.create_table('end_users',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False),
    sa.Column('other_id', sa.String(), nullable=True),
    sa.Column('other_name', sa.String(), nullable=False),
    sa.Column('other_address', sa.String(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_end_users_id'), 'end_users', ['id'], unique=False)
    op.create_table('model_api_keys',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('model_config_id', sa.UUID(), nullable=False, comment='模型配置ID'),
    sa.Column('model_name', sa.String(), nullable=False, comment='模型实际名称'),
    sa.Column('provider', sa.String(), nullable=False, comment='API Key提供商'),
    sa.Column('api_key', sa.String(), nullable=False, comment='API密钥'),
    sa.Column('api_base', sa.String(), nullable=True, comment='API基础URL'),
    sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='API Key特定配置'),
    sa.Column('usage_count', sa.String(), nullable=True, comment='使用次数'),
    sa.Column('last_used_at', sa.DateTime(), nullable=True, comment='最后使用时间'),
    sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否激活'),
    sa.Column('priority', sa.String(), nullable=True, comment='优先级'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
    sa.ForeignKeyConstraint(['model_config_id'], ['model_configs.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_model_api_keys_id'), 'model_api_keys', ['id'], unique=False)
    op.create_table('workspaces',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('icon', sa.String(), nullable=True),
    sa.Column('iconType', sa.String(), nullable=True),
    sa.Column('description', sa.String(), nullable=True),
    sa.Column('tenant_id', sa.UUID(), nullable=False),
    sa.Column('storage_type', sa.String(), nullable=True),
    sa.Column('llm', sa.String(), nullable=True),
    sa.Column('embedding', sa.String(), nullable=True),
    sa.Column('rerank', sa.String(), nullable=True),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=True),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_workspaces_id'), 'workspaces', ['id'], unique=False)
    op.create_index(op.f('ix_workspaces_name'), 'workspaces', ['name'], unique=False)
    op.create_table('agent_invocations',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('caller_agent_id', sa.UUID(), nullable=False, comment='调用者 Agent ID'),
    sa.Column('callee_agent_id', sa.UUID(), nullable=False, comment='被调用者 Agent ID'),
    sa.Column('conversation_id', sa.UUID(), nullable=True, comment='关联会话 ID（不使用外键约束，避免循环依赖）'),
    sa.Column('parent_invocation_id', sa.UUID(), nullable=True, comment='父调用 ID（用于追踪调用链）'),
    sa.Column('input_message', sa.Text(), nullable=False, comment='输入消息'),
    sa.Column('output_message', sa.Text(), nullable=True, comment='输出消息'),
    sa.Column('context', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='上下文信息'),
    sa.Column('status', sa.String(length=20), nullable=False, comment='状态: pending|running|completed|failed'),
    sa.Column('error_message', sa.Text(), nullable=True, comment='错误信息'),
    sa.Column('started_at', sa.DateTime(), nullable=False),
    sa.Column('completed_at', sa.DateTime(), nullable=True),
    sa.Column('elapsed_time', sa.Float(), nullable=True, comment='耗时（秒）'),
    sa.Column('token_usage', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='Token 使用情况'),
    sa.Column('meta_data', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='额外元数据'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['callee_agent_id'], ['agent_configs.id'], ),
    sa.ForeignKeyConstraint(['caller_agent_id'], ['agent_configs.id'], ),
    sa.ForeignKeyConstraint(['parent_invocation_id'], ['agent_invocations.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_agent_invocations_callee_agent_id'), 'agent_invocations', ['callee_agent_id'], unique=False)
    op.create_index(op.f('ix_agent_invocations_caller_agent_id'), 'agent_invocations', ['caller_agent_id'], unique=False)
    op.create_index(op.f('ix_agent_invocations_conversation_id'), 'agent_invocations', ['conversation_id'], unique=False)
    op.create_index(op.f('ix_agent_invocations_id'), 'agent_invocations', ['id'], unique=False)
    op.create_index(op.f('ix_agent_invocations_parent_invocation_id'), 'agent_invocations', ['parent_invocation_id'], unique=False)
    op.create_index(op.f('ix_agent_invocations_started_at'), 'agent_invocations', ['started_at'], unique=False)
    op.create_index(op.f('ix_agent_invocations_status'), 'agent_invocations', ['status'], unique=False)
    op.create_table('conversations',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False, comment='应用ID'),
    sa.Column('workspace_id', sa.UUID(), nullable=False, comment='工作空间ID'),
    sa.Column('user_id', sa.String(), nullable=True, comment='用户ID（外部系统）'),
    sa.Column('title', sa.String(length=255), nullable=True, comment='会话标题'),
    sa.Column('summary', sa.Text(), nullable=True, comment='会话摘要'),
    sa.Column('is_draft', sa.Boolean(), nullable=False, comment='是否为草稿会话'),
    sa.Column('config_snapshot', sa.JSON(), nullable=True, comment='配置快照（Agent配置、模型配置等）'),
    sa.Column('message_count', sa.Integer(), nullable=True, comment='消息数量'),
    sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否活跃'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
    sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_conversations_id'), 'conversations', ['id'], unique=False)
    op.create_table('memory_increments',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('workspace_id', sa.UUID(), nullable=False),
    sa.Column('total_num', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_memory_increments_id'), 'memory_increments', ['id'], unique=False)
    op.create_index(op.f('ix_memory_increments_workspace_id'), 'memory_increments', ['workspace_id'], unique=False)
    op.create_table('multi_agent_configs',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False, comment='关联应用'),
    sa.Column('master_agent_id', sa.UUID(), nullable=False, comment='主 Agent 发布版本 ID'),
    sa.Column('master_agent_name', sa.String(length=100), nullable=True, comment='主 Agent 名称'),
    sa.Column('orchestration_mode', sa.String(length=20), nullable=False, comment='协作模式: sequential|parallel|conditional|loop'),
    sa.Column('sub_agents', postgresql.JSON(astext_type=sa.Text()), nullable=False, comment="子 Agent 列表: [{'agent_id': 'uuid', 'name': '...', 'role': '...', 'priority': 1}]"),
    sa.Column('routing_rules', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment="路由规则: [{'condition': '...', 'target_agent_id': 'uuid', 'priority': 1}]"),
    sa.Column('execution_config', postgresql.JSON(astext_type=sa.Text()), nullable=False, comment="执行配置: {'max_iterations': 5, 'timeout': 60, 'parallel_limit': 3}"),
    sa.Column('aggregation_strategy', sa.String(length=20), nullable=False, comment='结果整合策略: merge|vote|priority|custom'),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
    sa.ForeignKeyConstraint(['master_agent_id'], ['app_releases.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_multi_agent_configs_app_id'), 'multi_agent_configs', ['app_id'], unique=True)
    op.create_index(op.f('ix_multi_agent_configs_id'), 'multi_agent_configs', ['id'], unique=False)
    op.create_table('users',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('username', sa.String(), nullable=False),
    sa.Column('email', sa.String(), nullable=False),
    sa.Column('hashed_password', sa.String(), nullable=False),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('is_superuser', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.Column('last_login_at', sa.DateTime(), nullable=True),
    sa.Column('current_workspace_id', sa.UUID(), nullable=True),
    sa.Column('tenant_id', sa.UUID(), nullable=False),
    sa.ForeignKeyConstraint(['current_workspace_id'], ['workspaces.id'], ),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
    op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
    op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
    op.create_table('api_keys',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('name', sa.String(length=255), nullable=False, comment='API Key 名称'),
    sa.Column('description', sa.Text(), nullable=True, comment='描述'),
    sa.Column('key_prefix', sa.String(length=20), nullable=False, comment='Key 前缀'),
    sa.Column('key_hash', sa.String(length=255), nullable=False, comment='Key 哈希值'),
    sa.Column('type', sa.String(length=50), nullable=False, comment='API Key 类型'),
    sa.Column('scopes', postgresql.JSONB(astext_type=sa.Text()), nullable=False, comment='权限范围列表'),
    sa.Column('workspace_id', sa.UUID(), nullable=False, comment='所属工作空间'),
    sa.Column('resource_id', sa.UUID(), nullable=True, comment='关联资源ID'),
    sa.Column('resource_type', sa.String(length=50), nullable=True, comment='资源类型'),
    sa.Column('rate_limit', sa.Integer(), nullable=True, comment='速率限制（请求/分钟）'),
    sa.Column('quota_limit', sa.Integer(), nullable=True, comment='配额限制（总请求数）'),
    sa.Column('quota_used', sa.Integer(), nullable=True, comment='已使用配额'),
    sa.Column('expires_at', sa.DateTime(), nullable=True, comment='过期时间'),
    sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否激活'),
    sa.Column('last_used_at', sa.DateTime(), nullable=True, comment='最后使用时间'),
    sa.Column('usage_count', sa.Integer(), nullable=True, comment='使用次数'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='创建者'),
    sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
    sa.Column('updated_at', sa.DateTime(), nullable=False, comment='更新时间'),
    sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_api_keys_id'), 'api_keys', ['id'], unique=False)
    op.create_index(op.f('ix_api_keys_key_hash'), 'api_keys', ['key_hash'], unique=True)
    op.create_index(op.f('ix_api_keys_resource_id'), 'api_keys', ['resource_id'], unique=False)
    op.create_index(op.f('ix_api_keys_type'), 'api_keys', ['type'], unique=False)
    op.create_index(op.f('ix_api_keys_workspace_id'), 'api_keys', ['workspace_id'], unique=False)
    op.create_table('app_shares',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('source_app_id', sa.UUID(), nullable=False, comment='源应用ID'),
    sa.Column('source_workspace_id', sa.UUID(), nullable=False, comment='源工作空间ID'),
    sa.Column('target_workspace_id', sa.UUID(), nullable=False, comment='目标工作空间ID'),
    sa.Column('shared_by', sa.UUID(), nullable=False, comment='分享者用户ID'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['shared_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['source_app_id'], ['apps.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['source_workspace_id'], ['workspaces.id'], ),
    sa.ForeignKeyConstraint(['target_workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_app_shares_id'), 'app_shares', ['id'], unique=False)
    op.create_table('knowledges',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('workspace_id', sa.UUID(), nullable=False, comment='workspaces.id'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
    sa.Column('parent_id', sa.UUID(), nullable=True, comment='parent folder id when type is Folder'),
    sa.Column('name', sa.String(), nullable=False, comment='KB name'),
    sa.Column('description', sa.String(), nullable=True, comment='KB description'),
    sa.Column('avatar', sa.String(), nullable=True, comment='avatar url'),
    sa.Column('type', sa.String(), nullable=True, comment='Type:General|Web|Third-party|Folder'),
    sa.Column('permission_id', sa.String(), nullable=True, comment='permission ID:Private|Share'),
    sa.Column('embedding_id', sa.UUID(), nullable=True, comment='default embedding model ID'),
    sa.Column('reranker_id', sa.UUID(), nullable=True, comment='default reranker model ID'),
    sa.Column('llm_id', sa.UUID(), nullable=True, comment='default llm model ID'),
    sa.Column('image2text_id', sa.UUID(), nullable=True, comment='default image2text model ID'),
    sa.Column('doc_num', sa.Integer(), nullable=True, comment='doc num'),
    sa.Column('chunk_num', sa.Integer(), nullable=True, comment='chunk num'),
    sa.Column('parser_id', sa.String(), nullable=True, comment='default parser ID'),
    sa.Column('parser_config', sa.JSON(), nullable=False, comment='default parser config'),
    sa.Column('status', sa.Integer(), nullable=True, comment='is it validate(0: disable, 1: enable, 2:Soft-delete)'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['embedding_id'], ['model_configs.id'], ondelete='SET NULL'),
    sa.ForeignKeyConstraint(['image2text_id'], ['model_configs.id'], ondelete='SET NULL'),
    sa.ForeignKeyConstraint(['llm_id'], ['model_configs.id'], ondelete='SET NULL'),
    sa.ForeignKeyConstraint(['reranker_id'], ['model_configs.id'], ondelete='SET NULL'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_knowledges_id'), 'knowledges', ['id'], unique=False)
    op.create_index(op.f('ix_knowledges_name'), 'knowledges', ['name'], unique=False)
    op.create_index(op.f('ix_knowledges_parser_id'), 'knowledges', ['parser_id'], unique=False)
    op.create_index(op.f('ix_knowledges_status'), 'knowledges', ['status'], unique=False)
    op.create_table('messages',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('conversation_id', sa.UUID(), nullable=False, comment='会话ID'),
    sa.Column('role', sa.String(length=20), nullable=False, comment='角色: user/assistant/system'),
    sa.Column('content', sa.Text(), nullable=False, comment='消息内容'),
    sa.Column('meta_data', sa.JSON(), nullable=True, comment='消息元数据（如模型、token使用等）'),
    sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
    sa.ForeignKeyConstraint(['conversation_id'], ['conversations.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_messages_id'), 'messages', ['id'], unique=False)
    op.create_table('release_shares',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('release_id', sa.UUID(), nullable=False),
    sa.Column('app_id', sa.UUID(), nullable=False),
    sa.Column('is_enabled', sa.Boolean(), nullable=False, comment='是否启用公开分享'),
    sa.Column('share_token', sa.String(), nullable=False, comment='公开访问的唯一标识'),
    sa.Column('require_password', sa.Boolean(), nullable=False, comment='是否需要密码访问'),
    sa.Column('password_hash', sa.String(), nullable=True, comment='访问密码哈希'),
    sa.Column('allow_embed', sa.Boolean(), nullable=False, comment='是否允许嵌入'),
    sa.Column('embed_domains', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='允许嵌入的域名白名单'),
    sa.Column('view_count', sa.Integer(), nullable=False, comment='访问次数'),
    sa.Column('last_accessed_at', sa.DateTime(), nullable=True, comment='最后访问时间'),
    sa.Column('created_by', sa.UUID(), nullable=False, comment='创建者'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['release_id'], ['app_releases.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('release_id', name='uq_release_share_release_id')
    )
    op.create_index(op.f('ix_release_shares_app_id'), 'release_shares', ['app_id'], unique=False)
    op.create_index(op.f('ix_release_shares_id'), 'release_shares', ['id'], unique=False)
    op.create_index(op.f('ix_release_shares_release_id'), 'release_shares', ['release_id'], unique=True)
    op.create_index(op.f('ix_release_shares_share_token'), 'release_shares', ['share_token'], unique=True)
    op.create_table('workspace_invites',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('workspace_id', sa.UUID(), nullable=False),
    sa.Column('email', sa.String(), nullable=False),
    sa.Column('role', sa.String(), nullable=False),
    sa.Column('token_hash', sa.String(), nullable=False),
    sa.Column('status', sa.String(), nullable=False),
    sa.Column('expires_at', sa.DateTime(), nullable=False),
    sa.Column('accepted_at', sa.DateTime(), nullable=True),
    sa.Column('created_by_user_id', sa.UUID(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['created_by_user_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_workspace_invites_email'), 'workspace_invites', ['email'], unique=False)
    op.create_index(op.f('ix_workspace_invites_id'), 'workspace_invites', ['id'], unique=False)
    op.create_index(op.f('ix_workspace_invites_token_hash'), 'workspace_invites', ['token_hash'], unique=True)
    op.create_table('workspace_members',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('user_id', sa.UUID(), nullable=False),
    sa.Column('workspace_id', sa.UUID(), nullable=False),
    sa.Column('role', sa.String(), nullable=False),
    sa.Column('is_active', sa.Boolean(), nullable=True),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_workspace_members_id'), 'workspace_members', ['id'], unique=False)
    op.create_table('api_key_logs',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('api_key_id', sa.UUID(), nullable=False, comment='API Key ID'),
    sa.Column('endpoint', sa.String(length=255), nullable=False, comment='请求端点'),
    sa.Column('method', sa.String(length=10), nullable=False, comment='HTTP 方法'),
    sa.Column('ip_address', sa.String(length=50), nullable=True, comment='IP 地址'),
    sa.Column('user_agent', sa.Text(), nullable=True, comment='User Agent'),
    sa.Column('status_code', sa.Integer(), nullable=True, comment='响应状态码'),
    sa.Column('response_time', sa.Integer(), nullable=True, comment='响应时间（毫秒）'),
    sa.Column('tokens_used', sa.Integer(), nullable=True, comment='使用的 Token 数'),
    sa.Column('created_at', sa.DateTime(), nullable=False, comment='创建时间'),
    sa.ForeignKeyConstraint(['api_key_id'], ['api_keys.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_api_key_logs_api_key_id'), 'api_key_logs', ['api_key_id'], unique=False)
    op.create_index(op.f('ix_api_key_logs_created_at'), 'api_key_logs', ['created_at'], unique=False)
    op.create_index(op.f('ix_api_key_logs_id'), 'api_key_logs', ['id'], unique=False)
    op.create_table('knowledge_shares',
    sa.Column('id', sa.UUID(), nullable=False),
    sa.Column('source_kb_id', sa.UUID(), nullable=False, comment='source knowledges.id'),
    sa.Column('source_workspace_id', sa.UUID(), nullable=False, comment='source workspaces.id'),
    sa.Column('target_kb_id', sa.UUID(), nullable=False, comment='target knowledges.id'),
    sa.Column('target_workspace_id', sa.UUID(), nullable=False, comment='target workspaces.id'),
    sa.Column('shared_by', sa.UUID(), nullable=False, comment='shared users.id'),
    sa.Column('created_at', sa.DateTime(), nullable=True),
    sa.Column('updated_at', sa.DateTime(), nullable=True),
    sa.ForeignKeyConstraint(['shared_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['target_kb_id'], ['knowledges.id'], ),
    sa.ForeignKeyConstraint(['target_workspace_id'], ['workspaces.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_knowledge_shares_id'), 'knowledge_shares', ['id'], unique=False)
    # ### end Alembic commands ###

    # 创建或更新统计信息的函数
    op.execute("""
        CREATE OR REPLACE FUNCTION update_knowledge_stats()
        RETURNS TRIGGER AS $$
        BEGIN
            -- 处理 documents 表的插入、更新或删除
            IF TG_TABLE_NAME = 'documents' THEN
                -- 更新 knowledges 表的 doc_num
                UPDATE knowledges 
                SET doc_num = (
                    SELECT COUNT(*) 
                    FROM documents 
                    WHERE kb_id = knowledges.id AND status = 1
                )
                WHERE id = NEW.kb_id OR id = OLD.kb_id;

                -- 更新 knowledges 表的 chunk_num
                UPDATE knowledges 
                SET chunk_num = (
                    SELECT COALESCE(SUM(chunk_num), 0) 
                    FROM documents 
                    WHERE kb_id = knowledges.id AND status = 1
                )
                WHERE id = NEW.kb_id OR id = OLD.kb_id;

                -- 通过 knowledge_shares 表同步统计信息
                -- 1. 使用 source_kb_id 的 doc_num 更新 target_kb_id 的 doc_num
                UPDATE knowledges AS target
                SET doc_num = source.doc_num
                FROM knowledge_shares ks
                JOIN knowledges AS source ON source.id = ks.source_kb_id
                WHERE ks.target_kb_id = target.id 
                AND (source.id = NEW.kb_id OR source.id = OLD.kb_id);

                -- 2. 使用 source_kb_id 的 chunk_num 更新 target_kb_id 的 chunk_num
                UPDATE knowledges AS target
                SET chunk_num = source.chunk_num
                FROM knowledge_shares ks
                JOIN knowledges AS source ON source.id = ks.source_kb_id
                WHERE ks.target_kb_id = target.id 
                AND (source.id = NEW.kb_id OR source.id = OLD.kb_id);

            END IF;

            RETURN NULL;
        END;
        $$ LANGUAGE plpgsql;
        """)

    # 创建 documents 表上的触发器
    op.execute("""
        CREATE TRIGGER tr_documents_update_stats
        AFTER INSERT OR UPDATE OR DELETE ON documents
        FOR EACH ROW
        EXECUTE FUNCTION update_knowledge_stats();
        """)


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index(op.f('ix_knowledge_shares_id'), table_name='knowledge_shares')
    op.drop_table('knowledge_shares')
    op.drop_index(op.f('ix_api_key_logs_id'), table_name='api_key_logs')
    op.drop_index(op.f('ix_api_key_logs_created_at'), table_name='api_key_logs')
    op.drop_index(op.f('ix_api_key_logs_api_key_id'), table_name='api_key_logs')
    op.drop_table('api_key_logs')
    op.drop_index(op.f('ix_workspace_members_id'), table_name='workspace_members')
    op.drop_table('workspace_members')
    op.drop_index(op.f('ix_workspace_invites_token_hash'), table_name='workspace_invites')
    op.drop_index(op.f('ix_workspace_invites_id'), table_name='workspace_invites')
    op.drop_index(op.f('ix_workspace_invites_email'), table_name='workspace_invites')
    op.drop_table('workspace_invites')
    op.drop_index(op.f('ix_release_shares_share_token'), table_name='release_shares')
    op.drop_index(op.f('ix_release_shares_release_id'), table_name='release_shares')
    op.drop_index(op.f('ix_release_shares_id'), table_name='release_shares')
    op.drop_index(op.f('ix_release_shares_app_id'), table_name='release_shares')
    op.drop_table('release_shares')
    op.drop_index(op.f('ix_messages_id'), table_name='messages')
    op.drop_table('messages')
    op.drop_index(op.f('ix_knowledges_status'), table_name='knowledges')
    op.drop_index(op.f('ix_knowledges_parser_id'), table_name='knowledges')
    op.drop_index(op.f('ix_knowledges_name'), table_name='knowledges')
    op.drop_index(op.f('ix_knowledges_id'), table_name='knowledges')
    op.drop_table('knowledges')
    op.drop_index(op.f('ix_app_shares_id'), table_name='app_shares')
    op.drop_table('app_shares')
    op.drop_index(op.f('ix_api_keys_workspace_id'), table_name='api_keys')
    op.drop_index(op.f('ix_api_keys_type'), table_name='api_keys')
    op.drop_index(op.f('ix_api_keys_resource_id'), table_name='api_keys')
    op.drop_index(op.f('ix_api_keys_key_hash'), table_name='api_keys')
    op.drop_index(op.f('ix_api_keys_id'), table_name='api_keys')
    op.drop_table('api_keys')
    op.drop_index(op.f('ix_users_username'), table_name='users')
    op.drop_index(op.f('ix_users_id'), table_name='users')
    op.drop_index(op.f('ix_users_email'), table_name='users')
    op.drop_table('users')
    op.drop_index(op.f('ix_multi_agent_configs_id'), table_name='multi_agent_configs')
    op.drop_index(op.f('ix_multi_agent_configs_app_id'), table_name='multi_agent_configs')
    op.drop_table('multi_agent_configs')
    op.drop_index(op.f('ix_memory_increments_workspace_id'), table_name='memory_increments')
    op.drop_index(op.f('ix_memory_increments_id'), table_name='memory_increments')
    op.drop_table('memory_increments')
    op.drop_index(op.f('ix_conversations_id'), table_name='conversations')
    op.drop_table('conversations')
    op.drop_index(op.f('ix_agent_invocations_status'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_started_at'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_parent_invocation_id'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_id'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_conversation_id'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_caller_agent_id'), table_name='agent_invocations')
    op.drop_index(op.f('ix_agent_invocations_callee_agent_id'), table_name='agent_invocations')
    op.drop_table('agent_invocations')
    op.drop_index(op.f('ix_workspaces_name'), table_name='workspaces')
    op.drop_index(op.f('ix_workspaces_id'), table_name='workspaces')
    op.drop_table('workspaces')
    op.drop_index(op.f('ix_model_api_keys_id'), table_name='model_api_keys')
    op.drop_table('model_api_keys')
    op.drop_index(op.f('ix_end_users_id'), table_name='end_users')
    op.drop_table('end_users')
    op.drop_index(op.f('ix_app_releases_version'), table_name='app_releases')
    op.drop_index(op.f('ix_app_releases_id'), table_name='app_releases')
    op.drop_index(op.f('ix_app_releases_default_model_config_id'), table_name='app_releases')
    op.drop_index(op.f('ix_app_releases_app_id'), table_name='app_releases')
    op.drop_table('app_releases')
    op.drop_index(op.f('ix_agent_configs_id'), table_name='agent_configs')
    op.drop_index(op.f('ix_agent_configs_default_model_config_id'), table_name='agent_configs')
    op.drop_index(op.f('ix_agent_configs_app_id'), table_name='agent_configs')
    op.drop_table('agent_configs')
    op.drop_index(op.f('ix_tenants_name'), table_name='tenants')
    op.drop_index(op.f('ix_tenants_id'), table_name='tenants')
    op.drop_table('tenants')
    op.drop_index(op.f('ix_retrieval_info_id'), table_name='retrieval_info')
    op.drop_table('retrieval_info')
    op.drop_index(op.f('ix_model_configs_type'), table_name='model_configs')
    op.drop_index(op.f('ix_model_configs_id'), table_name='model_configs')
    op.drop_table('model_configs')
    op.drop_index(op.f('ix_generic_files_tenant_id'), table_name='generic_files')
    op.drop_index(op.f('ix_generic_files_status'), table_name='generic_files')
    op.drop_index(op.f('ix_generic_files_id'), table_name='generic_files')
    op.drop_index(op.f('ix_generic_files_file_ext'), table_name='generic_files')
    op.drop_index(op.f('ix_generic_files_created_by'), table_name='generic_files')
    op.drop_index(op.f('ix_generic_files_context'), table_name='generic_files')
    op.drop_index('idx_tenant_status', table_name='generic_files')
    op.drop_index('idx_tenant_context', table_name='generic_files')
    op.drop_index('idx_created_at', table_name='generic_files')
    op.drop_table('generic_files')
    op.drop_index(op.f('ix_files_id'), table_name='files')
    op.drop_index(op.f('ix_files_file_name'), table_name='files')
    op.drop_index(op.f('ix_files_file_ext'), table_name='files')
    op.drop_table('files')
    op.drop_index(op.f('ix_documents_parser_id'), table_name='documents')
    op.drop_index(op.f('ix_documents_id'), table_name='documents')
    op.drop_index(op.f('ix_documents_file_name'), table_name='documents')
    op.drop_index(op.f('ix_documents_file_ext'), table_name='documents')
    op.drop_table('documents')
    op.drop_table('data_config')
    op.drop_index(op.f('ix_apps_type'), table_name='apps')
    op.drop_index(op.f('ix_apps_name'), table_name='apps')
    op.drop_index(op.f('ix_apps_id'), table_name='apps')
    op.drop_index(op.f('ix_apps_current_release_id'), table_name='apps')
    op.drop_table('apps')
    # ### end Alembic commands ###

    # 删除触发器
    op.execute("DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;")

    # 删除函数
    op.execute("DROP FUNCTION IF EXISTS update_knowledge_stats();")
