Files
MemoryBear/api/migrations/versions/b012ab0089b9_init_db_table.py
lanceyq 570392aa6f Docs/installation tutorial (#7)
* [add]修改迁移文件新建空白表结构

* Add installation guide and environment setup

Added installation instructions and environment requirements for MemoryBear.

* [delete]删除api,web的readme.md。只保留唯一readme.md

* Fix database connection example in README

Update database connection configuration example in README.
2025-12-05 21:38:31 +08:00

441 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""init_db_table
Revision ID: b012ab0089b9
Revises:
Create Date: 2025-11-12 14:07:56.132912
"""
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 = 'b012ab0089b9'
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('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('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),
sa.Column('default_model_config_id', sa.UUID(), nullable=True),
sa.Column('tools_config', postgresql.JSON(astext_type=sa.Text()), nullable=True),
sa.Column('memory_strategy', sa.String(), nullable=True),
sa.Column('conversation_prefs', postgresql.JSON(astext_type=sa.Text()), 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')
)
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('name', sa.String(), nullable=False),
sa.Column('description', sa.String(), nullable=True),
sa.Column('avatar', 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('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('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('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('tenant_id', sa.UUID(), nullable=False),
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('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('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('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: wasted, 1: validate)'),
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('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('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("""
DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;
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_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_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_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_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_model_api_keys_id'), table_name='model_api_keys')
op.drop_table('model_api_keys')
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_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_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();")